diff --git a/package.json b/package.json index 53988e1b..68912d53 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,8 @@ "@mui/icons-material": "^5.11.11", "@mui/material": "^5.11.14", "@reduxjs/toolkit": "^1.8.5", + "@superset-ui/embedded-sdk": "^0.1.0-alpha.8", + "@superset-ui/switchboard": "^1.5.0", "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.3.2", "@testing-library/user-event": "^7.1.2", @@ -23,7 +25,7 @@ "highcharts": "^8.2.2", "highcharts-react-official": "^3.0.0", "lodash": "^4.17.20", - "moment": "^2.27.0", + "moment": "^2.29.0", "mui-datatables": "^4.3.0", "oidc-client": "^1.10.1", "powerbi-client": "^2.22.2", @@ -87,7 +89,8 @@ ] }, "devDependencies": { - "prettier": "^2.2.1" + "prettier": "^2.2.1", + "react-syntax-highlighter": "^15.5.0" }, "homepage": "." } diff --git a/src/actions/RR/hisFacilityArtHtsMnchActions.js b/src/actions/RR/hisFacilityArtHtsMnchActions.js new file mode 100644 index 00000000..76f9b71d --- /dev/null +++ b/src/actions/RR/hisFacilityArtHtsMnchActions.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 loadHisFacilityArtHtsMnchAction = () => async (dispatch, getState) => { + const diffInMinutes = moment().diff( + moment(getState().hisFacilityArtHtsMnch.lastFetch), + 'minutes' + ); + if (getState().ui.currentPage !== PAGES.rr) { + return; + } + else if ((diffInMinutes < CACHING.LONG) && getState().filters.filtered === false) { + return; + } else { + await dispatch(fetchHisFacilityTxcurr()); + } +} + +export const fetchHisFacilityTxcurr = () => async (dispatch, getState) => { + dispatch({ type: actionTypes.HIS_FACILITY_ART_HTS_MNCH_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/facilityArtHtsMnch', params); + dispatch({ type: actionTypes.HIS_FACILITY_ART_HTS_MNCH_FETCH, payload: { filtered: getState().filters.filtered, list: response }}); +}; diff --git a/src/actions/RR/hisFacilityByInfrastructureActions.js b/src/actions/RR/hisFacilityByInfrastructureActions.js new file mode 100644 index 00000000..e49371ac --- /dev/null +++ b/src/actions/RR/hisFacilityByInfrastructureActions.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 loadHisFacilityByInfrastructureActions = () => 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_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/facilityByInfrastructure', params); + dispatch({ type: actionTypes.HIS_FACILITY_BY_INFRASTRUCTURE_FETCH, payload: { filtered: getState().filters.filtered, list: response }}); +}; diff --git a/src/actions/RR/hisFacilityByInfrastructureCountyActions.js b/src/actions/RR/hisFacilityByInfrastructureCountyActions.js new file mode 100644 index 00000000..53e52fe2 --- /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().hisFacilityByInfrastructureCounty.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/hisFacilityLevelByCountyActions.js b/src/actions/RR/hisFacilityLevelByCountyActions.js new file mode 100644 index 00000000..f98f26bd --- /dev/null +++ b/src/actions/RR/hisFacilityLevelByCountyActions.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 loadHisFacilityLevelByCountyAction = () => async (dispatch, getState) => { + const diffInMinutes = moment().diff( + moment(getState().hisFacilityStatusByCounty.lastFetch), + 'minutes' + ); + if (getState().ui.currentPage !== PAGES.rr) { + return; + } + else if ((diffInMinutes < CACHING.LONG) && getState().filters.filtered === false) { + return; + } else { + await dispatch(fetchHisFacilityLevelByCounty()); + } +} + +export const fetchHisFacilityLevelByCounty = () => async (dispatch, getState) => { + dispatch({ type: actionTypes.HIS_FACILITY_LEVEL_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, + 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/facilityLevelByOwnershipCounty', params); + dispatch({ type: actionTypes.HIS_FACILITY_LEVEL_BY_COUNTY_FETCH, payload: { filtered: getState().filters.filtered, list: response }}); +}; diff --git a/src/actions/RR/hisFacilityLevelByPartnerActions.js b/src/actions/RR/hisFacilityLevelByPartnerActions.js new file mode 100644 index 00000000..8679b312 --- /dev/null +++ b/src/actions/RR/hisFacilityLevelByPartnerActions.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 loadHisFacilityLevelByPartnerAction = () => 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(fetchHisFacilityLevelByPartner()); + } +} + +export const fetchHisFacilityLevelByPartner = () => async (dispatch, getState) => { + dispatch({ type: actionTypes.HIS_FACILITY_LEVEL_BY_PARTNER_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/facilityLevelByOwnershipPartner', params); + dispatch({ type: actionTypes.HIS_FACILITY_LEVEL_BY_PARTNER_FETCH, payload: { filtered: getState().filters.filtered, list: response }}); +}; diff --git a/src/actions/RR/hisFacilityLinelistActions.js b/src/actions/RR/hisFacilityLinelistActions.js new file mode 100644 index 00000000..d79e9276 --- /dev/null +++ b/src/actions/RR/hisFacilityLinelistActions.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 loadHisFacilityLinelistAction = () => async (dispatch, getState) => { + const diffInMinutes = moment().diff( + moment(getState().hisFacilityLinelist.lastFetch), + 'minutes' + ); + if (getState().ui.currentPage !== PAGES.rr) { + return; + } + else if ((diffInMinutes < CACHING.LONG) && getState().filters.filtered === false) { + return; + } else { + await dispatch(fetchHisFacilityLinelist()); + } +} + +export const fetchHisFacilityLinelist = () => async (dispatch, getState) => { + dispatch({ type: actionTypes.HIS_FACILITY_LINELIST_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/facilityLinelist', params); + dispatch({ type: actionTypes.HIS_FACILITY_LINELIST_FETCH, payload: { filtered: getState().filters.filtered, list: response }}); +}; diff --git a/src/actions/RR/hisFacilityStatusActions.js b/src/actions/RR/hisFacilityStatusActions.js new file mode 100644 index 00000000..f8451695 --- /dev/null +++ b/src/actions/RR/hisFacilityStatusActions.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 loadHisFacilityStatusAction = () => async (dispatch, getState) => { + const diffInMinutes = moment().diff( + moment(getState().hisFacilityStatus.lastFetch), + 'minutes' + ); + if (getState().ui.currentPage !== PAGES.rr) { + return; + } + else if ((diffInMinutes < CACHING.LONG) && getState().filters.filtered === false) { + return; + } else { + await dispatch(fetchHisFacilityStatus()); + } +} + +export const fetchHisFacilityStatus = () => async (dispatch, getState) => { + dispatch({ type: actionTypes.HIS_FACILITY_STATUS_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/facilityStatus', params); + dispatch({ type: actionTypes.HIS_FACILITY_STATUS_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/RR/hisFacilityStatusByPartnerActions.js b/src/actions/RR/hisFacilityStatusByPartnerActions.js new file mode 100644 index 00000000..025030d1 --- /dev/null +++ b/src/actions/RR/hisFacilityStatusByPartnerActions.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 loadHisFacilityStatusByPartnerAction = () => 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_PARTNER_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/facilityStatusByPartner', params); + dispatch({ type: actionTypes.HIS_FACILITY_STATUS_BY_PARTNER_FETCH, payload: { filtered: getState().filters.filtered, list: response }}); +}; diff --git a/src/actions/RR/hisFacilityTxcurrActions.js b/src/actions/RR/hisFacilityTxcurrActions.js new file mode 100644 index 00000000..c4115977 --- /dev/null +++ b/src/actions/RR/hisFacilityTxcurrActions.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 loadHisFacilityTxcurrAction = () => async (dispatch, getState) => { + const diffInMinutes = moment().diff( + moment(getState().hisFacilityTxcurr.lastFetch), + 'minutes' + ); + if (getState().ui.currentPage !== PAGES.rr) { + return; + } + else if ((diffInMinutes < CACHING.LONG) && getState().filters.filtered === false) { + return; + } else { + await dispatch(fetchHisFacilityTxcurr()); + } +} + +export const fetchHisFacilityTxcurr = () => async (dispatch, getState) => { + dispatch({ type: actionTypes.HIS_FACILITY_TXCURR_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/facilityTxcurr', params); + dispatch({ type: actionTypes.HIS_FACILITY_TXCURR_FETCH, payload: { filtered: getState().filters.filtered, list: response }}); +}; diff --git a/src/actions/types.js b/src/actions/types.js index 9ca05a22..b03a5439 100644 --- a/src/actions/types.js +++ b/src/actions/types.js @@ -511,6 +511,46 @@ export const RR_OVERALL_REPORTING_RATES_BY_FACILITY_REPORTED_REQUEST = 'RR_OVERA export const RR_OVERALL_REPORTING_RATES_BY_FACILITY_REPORTED_FETCH = 'RR_OVERALL_REPORTING_RATES_BY_FACILITY_REPORTED_FETCH' export const RR_OVERALL_REPORTING_RATES_BY_FACILITY_REPORTED_REQUEST_FAILED = 'RR_OVERALL_REPORTING_RATES_BY_FACILITY_REPORTED_REQUEST_FAILED' +export const HIS_FACILITY_STATUS_REQUEST = 'HIS_FACILITY_STATUS_REQUEST' +export const HIS_FACILITY_STATUS_FETCH = 'HIS_FACILITY_STATUS_FETCH' +export const HIS_FACILITY_STATUS_FAILED = 'HIS_FACILITY_STATUS_FAILED' + +export const HIS_FACILITY_STATUS_BY_PARTNER_REQUEST = 'HIS_FACILITY_STATUS_BY_PARTNER_REQUEST' +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' + +export const HIS_FACILITY_LEVEL_BY_COUNTY_REQUEST = 'HIS_FACILITY_LEVEL_BY_COUNTY_REQUEST' +export const HIS_FACILITY_LEVEL_BY_COUNTY_FETCH = 'HIS_FACILITY_LEVEL_BY_COUNTY_FETCH' +export const HIS_FACILITY_LEVEL_BY_COUNTY_FAILED = 'HIS_FACILITY_LEVEL_BY_COUNTY_FAILED' + +export const HIS_FACILITY_BY_INFRASTRUCTURE_REQUEST = 'HIS_FACILITY_BY_INFRASTRUCTURE_REQUEST' +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' + +export const HIS_FACILITY_ART_HTS_MNCH_REQUEST = 'HIS_FACILITY_ART_HTS_MNCH_REQUEST' +export const HIS_FACILITY_ART_HTS_MNCH_FETCH = 'HIS_FACILITY_ART_HTS_MNCH_FETCH' +export const HIS_FACILITY_ART_HTS_MNCH_FAILED = 'HIS_FACILITY_ART_HTS_MNCH_FAILED' + +export const HIS_FACILITY_TXCURR_REQUEST = 'HIS_FACILITY_TXCURR_REQUEST' +export const HIS_FACILITY_TXCURR_FETCH = 'HIS_FACILITY_TXCURR_FETCH' +export const HIS_FACILITY_TXCURR_FAILED = 'HIS_FACILITY_TXCURR_FAILED' + export const RR_OVERALL_REPORTING_RATES_BY_FACILITY_NOT_REPORTED_REQUEST = 'RR_OVERALL_REPORTING_RATES_BY_FACILITY_NOT_REPORTED_REQUEST' export const RR_OVERALL_REPORTING_RATES_BY_FACILITY_NOT_REPORTED_FETCH = 'RR_OVERALL_REPORTING_RATES_BY_FACILITY_NOT_REPORTED_FETCH' export const RR_OVERALL_REPORTING_RATES_BY_FACILITY_NOT_REPORTED_REQUEST_FAILED = 'RR_OVERALL_REPORTING_RATES_BY_FACILITY_NOT_REPORTED_REQUEST_FAILED' diff --git a/src/constants.js b/src/constants.js index 49295a42..2840bf4a 100644 --- a/src/constants.js +++ b/src/constants.js @@ -8,6 +8,7 @@ export const HOME_TABS = { export const RR_TABS = { ct: 'CARE & TREATMENT', hts: 'HIV TESTING SERVICES', + his: 'HIS DEPLOYMENTS' }; export const HTS_TABS = { diff --git a/src/containers/DefaultLayout/DefaultHeader.js b/src/containers/DefaultLayout/DefaultHeader.js index cc83ddea..fc949909 100644 --- a/src/containers/DefaultLayout/DefaultHeader.js +++ b/src/containers/DefaultLayout/DefaultHeader.js @@ -34,7 +34,7 @@ const DefaultHeader = () => { setCookie('metabase.SESSION', sessionId, { path: '/' }); } } - }, []); + }, []); const login = async () => { const res = await getUserType(); if (user && !user.expired) { @@ -62,7 +62,7 @@ const DefaultHeader = () => { const clearCacheData = async () => { await localStorage.clear(); if (localStorage.length === 0){ - alert('Complete Cache Cleared'); + alert('Complete Cache Cleared'); window.location.reload(); } }; @@ -212,6 +212,9 @@ const Adhoc = () => { > Superset + + TAFSIRI + ); diff --git a/src/index.js b/src/index.js index 6784cd50..808e74ab 100644 --- a/src/index.js +++ b/src/index.js @@ -1,17 +1,17 @@ -import React from "react"; -import ReactDOM from "react-dom"; -import { Provider } from "react-redux"; +import React from 'react'; +import ReactDOM from 'react-dom'; +import { Provider } from 'react-redux'; import { PersistGate } from 'redux-persist/integration/react'; -import "./index.css"; -import App from "./App"; -import Loading from "./views/Shared/Loading"; +import './index.css'; +import App from './App'; +import Loading from './views/Shared/Loading'; import { store, persistor } from './store'; import { RecoilRoot, atom, selector, useRecoilState, - useRecoilValue, + useRecoilValue } from 'recoil'; import { CookiesProvider } from 'react-cookie'; @@ -25,5 +25,5 @@ ReactDOM.render( , - document.getElementById("root") -) + document.getElementById('root') +); diff --git a/src/reducers/RR/hisFacilityArtHtsMnch.js b/src/reducers/RR/hisFacilityArtHtsMnch.js new file mode 100644 index 00000000..fe86e065 --- /dev/null +++ b/src/reducers/RR/hisFacilityArtHtsMnch.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_ART_HTS_MNCH_REQUEST: + newState.loading = true; + return newState; + case actionTypes.HIS_FACILITY_ART_HTS_MNCH_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_ART_HTS_MNCH_FAILED: + newState.loading = false; + return newState; + default: + return state; + } +} diff --git a/src/reducers/RR/hisFacilityByInfrastructure.js b/src/reducers/RR/hisFacilityByInfrastructure.js new file mode 100644 index 00000000..b5a607d9 --- /dev/null +++ b/src/reducers/RR/hisFacilityByInfrastructure.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_REQUEST: + newState.loading = true; + return newState; + case actionTypes.HIS_FACILITY_BY_INFRASTRUCTURE_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_FAILED: + newState.loading = false; + return newState; + default: + return state; + } +} 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/hisFacilityLevelByCounty.js b/src/reducers/RR/hisFacilityLevelByCounty.js new file mode 100644 index 00000000..dfd01590 --- /dev/null +++ b/src/reducers/RR/hisFacilityLevelByCounty.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_LEVEL_BY_COUNTY_REQUEST: + newState.loading = true; + return newState; + case actionTypes.HIS_FACILITY_LEVEL_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_LEVEL_BY_COUNTY_FAILED: + newState.loading = false; + return newState; + default: + return state; + } +} diff --git a/src/reducers/RR/hisFacilityLevelByPartner.js b/src/reducers/RR/hisFacilityLevelByPartner.js new file mode 100644 index 00000000..9e9eba11 --- /dev/null +++ b/src/reducers/RR/hisFacilityLevelByPartner.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_LEVEL_BY_PARTNER_REQUEST: + newState.loading = true; + return newState; + case actionTypes.HIS_FACILITY_LEVEL_BY_PARTNER_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_LEVEL_BY_PARTNER_FAILED: + newState.loading = false; + return newState; + default: + return state; + } +} diff --git a/src/reducers/RR/hisFacilityLinelist.js b/src/reducers/RR/hisFacilityLinelist.js new file mode 100644 index 00000000..c10a0034 --- /dev/null +++ b/src/reducers/RR/hisFacilityLinelist.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_LINELIST_REQUEST: + newState.loading = true; + return newState; + case actionTypes.HIS_FACILITY_LINELIST_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_LINELIST_FAILED: + newState.loading = false; + return newState; + default: + return state; + } +} diff --git a/src/reducers/RR/hisFacilityStatus.js b/src/reducers/RR/hisFacilityStatus.js new file mode 100644 index 00000000..df2ed34f --- /dev/null +++ b/src/reducers/RR/hisFacilityStatus.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_REQUEST: + newState.loading = true; + return newState; + case actionTypes.HIS_FACILITY_STATUS_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_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/RR/hisFacilityStatusByPartner.js b/src/reducers/RR/hisFacilityStatusByPartner.js new file mode 100644 index 00000000..c15e5de0 --- /dev/null +++ b/src/reducers/RR/hisFacilityStatusByPartner.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_PARTNER_REQUEST: + newState.loading = true; + return newState; + case actionTypes.HIS_FACILITY_STATUS_BY_PARTNER_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_PARTNER_FAILED: + newState.loading = false; + return newState; + default: + return state; + } +} diff --git a/src/reducers/RR/hisFacilityTxcurr.js b/src/reducers/RR/hisFacilityTxcurr.js new file mode 100644 index 00000000..95f889da --- /dev/null +++ b/src/reducers/RR/hisFacilityTxcurr.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_TXCURR_REQUEST: + newState.loading = true; + return newState; + case actionTypes.HIS_FACILITY_TXCURR_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_TXCURR_FAILED: + newState.loading = false; + return newState; + default: + return state; + } +} diff --git a/src/reducers/index.js b/src/reducers/index.js index 06360418..245886f4 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -326,6 +326,17 @@ import missedEIDOverview from './PMTCTRRI/MissedEID/missedEIDOverview'; import missedEIDSDP from './PMTCTRRI/MissedEID/missedEIDSDP'; import missedEIDMissingPCR from './PMTCTRRI/MissedEID/missedEIDMissingPCR'; +import hisFacilityByInfrastructure from './RR/hisFacilityByInfrastructure'; +import hisFacilityLinelist from './RR/hisFacilityLinelist'; +import hisFacilityLevelByCounty from './RR/hisFacilityLevelByCounty'; +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 hisFacilityArtHtsMnch from './RR/hisFacilityArtHtsMnch'; + import missedInfantProphylaxis from './PMTCTRRI/MissedInfantProphylaxis/missedInfantProphylaxis'; import missedViralLoad from './PMTCTRRI/MissedViralLoad/missedViralLoad'; @@ -650,4 +661,15 @@ export default combineReducers({ missedInfantProphylaxis, missedViralLoad, missedDTG, + + hisFacilityByInfrastructure, + hisFacilityLevelByCounty, + hisFacilityLevelByPartner, + hisFacilityLinelist, + hisFacilityStatus, + hisFacilityStatusByPartner, + hisFacilityTxcurr, + hisFacilityByInfrastructureCounty, + hisFacilityStatusByCounty, + hisFacilityArtHtsMnch, }); diff --git a/src/routes.js b/src/routes.js index c14d12b0..7ff27153 100644 --- a/src/routes.js +++ b/src/routes.js @@ -1,21 +1,73 @@ -import React from 'react'; import Loadable from 'react-loadable'; import withTracker from './withTracker'; import Loading from './views/Shared/Loading'; -import { LOADING_DELAY } from "./constants"; +import { LOADING_DELAY } from './constants'; -const Home = Loadable({ loader: () => import('./views/Home/Home'), loading: Loading, delay: LOADING_DELAY }); -const RR = Loadable({ loader: () => import('./views/RR/RR'), loading: Loading, delay: LOADING_DELAY }); -const HTS = Loadable({ loader: () => import('./views/HTS/HTS'), loading: Loading, delay: LOADING_DELAY }); -const CT = Loadable({ loader: () => import('./views/CT/CT'), loading: Loading, delay: LOADING_DELAY }); -const SD = Loadable({ loader: () => import('./views/ServiceDesk/ServiceDesk'), loading: Loading, delay: LOADING_DELAY }); -const OperationalHIS = Loadable({ loader: () => import('./views/Operational&HIS/OperationalHIS'), loading: Loading, delay: LOADING_DELAY }); -const PMTCTRRI = Loadable({ loader: () => import('./views/PMTCTRRI/PMTCTRRI'), loading: Loading, delay: LOADING_DELAY,}); -const GIS = Loadable({ loader: () => import('./views/GIS/GIS'), loading: Loading, delay: LOADING_DELAY }); -const HRH = Loadable({ loader: () => import('./views/HRH/HRH'), loading: Loading, delay: LOADING_DELAY }); -const Resources = Loadable({ loader: () => import('./views/Resources/Resources'), loading: Loading, delay: LOADING_DELAY }); -const Organizations = Loadable({ loader: () => import('./views/Administration/Organizations/Organizations'), loading: Loading, delay: LOADING_DELAY }); -const Profile = Loadable({ loader: () => import('./views/Users/Profile'), loading: Loading, delay: LOADING_DELAY }); +const Home = Loadable({ + loader: () => import('./views/Home/Home'), + loading: Loading, + delay: LOADING_DELAY, +}); +const RR = Loadable({ + loader: () => import('./views/RR/RR'), + loading: Loading, + delay: LOADING_DELAY, +}); +const HTS = Loadable({ + loader: () => import('./views/HTS/HTS'), + loading: Loading, + delay: LOADING_DELAY, +}); +const CT = Loadable({ + loader: () => import('./views/CT/CT'), + loading: Loading, + delay: LOADING_DELAY, +}); +const SD = Loadable({ + loader: () => import('./views/ServiceDesk/ServiceDesk'), + loading: Loading, + delay: LOADING_DELAY, +}); +const OperationalHIS = Loadable({ + loader: () => import('./views/Operational&HIS/OperationalHIS'), + loading: Loading, + delay: LOADING_DELAY, +}); +const PMTCTRRI = Loadable({ + loader: () => import('./views/PMTCTRRI/PMTCTRRI'), + loading: Loading, + delay: LOADING_DELAY, +}); +const GIS = Loadable({ + loader: () => import('./views/GIS/GIS'), + loading: Loading, + delay: LOADING_DELAY, +}); +const HRH = Loadable({ + loader: () => import('./views/HRH/HRH'), + loading: Loading, + delay: LOADING_DELAY, +}); +const Resources = Loadable({ + loader: () => import('./views/Resources/Resources'), + loading: Loading, + delay: LOADING_DELAY, +}); +const Organizations = Loadable({ + loader: () => import('./views/Administration/Organizations/Organizations'), + loading: Loading, + delay: LOADING_DELAY, +}); +const Profile = Loadable({ + loader: () => import('./views/Users/Profile'), + loading: Loading, + delay: LOADING_DELAY, +}); +const Tafsiri = Loadable({ + loader: () => import('./views/Tafsiri/Tafsiri'), + loading: Loading, + delay: LOADING_DELAY, +}); const routes = [ { @@ -108,6 +160,13 @@ const routes = [ name: 'Highlight of the month', private: false, }, + { + path: '/tafsiri', + exact: true, + name: 'TAFSIRI', + private: true, + component: withTracker(Tafsiri), + }, ]; export default routes; diff --git a/src/selectors/RR/HisDeploymentsSelector.js b/src/selectors/RR/HisDeploymentsSelector.js new file mode 100644 index 00000000..a5185011 --- /dev/null +++ b/src/selectors/RR/HisDeploymentsSelector.js @@ -0,0 +1,310 @@ +import { createSelector } from 'reselect'; +import { formatNumber } from '../../utils/utils'; + +const listStatusUnfiltered = state => state.hisFacilityStatus.listUnfiltered; +const listStatusFiltered = state => state.hisFacilityStatus.listFiltered; +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; + +const listLevelByCountyUnfiltered = state => state.hisFacilityLevelByCounty.listUnfiltered; +const listLevelByCountyFiltered = state => state.hisFacilityLevelByCounty.listFiltered; + +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; + +const listTxcurrUnfiltered = state => state.hisFacilityTxcurr.listUnfiltered; +const listTxcurrFiltered = state => state.hisFacilityTxcurr.listFiltered; +const listTxcurrLoading = state => state.hisFacilityTxcurr.loading; + +const listFacilityArtHtsMnchUnfiltered = state => state.hisFacilityArtHtsMnch.listUnfiltered; +const listFacilityArtHtsMnchFiltered = state => state.hisFacilityArtHtsMnch.listFiltered; +const listFacilityArtHtsMnchLoading = state => state.hisFacilityArtHtsMnch.loading; + +export const getFacilityStatus = createSelector( + [listStatusUnfiltered, listStatusFiltered, filtered], + (listUnfiltered, listFiltered, filtered) => { + const list = filtered ? listFiltered : listUnfiltered; + let active = list.find(x => x.EMR_Status.toLowerCase() === 'active')?.facilities + let discontinued = list.find(x => x.EMR_Status.toLowerCase() === "discontinued")?.facilities + let stalled = list.find(x => x.EMR_Status.toLowerCase() === "stalled/inactive")?.facilities + list.find(x => x.EMR_Status.toLowerCase() === "inactive")?.facilities + + return { active, discontinued, stalled }; + } +); + +export const getFacilityStatusByPartner = createSelector( + [listStatusByPartnerUnfiltered, listStatusByPartnerFiltered, filtered], + (listUnfiltered, listFiltered, filtered) => { + const list = filtered ? listFiltered : listUnfiltered; + let data = list.reduce((acc, curr) => { + const { facilities, EMR_Status, PartnerName } = curr + if(!acc[PartnerName]) { + acc[PartnerName] = { + "PartnerName": PartnerName, + "Active": 0, + "Discontinued": 0, + "Inactive/Stalled": 0, + "Total": 0 + } + } + const statusKey = (EMR_Status === "Stalled/Inactive" || EMR_Status === "Inactive") ? "Inactive/Stalled" : EMR_Status; + + acc[PartnerName][statusKey] += parseInt(facilities); + acc[PartnerName].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 partnerNames = formattedResult.map(i => i.PartnerName?.toUpperCase()) + const actives = formattedResult.map(i => i.Active) + const discontinueds = formattedResult.map(i => i.Discontinued) + const inactives = formattedResult.map(i => i["Inactive/Stalled"]) + + return { partnerNames, actives, discontinueds, inactives } + } +); + +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) => { + const list = filtered ? listFiltered : listUnfiltered; + let data = list.reduce((acc, curr) => { + const { facilities, InfrastructureType, PartnerName } = curr + if(!acc[PartnerName]) { + acc[PartnerName] = { + "PartnerName": PartnerName, + "On Premises": 0, + "On Cloud": 0, + "Total": 0 + } + } + + acc[PartnerName][InfrastructureType] += parseInt(facilities); + acc[PartnerName].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 partnerNames = formattedResult.map(i => i.PartnerName?.toUpperCase()) + const onPremises = formattedResult.map(i => i["On Premises"]) + const onCloud = formattedResult.map(i => i["On Cloud"]) + + return { partnerNames, onPremises, onCloud } + } +); + +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) => { + const list = filtered ? listFiltered : listUnfiltered; + let data = list.reduce((acc, curr) => { + const { facilities, Keph_level, PartnerName } = curr + if(!acc[PartnerName]) { + acc[PartnerName] = { + "PartnerName": PartnerName, + "Level 1": 0, + "Level 2": 0, + "Level 3": 0, + "Level 4": 0, + "Level 5": 0, + "Level 6": 0, + "Total": 0 + } + } + + acc[PartnerName][Keph_level] += parseInt(facilities); + acc[PartnerName].Total += parseInt(facilities); + return acc + }, {}); + let formattedResult = Object.values(data); + formattedResult.sort((a, b) => b["Level 2"] - a["Level 2"]); + formattedResult = formattedResult.map(partner => { + partner.lvl2Perc = partner.Total ? (partner["Level 2"]/partner.Total)*100 : 0 + return partner + }).sort((a, b) => b.lvl2Perc - a.lvl2Perc) + + const partnerNames = formattedResult.map(i => i.PartnerName?.toUpperCase()) + const lvl1 = formattedResult.map(i => i["Level 1"]) + const lvl2 = formattedResult.map(i => i["Level 2"]) + const lvl3 = formattedResult.map(i => i["Level 3"]) + const lvl4 = formattedResult.map(i => i["Level 4"]) + const lvl5 = formattedResult.map(i => i["Level 5"]) + const lvl6 = formattedResult.map(i => i["Level 6"]) + + return { partnerNames, lvl1, lvl2, lvl3, lvl4, lvl5, lvl6 } + } +); + +export const getFacilityLevelByCounty = createSelector( + [listLevelByCountyUnfiltered, listLevelByCountyFiltered, filtered], + (listUnfiltered, listFiltered, filtered) => { + const list = filtered ? listFiltered : listUnfiltered; + let data = list.reduce((acc, curr) => { + const { facilities, Keph_level, County } = curr + if(!acc[County]) { + acc[County] = { + "County": County, + "Level 1": 0, + "Level 2": 0, + "Level 3": 0, + "Level 4": 0, + "Level 5": 0, + "Level 6": 0, + "Total": 0 + } + } + + acc[County][Keph_level] += parseInt(facilities); + acc[County].Total += parseInt(facilities); + return acc + }, {}); + let formattedResult = Object.values(data); + formattedResult.sort((a, b) => b["Level 2"] - a["Level 2"]); + formattedResult = formattedResult.map(partner => { + partner.lvl2Perc = partner.Total ? (partner["Level 2"]/partner.Total)*100 : 0 + return partner + }).sort((a, b) => b.lvl2Perc - a.lvl2Perc) + + const counties = formattedResult.map(i => i.County?.toUpperCase()) + const lvl1 = formattedResult.map(i => i["Level 1"]) + const lvl2 = formattedResult.map(i => i["Level 2"]) + const lvl3 = formattedResult.map(i => i["Level 3"]) + const lvl4 = formattedResult.map(i => i["Level 4"]) + const lvl5 = formattedResult.map(i => i["Level 5"]) + const lvl6 = formattedResult.map(i => i["Level 6"]) + + return { counties, lvl1, lvl2, lvl3, lvl4, lvl5, lvl6 } + } +); + +export const getFacilityLinelist = createSelector( + [listLinelistUnfiltered, listLinelistFiltered, filtered, listLinelistLoading], + (listUnfiltered, listFiltered, filtered, loading) => { + const list = filtered ? listFiltered : listUnfiltered; + + return { list, loading }; + } +); + +export const getFacilityArtHtsMnchLinelist = createSelector( + [listFacilityArtHtsMnchUnfiltered, listFacilityArtHtsMnchFiltered, filtered, listFacilityArtHtsMnchLoading], + (listUnfiltered, listFiltered, filtered, loading) => { + const list = filtered ? listFiltered : listUnfiltered; + let data = list.map(d => { + return { ...d, 'CurrentOnART_Total': formatNumber(d.CurrentOnART_Total), 'Tested_Total': formatNumber(d.Tested_Total), 'onMaternalHAARTtotal': formatNumber(d.onMaternalHAARTtotal) }; + }) + + return { 'list': data, loading }; + } +); + +export const getFacilityTxCurr = createSelector( + [listTxcurrUnfiltered, listTxcurrFiltered, filtered, listTxcurrLoading], + (listUnfiltered, listFiltered, filtered, loading) => { + const list = filtered ? listFiltered : listUnfiltered; + + let data = list.map((d) => { + d = d.KEPH_Level === null ? { ...d, 'KEPH_Level': 'Missing' } : d; + return {...d, 'TxCurr': formatNumber(d.TxCurr)} + }) + + return { 'list': data, loading }; + } +); diff --git a/src/views/CT/CT.js b/src/views/CT/CT.js index dbd30912..e3bef024 100644 --- a/src/views/CT/CT.js +++ b/src/views/CT/CT.js @@ -353,12 +353,12 @@ import { loadCurrentOnArtByFacility } from '../../actions/CT/CurrentOnArt/curren import { loadCurrentOnArt } from '../../actions/CT/CurrentOnArt/currentOnArtActions'; import { loadCurrOnARTKHIS } from '../../actions/Operational&HIS/Comparison/currOnArtKHISActions'; import { loadArtVerificationByCounty } from '../../actions/CT/ArtVerification/artVerificationByCountyActions'; -import { loadArtVerificationByPartner } from './../../actions/CT/ArtVerification/artVerificationByPartnerActions'; -import { loadArtVerificationReasons } from './../../actions/CT/ArtVerification/artVerificationReasonsActions'; +import { loadArtVerificationByPartner } from '../../actions/CT/ArtVerification/artVerificationByPartnerActions'; +import { loadArtVerificationReasons } from '../../actions/CT/ArtVerification/artVerificationReasonsActions'; import { loadAppointmentKeepingWaterfall } from '../../actions/CT/TreatmentOutcomes/appointmentKeepingWaterfallActions'; import { loadQuaterlyIIT } from '../../actions/CT/TreatmentOutcomes/quaterlyIITActions'; -import { loadIITTracing } from './../../actions/CT/TreatmentOutcomes/IITTracingActions'; -import { loadIITTracingOutcomes } from './../../actions/CT/TreatmentOutcomes/IITTracingOutcomesActions'; +import { loadIITTracing } from '../../actions/CT/TreatmentOutcomes/IITTracingActions'; +import { loadIITTracingOutcomes } from '../../actions/CT/TreatmentOutcomes/IITTracingOutcomesActions'; import { loadViralLoadUptakeUToU } from '../../actions/CT/ViralLoad/viralLoadUptakeUToUActions'; import { loadViralLoadCategorizationUToU } from '../../actions/CT/ViralLoad/viralLoadCategorizationUToUActions'; import { loadAlhivOnArtByAgeSex } from '../../actions/CT/OTZ/OtzAlhivOnArtByAgeSexActions'; @@ -498,6 +498,9 @@ const CT = () => { } else { dispatch(disablePBFWFilter()); } + return () => { + disablePBFWFilter(); + } }, [dispatch, active_tab, mini_tab]); useEffect(() => { @@ -802,7 +805,7 @@ const CT = () => { dispatch(loadArtVerificationByCounty()) dispatch(loadArtVerificationByPartner()) dispatch(loadArtVerificationReasons()) - + dispatch(loadCurrentOnArtVerifiedByCounty(active_tab)); dispatch(loadCurrentOnArtVerifiedByPartner(active_tab)); dispatch(loadCurrentOnArtByCounty(active_tab)); diff --git a/src/views/CT/ViralLoad/ViralLoadOverallNonSuppressedVlTest.js b/src/views/CT/ViralLoad/ViralLoadOverallNonSuppressedVlTest.js index 6714aea4..89a9372d 100644 --- a/src/views/CT/ViralLoad/ViralLoadOverallNonSuppressedVlTest.js +++ b/src/views/CT/ViralLoad/ViralLoadOverallNonSuppressedVlTest.js @@ -27,9 +27,10 @@ const ViralLoadOverallNonSuppressedVlTest = () => { }, title: { text: '' }, xAxis: [{ - categories: ['NUMBER OF PLHIV WITH > 1000cpm', 'NUMBER OF PLHIV ON ART WITH VL > 1000 COPIES/ML WHO RECEIVED EAC', - 'NUMBER OF PLHIV ON ART WITH VL > 1000 COPIES/ML WHO RECEIVED FOLLOW UP VL TESTS', 'NUMBER OF PLHIV ON ART WITH VL > 1000 COPIES/ML WHO RECEIVED FOLLOW UP VL TESTS', - 'NUMBER WITH FOLLOW UP VL TEST AT VL > 1000 COPIES/ML SWITCHED TO SECOND LINE REGIMENT'], + categories: ['NUMBER OF PLHIV WITH > 200 COPIES/ML', 'NUMBER OF PLHIV ON ART WITH VL > 200 COPIES/ML WHO RECEIVED EAC', + 'NUMBER OF PLHIV ON ART WITH VL > 200 COPIES/ML WHO RECEIVED FOLLOW UP VL TESTS', + 'NUMBER OF PLHIV ON ART WITH VL > 200 COPIES/ML WHO RECEIVED FOLLOW UP VL TESTS', + 'NUMBER WITH FOLLOW UP VL TEST AT VL > 200 COPIES/ML SWITCHED TO SECOND LINE REGIMENT'], crosshair: true }], yAxis: [ @@ -43,11 +44,8 @@ const ViralLoadOverallNonSuppressedVlTest = () => { returnString = '' + this.x + '
'; allSeries.forEach(function(ser) { - if (ser.options.stack === thisPoint.series.options.stack) { - if (ser.data[thisIndex].y !== null) { - returnString += ser.points[thisIndex].name + ': ' + ser.points[thisIndex].y + '
'; - } - + if (ser.options.stack === thisPoint.series.options.stack && ser.data[thisIndex].y !== null) { + returnString += ser.points[thisIndex].name + ': ' + ser.points[thisIndex].y + '
'; } }); @@ -79,7 +77,7 @@ const ViralLoadOverallNonSuppressedVlTest = () => { name: 'VIRAL LOAD CASCADE', data: [ { - name: 'NUMBER OF PLHIV WITH > 1000cpm', + name: 'NUMBER OF PLHIV WITH > 200cpm', y: viralLoadUptakeGt1000Copies, color: '#bb1414', text: viralLoadUptakeGt1000Copies.toLocaleString('en'), @@ -92,7 +90,7 @@ const ViralLoadOverallNonSuppressedVlTest = () => { text: 'EAC 3
' + viralLoadUptakeGt1000CopiesEac.EACVisitDate_3?.toLocaleString('en') + ' (' + parseFloat(((viralLoadUptakeGt1000CopiesEac.EACVisitDate_3 / Object.values(viralLoadUptakeGt1000CopiesEac).reduce((a, b) => a + b, 0)) * 100).toString()).toFixed(0) + '%)' }, { - name: 'NUMBER OF PLHIV ON ART WITH VL > 1000 COPIES/ML WHO RECEIVED FOLLOW UP VL TESTS', + name: 'NUMBER OF PLHIV ON ART WITH VL > 200 COPIES/ML WHO RECEIVED FOLLOW UP VL TESTS', y: viralLoadUptakeGt1000CopiesRecFollowTestAll, color: '#fad53f', text: viralLoadUptakeGt1000CopiesRecFollowTestAll.toLocaleString('en') @@ -104,7 +102,7 @@ const ViralLoadOverallNonSuppressedVlTest = () => { text: ' NUMBER VIRALLY SUPPRESSED
ON FOLLOW UP VL TEST ' + viralLoadUptakeGt1000CopiesRecFollowTest[0]?.Num.toLocaleString('en') + ' (' + parseFloat(((viralLoadUptakeGt1000CopiesRecFollowTest[0]?.Num / totalFollowTest) * 100).toString()).toFixed(0) + '%)' }, { - name: 'NUMBER WITH FOLLOW UP VL TEST AT VL > 1000 COPIES/ML SWITCHED TO SECOND LINE REGIMENT', + name: 'NUMBER WITH FOLLOW UP VL TEST AT VL > 200 COPIES/ML SWITCHED TO SECOND LINE REGIMENT', y: viralLoadOverallNumberGt1000CopiesSecondlineRegimentData ?? 0, color: '#142459', text: viralLoadOverallNumberGt1000CopiesSecondlineRegimentData?.toLocaleString('en') @@ -123,10 +121,10 @@ const ViralLoadOverallNonSuppressedVlTest = () => { }, null, { - name: 'NUMBER WHO HAD > 1000 COPIES/ML ON A FOLLOW UP TEST', + name: 'NUMBER WHO HAD > 200 COPIES/ML ON A FOLLOW UP TEST', y: viralLoadUptakeGt1000CopiesRecFollowTest[1]?.Num ?? 0, color: '#bb1414', - text: 'NUMBER WHO HAD > 1000 COPIES/ML
ON A FOLLOW UP TEST ' + viralLoadUptakeGt1000CopiesRecFollowTest[1]?.Num.toLocaleString('en') + ' (' + parseFloat(((viralLoadUptakeGt1000CopiesRecFollowTest[1]?.Num / totalFollowTest) * 100).toString()).toFixed(0) + '%)' + text: 'NUMBER WHO HAD > 200 COPIES/ML
ON A FOLLOW UP TEST ' + viralLoadUptakeGt1000CopiesRecFollowTest[1]?.Num.toLocaleString('en') + ' (' + parseFloat(((viralLoadUptakeGt1000CopiesRecFollowTest[1]?.Num / totalFollowTest) * 100).toString()).toFixed(0) + '%)' }, null ] @@ -159,7 +157,7 @@ const ViralLoadOverallNonSuppressedVlTest = () => {
- {'VL CASCADE FOR PATIENTS WITH A NON-SUPPRESSED VL TEST RESULT FOR THE LAST 12 MONTHS (VL > 1000 copies/ml)'} + {'VL CASCADE FOR PATIENTS WITH A NON-SUPPRESSED VL TEST RESULT FOR THE LAST 12 MONTHS (VL > 200 copies/ml)'}
diff --git a/src/views/Highlight/Highlight.js b/src/views/Highlight/Highlight.js new file mode 100644 index 00000000..52b8bef6 --- /dev/null +++ b/src/views/Highlight/Highlight.js @@ -0,0 +1,10 @@ +import HighlightTabs from './HighlightTabs'; + +const Highlight = () => { + + return ( + + ); +} + +export default Highlight; diff --git a/src/views/Highlight/HighlightCOP.js b/src/views/Highlight/HighlightCOP.js new file mode 100644 index 00000000..557a9f05 --- /dev/null +++ b/src/views/Highlight/HighlightCOP.js @@ -0,0 +1,97 @@ +import React from 'react'; +import axios from 'axios'; +import { embedDashboard } from "@superset-ui/embedded-sdk"; + + +const supersetUrl = 'https://dwhanalytics.kenyahmis.org' +const supersetApiUrl = supersetUrl + '/api/v1/security' +const dashboardId = "7aa9c6c5-2904-4d6f-87e3-9a36a0b51e6e" + +async function getToken() { + + //calling login to get access token + const login_body = { + "password": "admin", + "provider": "db", + "refresh": true, + "username": "admin" + }; + + const login_headers = { + "headers": { + "Content-Type": "application/json" + } + } + + // console.log(supersetApiUrl + '/login') + const { data } = await axios.post(supersetApiUrl + '/login', login_body, login_headers) + const access_token = data['access_token'] + // console.log(access_token) + + + // Calling guest token + const guest_token_body = { + "resources": [ + { + "type": "dashboard", + "id": dashboardId, + } + ], + "rls": [], + "user": { + "username": "report-viewer", + "first_name": "report-viewer", + "last_name": "report-viewer", + } + }; + + const guest_token_headers = { + "headers": { + "Content-Type": "application/json", + "Authorization": 'Bearer ' + access_token + } + } + + + // console.log(supersetApiUrl + '/guest_token/') + // console.log(guest_token_body) + // console.log(guest_token_headers) + await axios.post(supersetApiUrl + '/guest_token/', guest_token_body, guest_token_headers).then(dt=>{ + // console.log(dt.data['token']) + embedDashboard({ + id: dashboardId, // given by the Superset embedding UI + supersetDomain: supersetUrl, + mountPoint: document.getElementById("superset-container"), // html element in which iframe render + fetchGuestToken: () => dt.data['token'], + dashboardUiConfig: { + hideTitle: false, + hideChartControls: true, + hideTab: true, + filters: { + expanded: true, + }, + } + }); + }) + + var iframe = document.querySelector("iframe") + if (iframe) { + iframe.style.width = '100%'; // Set the width as needed + iframe.style.minHeight = '70vw'; // Set the height as needed + iframe.style.overflow = 'hidden' + iframe.style.border = 0 + } + +} + +const HighlightCOP = () => { + getToken() + + return ( + <> +
+ + ) +} + +export default HighlightCOP diff --git a/src/views/Highlight/HighlightInternal.js b/src/views/Highlight/HighlightInternal.js new file mode 100644 index 00000000..76b616b5 --- /dev/null +++ b/src/views/Highlight/HighlightInternal.js @@ -0,0 +1,30 @@ +import { useEffect, useState } from 'react'; +import axios from 'axios'; +import { DWH_API_URL } from '../../constants'; + +const HighlightInternal = () => { + const [iframeUrl, setIframeUrl] = useState(''); + + useEffect(() => { + axios.get(`${DWH_API_URL}self-service/monthly-highlight`) + .then(res => { + const url = res.data?.url; + setIframeUrl(url); + }) + .catch(error => { + console.error('Error fetching iframe URL:', error); + }); + }, []); + + return ( +