diff --git a/src/actions/RR/hisFacilityByInfrastructureCountyActions.js b/src/actions/RR/hisFacilityByInfrastructureCountyActions.js new file mode 100644 index 00000000..6227dd56 --- /dev/null +++ b/src/actions/RR/hisFacilityByInfrastructureCountyActions.js @@ -0,0 +1,34 @@ +import moment from 'moment'; +import { CACHING, PAGES } from '../../constants'; +import * as actionTypes from '../types'; +import { getAll } from '../../views/Shared/Api'; + +export const loadHisFacilityByInfrastructureCountyActions = () => async (dispatch, getState) => { + const diffInMinutes = moment().diff( + moment(getState().hisFacilityByInfrastructure.lastFetch), + 'minutes' + ); + if (getState().ui.currentPage !== PAGES.rr) { + return; + } + else if ((diffInMinutes < CACHING.LONG) && getState().filters.filtered === false) { + return; + } else { + await dispatch(fetchHisFacilityByInfrastructure()); + } +} + +export const fetchHisFacilityByInfrastructure = () => async (dispatch, getState) => { + dispatch({ type: actionTypes.HIS_FACILITY_BY_INFRASTRUCTURE_COUNTY_REQUEST }); + const params = { + county: getState().filters.counties, + subCounty: getState().filters.subCounties, + facility: getState().filters.facilities, + partner: getState().filters.partners, + agency: getState().filters.agencies, + year: getState().filters.fromDate ? moment(getState().filters.fromDate, "MMM YYYY").format("YYYY") : '', + month: getState().filters.fromDate ? moment(getState().filters.fromDate, "MMM YYYY").format("MM") : '', + }; + const response = await getAll('common/facilityByInfrastructureCounty', params); + dispatch({ type: actionTypes.HIS_FACILITY_BY_INFRASTRUCTURE_COUNTY_FETCH, payload: { filtered: getState().filters.filtered, list: response }}); +}; diff --git a/src/actions/RR/hisFacilityStatusByCountyActions.js b/src/actions/RR/hisFacilityStatusByCountyActions.js new file mode 100644 index 00000000..f700d5e2 --- /dev/null +++ b/src/actions/RR/hisFacilityStatusByCountyActions.js @@ -0,0 +1,32 @@ +import moment from 'moment'; +import { CACHING, PAGES } from '../../constants'; +import * as actionTypes from '../types'; +import { getAll } from '../../views/Shared/Api'; + +export const loadHisFacilityStatusByCountyAction = () => async (dispatch, getState) => { + const diffInMinutes = moment().diff( + moment(getState().hisFacilityStatusByPartner.lastFetch), + 'minutes' + ); + if (getState().ui.currentPage !== PAGES.rr) { + return; + } + else if ((diffInMinutes < CACHING.LONG) && getState().filters.filtered === false) { + return; + } else { + await dispatch(fetchHisFacilityStatusByPartner()); + } +} + +export const fetchHisFacilityStatusByPartner = () => async (dispatch, getState) => { + dispatch({ type: actionTypes.HIS_FACILITY_STATUS_BY_COUNTY_REQUEST }); + const params = { + county: getState().filters.counties, + subCounty: getState().filters.subCounties, + facility: getState().filters.facilities, + partner: getState().filters.partners, + agency: getState().filters.agencies, + }; + const response = await getAll('common/facilityStatusByCounty', params); + dispatch({ type: actionTypes.HIS_FACILITY_STATUS_BY_COUNTY_FETCH, payload: { filtered: getState().filters.filtered, list: response }}); +}; diff --git a/src/actions/types.js b/src/actions/types.js index 39a4f59a..aedc6d40 100644 --- a/src/actions/types.js +++ b/src/actions/types.js @@ -519,6 +519,10 @@ export const HIS_FACILITY_STATUS_BY_PARTNER_REQUEST = 'HIS_FACILITY_STATUS_BY_PA export const HIS_FACILITY_STATUS_BY_PARTNER_FETCH = 'HIS_FACILITY_STATUS_BY_PARTNER_FETCH' export const HIS_FACILITY_STATUS_BY_PARTNER_FAILED = 'HIS_FACILITY_STATUS_BY_PARTNER_FAILED' +export const HIS_FACILITY_STATUS_BY_COUNTY_REQUEST = 'HIS_FACILITY_STATUS_BY_COUNTY_REQUEST' +export const HIS_FACILITY_STATUS_BY_COUNTY_FETCH = 'HIS_FACILITY_STATUS_BY_COUNTY_FETCH' +export const HIS_FACILITY_STATUS_BY_COUNTY_FAILED = 'HIS_FACILITY_STATUS_BY_COUNTY_FAILED' + export const HIS_FACILITY_LEVEL_BY_PARTNER_REQUEST = 'HIS_FACILITY_LEVEL_BY_PARTNER_REQUEST' export const HIS_FACILITY_LEVEL_BY_PARTNER_FETCH = 'HIS_FACILITY_LEVEL_BY_PARTNER_FETCH' export const HIS_FACILITY_LEVEL_BY_PARTNER_FAILED = 'HIS_FACILITY_LEVEL_BY_PARTNER_FAILED' @@ -531,6 +535,10 @@ export const HIS_FACILITY_BY_INFRASTRUCTURE_REQUEST = 'HIS_FACILITY_BY_INFRASTRU export const HIS_FACILITY_BY_INFRASTRUCTURE_FETCH = 'HIS_FACILITY_BY_INFRASTRUCTURE_FETCH' export const HIS_FACILITY_BY_INFRASTRUCTURE_FAILED = 'HIS_FACILITY_BY_INFRASTRUCTURE_FAILED' +export const HIS_FACILITY_BY_INFRASTRUCTURE_COUNTY_REQUEST = 'HIS_FACILITY_BY_INFRASTRUCTURE_COUNTY_REQUEST' +export const HIS_FACILITY_BY_INFRASTRUCTURE_COUNTY_FETCH = 'HIS_FACILITY_BY_INFRASTRUCTURE_COUNTY_FETCH' +export const HIS_FACILITY_BY_INFRASTRUCTURE_COUNTY_FAILED = 'HIS_FACILITY_BY_INFRASTRUCTURE_COUNTY_FAILED' + export const HIS_FACILITY_LINELIST_REQUEST = 'HIS_FACILITY_LINELIST_REQUEST' export const HIS_FACILITY_LINELIST_FETCH = 'HIS_FACILITY_LINELIST_FETCH' export const HIS_FACILITY_LINELIST_FAILED = 'HIS_FACILITY_LINELIST_FAILED' diff --git a/src/reducers/RR/hisFacilityByInfrastructureCounty.js b/src/reducers/RR/hisFacilityByInfrastructureCounty.js new file mode 100644 index 00000000..66409901 --- /dev/null +++ b/src/reducers/RR/hisFacilityByInfrastructureCounty.js @@ -0,0 +1,31 @@ +import * as actionTypes from "../../actions/types"; + +const initialState = { + lastFetch: null, + loading: false, + listUnfiltered: [], + listFiltered: [], +}; + +export default (state = initialState, action) => { + let newState = { ...state }; + switch (action.type) { + case actionTypes.HIS_FACILITY_BY_INFRASTRUCTURE_COUNTY_REQUEST: + newState.loading = true; + return newState; + case actionTypes.HIS_FACILITY_BY_INFRASTRUCTURE_COUNTY_FETCH: + if (action.payload.filtered === true) { + newState.listFiltered = action.payload.list; + } else { + newState.listUnfiltered = action.payload.list; + newState.lastFetch = Date.now(); + } + newState.loading = false; + return newState; + case actionTypes.HIS_FACILITY_BY_INFRASTRUCTURE_COUNTY_FAILED: + newState.loading = false; + return newState; + default: + return state; + } +} diff --git a/src/reducers/RR/hisFacilityStatusByCounty.js b/src/reducers/RR/hisFacilityStatusByCounty.js new file mode 100644 index 00000000..654fc748 --- /dev/null +++ b/src/reducers/RR/hisFacilityStatusByCounty.js @@ -0,0 +1,31 @@ +import * as actionTypes from "../../actions/types"; + +const initialState = { + lastFetch: null, + loading: false, + listUnfiltered: [], + listFiltered: [], +}; + +export default (state = initialState, action) => { + let newState = { ...state }; + switch (action.type) { + case actionTypes.HIS_FACILITY_STATUS_BY_COUNTY_REQUEST: + newState.loading = true; + return newState; + case actionTypes.HIS_FACILITY_STATUS_BY_COUNTY_FETCH: + if (action.payload.filtered === true) { + newState.listFiltered = action.payload.list; + } else { + newState.listUnfiltered = action.payload.list; + newState.lastFetch = Date.now(); + } + newState.loading = false; + return newState; + case actionTypes.HIS_FACILITY_STATUS_BY_COUNTY_FAILED: + newState.loading = false; + return newState; + default: + return state; + } +} diff --git a/src/reducers/index.js b/src/reducers/index.js index eb5b0250..dbfecc34 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -331,6 +331,8 @@ import hisFacilityStatus from './RR/hisFacilityStatus'; import hisFacilityStatusByPartner from './RR/hisFacilityStatusByPartner'; import hisFacilityLevelByPartner from './RR/hisFacilityLevelByPartner'; import hisFacilityTxcurr from './RR/hisFacilityTxcurr'; +import hisFacilityByInfrastructureCounty from './RR/hisFacilityByInfrastructureCounty'; +import hisFacilityStatusByCounty from './RR/hisFacilityStatusByCounty'; import missedInfantProphylaxis from './PMTCTRRI/MissedInfantProphylaxis/missedInfantProphylaxis'; @@ -662,4 +664,6 @@ export default combineReducers({ hisFacilityStatus, hisFacilityStatusByPartner, hisFacilityTxcurr, + hisFacilityByInfrastructureCounty, + hisFacilityStatusByCounty }); diff --git a/src/selectors/RR/HisDeploymentsSelector.js b/src/selectors/RR/HisDeploymentsSelector.js index f4e19726..2eda8317 100644 --- a/src/selectors/RR/HisDeploymentsSelector.js +++ b/src/selectors/RR/HisDeploymentsSelector.js @@ -7,6 +7,9 @@ const filtered = state => state.filters.filtered; const listStatusByPartnerUnfiltered = state => state.hisFacilityStatusByPartner.listUnfiltered; const listStatusByPartnerFiltered = state => state.hisFacilityStatusByPartner.listFiltered; +const listStatusByCountyUnfiltered = state => state.hisFacilityStatusByCounty.listUnfiltered; +const listStatusByCountyFiltered = state => state.hisFacilityStatusByCounty.listFiltered; + const listLevelByPartnerUnfiltered = state => state.hisFacilityLevelByPartner.listUnfiltered; const listLevelByPartnerFiltered = state => state.hisFacilityLevelByPartner.listFiltered; @@ -16,6 +19,9 @@ const listLevelByCountyFiltered = state => state.hisFacilityLevelByCounty.listFi const listByInfrastructureUnfiltered = state => state.hisFacilityByInfrastructure.listUnfiltered; const listByInfrastructureFiltered = state => state.hisFacilityByInfrastructure.listFiltered; +const listByInfrastructureCountyUnfiltered = state => state.hisFacilityByInfrastructureCounty.listUnfiltered; +const listByInfrastructureCountyFiltered = state => state.hisFacilityByInfrastructureCounty.listFiltered; + const listLinelistUnfiltered = state => state.hisFacilityLinelist.listUnfiltered; const listLinelistFiltered = state => state.hisFacilityLinelist.listFiltered; const listLinelistLoading = state => state.hisFacilityLinelist.loading; @@ -73,6 +79,44 @@ export const getFacilityStatusByPartner = createSelector( } ); +export const getFacilityStatusByCounty = createSelector( + [listStatusByCountyUnfiltered, listStatusByCountyFiltered, filtered], + (listUnfiltered, listFiltered, filtered) => { + const list = filtered ? listFiltered : listUnfiltered; + let data = list.reduce((acc, curr) => { + const { facilities, EMR_Status, County } = curr + if(!acc[County]) { + acc[County] = { + "County": County, + "Active": 0, + "Discontinued": 0, + "Inactive/Stalled": 0, + "Total": 0 + } + } + + const statusKey = (EMR_Status === "Stalled/Inactive" || EMR_Status === "Inactive") ? "Inactive/Stalled" : EMR_Status; + + acc[County][statusKey] += parseInt(facilities); + acc[County].Total += parseInt(facilities); + return acc + }, {}); + let formattedResult = Object.values(data); + formattedResult.sort((a, b) => b.Active - a.Active); + formattedResult = formattedResult.map(partner => { + partner.activePerc = partner.Total ? (partner.Active/partner.Total)*100 : 0 + return partner + }).sort((a, b) => b.activePerc - a.activePerc) + + const counties = formattedResult.map(i => i.County?.toUpperCase()) + const actives = formattedResult.map(i => i.Active) + const discontinueds = formattedResult.map(i => i.Discontinued) + const inactives = formattedResult.map(i => i["Inactive/Stalled"]) + + return { counties, actives, discontinueds, inactives } + } +); + export const getFacilityByInfrastructure = createSelector( [listByInfrastructureUnfiltered, listByInfrastructureFiltered, filtered], (listUnfiltered, listFiltered, filtered) => { @@ -107,6 +151,40 @@ export const getFacilityByInfrastructure = createSelector( } ); +export const getFacilityByInfrastructureCounty = createSelector( + [listByInfrastructureCountyUnfiltered, listByInfrastructureCountyFiltered, filtered], + (listUnfiltered, listFiltered, filtered) => { + const list = filtered ? listFiltered : listUnfiltered; + let data = list.reduce((acc, curr) => { + const { facilities, InfrastructureType, County } = curr + if(!acc[County]) { + acc[County] = { + "County": County, + "On Premises": 0, + "On Cloud": 0, + "Total": 0 + } + } + + acc[County][InfrastructureType] += parseInt(facilities); + acc[County].Total += parseInt(facilities); + return acc + }, {}); + let formattedResult = Object.values(data); + formattedResult.sort((a, b) => b["On Premises"] - a["On Premises"]); + formattedResult = formattedResult.map(partner => { + partner.onPremPerc = partner.Total ? (partner["On Premises"]/partner.Total)*100 : 0 + return partner + }).sort((a, b) => b.onPremPerc - a.onPremPerc) + + const counties = formattedResult.map(i => i.County?.toUpperCase()) + const onPremises = formattedResult.map(i => i["On Premises"]) + const onCloud = formattedResult.map(i => i["On Cloud"]) + + return { counties, onPremises, onCloud } + } +); + export const getFacilityLevelByPartner = createSelector( [listLevelByPartnerUnfiltered, listLevelByPartnerFiltered, filtered], (listUnfiltered, listFiltered, filtered) => { @@ -205,7 +283,7 @@ export const getFacilityTxCurr = createSelector( (listUnfiltered, listFiltered, filtered, loading) => { const list = filtered ? listFiltered : listUnfiltered; - let data = list.map((d) => d.KEPH_Level === null ? {...d, 'KEPH_Level': 'Missing' } : { ...d }) + let data = list.map((d) => d.KEPH_Level === null ? {...d, 'KEPH_Level': 'Missing' } : d) return { 'list': data, loading }; } diff --git a/src/views/RR/HisDeployments.js b/src/views/RR/HisDeployments.js index fa068ff5..69b32840 100644 --- a/src/views/RR/HisDeployments.js +++ b/src/views/RR/HisDeployments.js @@ -19,6 +19,12 @@ import { loadHisFacilityLevelByCountyAction } from '../../actions/RR/hisFacility import { loadHisFacilityLevelByPartnerAction } from '../../actions/RR/hisFacilityLevelByPartnerActions'; import { loadHisFacilityStatusAction } from '../../actions/RR/hisFacilityStatusActions'; import { loadHisFacilityTxcurrAction } from '../../actions/RR/hisFacilityTxcurrActions'; +import { loadHisFacilityStatusByCountyAction } from '../../actions/RR/hisFacilityStatusByCountyActions'; +import { + loadHisFacilityByInfrastructureCountyActions +} from '../../actions/RR/hisFacilityByInfrastructureCountyActions'; +import HisDeploymentsEMRStatusByCounty from './HisDeploymentsEMRStatusByCounty'; +import HisDeploymentsFacilityByInfrastructureCounty from './HisDeploymentsFacilityByInfrastructureCounty'; const HisDeployments = () =>{ const dispatch = useDispatch(); @@ -31,6 +37,8 @@ const HisDeployments = () =>{ dispatch(loadHisFacilityByInfrastructureActions()); dispatch(loadHisFacilityLinelistAction()); dispatch(loadHisFacilityTxcurrAction()); + dispatch(loadHisFacilityStatusByCountyAction()); + dispatch(loadHisFacilityByInfrastructureCountyActions()); }, [dispatch]) const onVisibilityChange = (isVisible) => { if (isVisible) { @@ -46,9 +54,11 @@ const HisDeployments = () =>{ + + diff --git a/src/views/RR/HisDeploymentsEMRStatusByCounty.js b/src/views/RR/HisDeploymentsEMRStatusByCounty.js new file mode 100644 index 00000000..a48dc51d --- /dev/null +++ b/src/views/RR/HisDeploymentsEMRStatusByCounty.js @@ -0,0 +1,113 @@ +import React, { useEffect, useState, useCallback } from 'react'; +import Highcharts from 'highcharts'; +import { Card, CardBody, CardHeader } from 'reactstrap'; +import HighchartsReact from 'highcharts-react-official'; +import * as hisSelector from '../../selectors/RR/HisDeploymentsSelector'; +import { useSelector } from 'react-redux'; + +const HISDeploymentsByCounty = () => { + const hisStatusData = useSelector( + hisSelector.getFacilityStatusByCounty + ); + const [hisDeployments, setHisDeployments] = useState({}); + + const loadHisDeployments = useCallback(async () => { + setHisDeployments({ + chart: { + type: 'column', + }, + title: { + text: '', + align: 'left', + }, + xAxis: { + title: { + text: 'COUNTY', + }, + categories: hisStatusData.counties, + }, + yAxis: { + min: 0, + title: { + text: 'NUMBER OF FACILITIES', + }, + reversedStacks: false, + stackLabels: { + enabled: false, + style: { + fontWeight: 'bold', + color: + (Highcharts.defaultOptions.title.style && + Highcharts.defaultOptions.title.style.color) || + 'gray', + textOutline: 'none', + }, + }, + }, + legend: { + align: 'left', + verticalAlign: 'top', + floating: false, + shadow: false, + y: 0, + x: 80, + }, + tooltip: { + headerFormat: '{point.x}
', + pointFormat: + '{series.name}: {point.y}
Total: {point.stackTotal}', + }, + plotOptions: { + column: { + stacking: 'percent', + dataLabels: { + enabled: false, + }, + }, + }, + series: [ + { + name: 'ACTIVE', + data: hisStatusData.actives, + color: '#00AD30', + }, + { + name: 'DISCONTINUED', + data: hisStatusData.discontinueds, + color: '#152459', + }, + { + name: 'STALLED/INACTIVE', + data: hisStatusData.inactives, + color: '#F6941C', + } + ], + }); + }, [hisStatusData]); + + useEffect(() => { + loadHisDeployments(); + }, [loadHisDeployments]); + + return ( +
+
+ + + EMR STATUS BY COUNTY + + +
+ +
+
+
+
+
+ ); +}; + +export default HISDeploymentsByCounty; diff --git a/src/views/RR/HisDeploymentsFacilityByInfrastructure.js b/src/views/RR/HisDeploymentsFacilityByInfrastructure.js index a153a4d2..dcb3997a 100644 --- a/src/views/RR/HisDeploymentsFacilityByInfrastructure.js +++ b/src/views/RR/HisDeploymentsFacilityByInfrastructure.js @@ -90,7 +90,7 @@ const HisDeploymentsFacilityByInfrastructure = () => {
- ACTIVE FACILITY BY INFRASTRUCTURE DEPLOYMENT + ACTIVE FACILITY BY INFRASTRUCTURE DEPLOYMENT(PARTNER)
diff --git a/src/views/RR/HisDeploymentsFacilityByInfrastructureCounty.js b/src/views/RR/HisDeploymentsFacilityByInfrastructureCounty.js new file mode 100644 index 00000000..db639959 --- /dev/null +++ b/src/views/RR/HisDeploymentsFacilityByInfrastructureCounty.js @@ -0,0 +1,109 @@ +import React, { useEffect, useState, useCallback } from 'react'; +import Highcharts from 'highcharts'; +import { Card, CardBody, CardHeader } from 'reactstrap'; +import HighchartsReact from 'highcharts-react-official'; +import { useSelector } from 'react-redux'; +import * as hisSelector from '../../selectors/RR/HisDeploymentsSelector'; + +const HisDeploymentsFacilityByInfrastructureCounty = () => { + const [hisDeployments, setHisDeployments] = useState({}); + const hisStatusData = useSelector( + hisSelector.getFacilityByInfrastructureCounty + ); + + const loadHisByInfrastructure = useCallback(async () => { + setHisDeployments({ + chart: { + type: 'column', + }, + title: { + text: '', + align: 'left', + }, + xAxis: { + title: { + text: 'COUNTY', + }, + categories: hisStatusData.counties, + }, + yAxis: { + min: 0, + title: { + text: 'NUMBER OF FACILITIES', + }, + reversedStacks: false, + stackLabels: { + enabled: false, + style: { + fontWeight: 'bold', + color: + // theme + (Highcharts.defaultOptions.title.style && + Highcharts.defaultOptions.title.style.color) || + 'gray', + textOutline: 'none', + }, + }, + }, + legend: { + align: 'left', + verticalAlign: 'top', + floating: false, + shadow: false, + y: 0, + x: 80, + }, + tooltip: { + headerFormat: '{point.x}
', + pointFormat: + '{series.name}: {point.y}
Total: {point.stackTotal}', + }, + plotOptions: { + column: { + stacking: 'percent', + dataLabels: { + enabled: false, + }, + }, + }, + series: [ + { + name: 'ON PREMISES', + data: hisStatusData.onPremises, + color: '#00AD30', + }, + { + name: 'ON CLOUD', + data: hisStatusData.onCloud, + color: '#152459', + } + ], + }); + }, [hisStatusData]); + + useEffect(() => { + loadHisByInfrastructure(); + }, [loadHisByInfrastructure]); + + return ( +
+
+ + + ACTIVE FACILITY BY INFRASTRUCTURE DEPLOYMENT(COUNTY) + + +
+ +
+
+
+
+
+ ); +}; + +export default HisDeploymentsFacilityByInfrastructureCounty;