diff --git a/src/actions/CT/NewOnArt/currentNewOnArtOverviewActions.js b/src/actions/CT/NewOnArt/currentNewOnArtOverviewActions.js index 29bb38b2..3b828993 100644 --- a/src/actions/CT/NewOnArt/currentNewOnArtOverviewActions.js +++ b/src/actions/CT/NewOnArt/currentNewOnArtOverviewActions.js @@ -1,7 +1,7 @@ import moment from 'moment'; import * as actionTypes from '../../types'; import { getAll } from '../../../views/Shared/Api'; -import { CACHING, PAGES } from '../../../constants'; +import { CACHING, ETL_DAY, PAGES } from '../../../constants'; export const loadCurrentNewOnArtOverview = () => async (dispatch, getState) => { const diffInMinutes = moment().diff( @@ -22,7 +22,7 @@ export const loadCurrentNewOnArtOverview = () => async (dispatch, getState) => { export const fetchCurrentNewOnArtOverview = () => async (dispatch, getState) => { dispatch({ type: actionTypes.CT_CURRENT_NEW_ON_ART_OVERVIEW_REQUEST }); - const previousMonth = moment().subtract(2, 'month').add(16, 'days'); + const previousMonth = moment().subtract(2, 'month').add(ETL_DAY, 'days'); const params = { county: getState().filters.counties, subCounty: getState().filters.subCounties, diff --git a/src/actions/CT/NewOnArt/newOnArtByAgeSexActions.js b/src/actions/CT/NewOnArt/newOnArtByAgeSexActions.js index 1b683cb5..d0778cfe 100644 --- a/src/actions/CT/NewOnArt/newOnArtByAgeSexActions.js +++ b/src/actions/CT/NewOnArt/newOnArtByAgeSexActions.js @@ -1,7 +1,7 @@ import moment from 'moment'; import * as actionTypes from '../../types'; import { getAll } from '../../../views/Shared/Api'; -import { CACHING, PAGES } from '../../../constants'; +import { CACHING, ETL_DAY, PAGES } from '../../../constants'; export const loadNewOnArtByAgeSex = () => async (dispatch, getState) => { const diffInMinutes = moment().diff( @@ -21,7 +21,7 @@ export const loadNewOnArtByAgeSex = () => async (dispatch, getState) => { export const fetchNewOnArtByAgeSex = () => async (dispatch, getState) => { dispatch({ type: actionTypes.CT_NEW_ON_ART_BY_AGE_SEX_REQUEST }); - const previousMonth = moment().subtract(2, 'month').add(16, 'days'); + const previousMonth = moment().subtract(2, 'month').add(ETL_DAY, 'days'); const params = { county: getState().filters.counties, subCounty: getState().filters.subCounties, diff --git a/src/actions/CT/ViralLoad/viralLoadCategorizationUToUActions.js b/src/actions/CT/ViralLoad/viralLoadCategorizationUToUActions.js new file mode 100644 index 00000000..b59b7129 --- /dev/null +++ b/src/actions/CT/ViralLoad/viralLoadCategorizationUToUActions.js @@ -0,0 +1,42 @@ +import moment from 'moment'; +import * as actionTypes from '../../types'; +import { getAll } from '../../../views/Shared/Api'; +import { CACHING } from '../../../constants'; + +export const loadViralLoadCategorizationUToU = (tab) => async (dispatch, getState) => { + const diffInMinutes = moment().diff( + moment(getState().viralLoadCategorizationUToU.lastFetch), + 'minutes' + ); + if (getState().ui.ctTab !== 'vl' && + tab !== 'vl') { + return; + } + else if ((diffInMinutes < CACHING.LONG) && getState().filters.filtered === false) { + return; + } else { + await dispatch(fetchViralLoadCategorizationUToU()); + } +}; + +export const fetchViralLoadCategorizationUToU = () => async (dispatch, getState) => { + dispatch({ type: actionTypes.CT_VIRAL_LOAD_CATEGORIZATION_U_TO_U_REQUEST }); + const params = { + county: getState().filters.counties, + subCounty: getState().filters.subCounties, + facility: getState().filters.facilities, + partner: getState().filters.partners, + agency: getState().filters.agencies, + pbfw: getState().filters.pbfws, + gender: getState().filters.genders, + datimAgeGroup: getState().filters.datimAgeGroups, + }; + const response = await getAll( + 'care-treatment/vlCategorizationUToU', + params + ); + dispatch({ + type: actionTypes.CT_VIRAL_LOAD_CATEGORIZATION_U_TO_U_FETCH, + payload: { filtered: getState().filters.filtered, list: response }, + }); +}; diff --git a/src/actions/CT/ViralLoad/viralLoadUptakeUToUActions.js b/src/actions/CT/ViralLoad/viralLoadUptakeUToUActions.js new file mode 100644 index 00000000..207c5936 --- /dev/null +++ b/src/actions/CT/ViralLoad/viralLoadUptakeUToUActions.js @@ -0,0 +1,37 @@ +import moment from 'moment'; +import * as actionTypes from '../../types'; +import { getAll } from '../../../views/Shared/Api'; +import { CACHING } from '../../../constants'; + +export const loadViralLoadUptakeUToU = (tab) => async (dispatch, getState) => { + const diffInMinutes = moment().diff( + moment(getState().viralLoadUptakeUToU.lastFetch), + 'minutes' + ); + if (getState().ui.ctTab !== 'vl' && + tab !== 'vl') { + return; + } + else if ((diffInMinutes < CACHING.LONG) && getState().filters.filtered === false) { + return; + } else { + await dispatch(fetchViralLoadUptakeUToU()); + } +}; + +export const fetchViralLoadUptakeUToU = () => async (dispatch, getState) => { + console.log(getState().filters); + dispatch({ type: actionTypes.CT_VIRAL_LOAD_UPTAKE_U_TO_U_REQUEST }); + const params = { + county: getState().filters.counties, + subCounty: getState().filters.subCounties, + facility: getState().filters.facilities, + partner: getState().filters.partners, + agency: getState().filters.agencies, + pbfw: getState().filters.pbfws, + gender: getState().filters.genders, + datimAgeGroup: getState().filters.datimAgeGroups, + }; + const response = await getAll('care-treatment/vlUptakeUToU', params); + dispatch({ type: actionTypes.CT_VIRAL_LOAD_UPTAKE_U_TO_U_FETCH, payload: { filtered: getState().filters.filtered, list: response }}); +}; diff --git a/src/actions/Operational&HIS/Comparison/currOnArtKHISByCountyActions.js b/src/actions/Operational&HIS/Comparison/currOnArtKHISByCountyActions.js index 7f226c59..af2852bc 100644 --- a/src/actions/Operational&HIS/Comparison/currOnArtKHISByCountyActions.js +++ b/src/actions/Operational&HIS/Comparison/currOnArtKHISByCountyActions.js @@ -1,7 +1,7 @@ import moment from 'moment'; import * as actionTypes from '../../types'; import { getAll } from '../../../views/Shared/Api'; -import { CACHING, PAGES } from '../../../constants'; +import { CACHING, ETL_DAY, PAGES } from '../../../constants'; export const loadCurrOnARTKHISByCounty = () => async (dispatch, getState) => { const diffInMinutes = moment().diff( @@ -30,8 +30,8 @@ export const fetchCurrOnARTKHISByCounty = () => async (dispatch, getState) => { project: getState().filters.projects, gender: getState().filters.genders, datimAgeGroup: getState().filters.datimAgeGroups, - year: getState().filters.fromDate ? moment(getState().filters.fromDate, "MMM YYYY").format("YYYY") : moment().subtract(2, 'month').add(16, 'days').format("YYYY"), - month: getState().filters.fromDate ? moment(getState().filters.fromDate, "MMM YYYY").format("MM") : moment().subtract(2, 'month').add(16, 'days').format("MM"), + year: getState().filters.fromDate ? moment(getState().filters.fromDate, "MMM YYYY").format("YYYY") : moment().subtract(2, 'month').add(ETL_DAY, 'days').format("YYYY"), + month: getState().filters.fromDate ? moment(getState().filters.fromDate, "MMM YYYY").format("MM") : moment().subtract(2, 'month').add(ETL_DAY, 'days').format("MM"), }; const response = await getAll('operational-his/getTxCurrKHISCounty', params); dispatch({ type: actionTypes.KHIS_CURR_ON_ART_BY_COUNTY_FETCH, payload: { filtered: getState().filters.filtered, list: response }}); diff --git a/src/actions/Operational&HIS/Comparison/newOnArtByPartnerFacilityDWHActions.js b/src/actions/Operational&HIS/Comparison/newOnArtByPartnerFacilityDWHActions.js index 1a450413..af97d9b4 100644 --- a/src/actions/Operational&HIS/Comparison/newOnArtByPartnerFacilityDWHActions.js +++ b/src/actions/Operational&HIS/Comparison/newOnArtByPartnerFacilityDWHActions.js @@ -1,7 +1,7 @@ import moment from 'moment'; import * as actionTypes from '../../types'; import { getAll } from '../../../views/Shared/Api'; -import { CACHING, PAGES } from '../../../constants'; +import { CACHING, ETL_DAY, PAGES } from '../../../constants'; export const loadNewOnARTPartnerFacilityDWH = () => async (dispatch, getState) => { const diffInMinutes = moment().diff( @@ -30,8 +30,8 @@ export const fetchNewOnARTByFacilityDWH = () => async (dispatch, getState) => { project: getState().filters.projects, gender: getState().filters.genders, datimAgeGroup: getState().filters.datimAgeGroups, - year: getState().filters.fromDate ? moment(getState().filters.fromDate, "MMM YYYY").format("YYYY") : moment().subtract(2, 'month').add(16, 'days').format("YYYY"), - month: getState().filters.fromDate ? moment(getState().filters.fromDate, "MMM YYYY").format("MM") : moment().subtract(2, 'month').add(16, 'days').format("MM"), + year: getState().filters.fromDate ? moment(getState().filters.fromDate, "MMM YYYY").format("YYYY") : moment().subtract(2, 'month').add(ETL_DAY, 'days').format("YYYY"), + month: getState().filters.fromDate ? moment(getState().filters.fromDate, "MMM YYYY").format("MM") : moment().subtract(2, 'month').add(ETL_DAY, 'days').format("MM"), }; const response = await getAll('operational-his/getTxNewBySexDwh', params); dispatch({ type: actionTypes.DWH_NEW_ON_ART_BY_PARTNER_FACILITY_FETCH, payload: { filtered: getState().filters.filtered, list: response }}); diff --git a/src/actions/RR/overallReportingRatesByFacilityNotReported.js b/src/actions/RR/overallReportingRatesByFacilityNotReported.js index 07789827..697e1c66 100644 --- a/src/actions/RR/overallReportingRatesByFacilityNotReported.js +++ b/src/actions/RR/overallReportingRatesByFacilityNotReported.js @@ -1,7 +1,7 @@ import axios from 'axios'; import moment from 'moment'; import * as actionTypes from '../types'; -import { CACHING, DWH_API_URL, PAGES } from '../../constants'; +import { CACHING, DWH_API_URL, ETL_DAY, PAGES } from '../../constants'; export const loadOverallReportingRatesByFacilityNotReported = () => async (dispatch, getState) => { const docket = getState().ui.rrTab; @@ -33,11 +33,10 @@ export const fetchOverallReportingRatesByFacilityNotReported = () => async (disp params.period = getState().filters.fromDate ? moment(getState().filters.fromDate, 'MMM YYYY') .startOf('month') - .subtract(0, 'month') .format('YYYY,M') : moment() .subtract(1, 'month') - .add(16, 'days') + .add(ETL_DAY, 'days') .format('YYYY,M'); try { const response = await axios.get(`${DWH_API_URL}manifests/overallReportingByFacility/${docket}`, { params: params }); diff --git a/src/actions/RR/overallReportingRatesByFacilityReported.js b/src/actions/RR/overallReportingRatesByFacilityReported.js index 99ab829f..95700cdd 100644 --- a/src/actions/RR/overallReportingRatesByFacilityReported.js +++ b/src/actions/RR/overallReportingRatesByFacilityReported.js @@ -1,7 +1,7 @@ import axios from 'axios'; import moment from 'moment'; import * as actionTypes from '../types'; -import { CACHING, DWH_API_URL, PAGES } from '../../constants'; +import { CACHING, DWH_API_URL, ETL_DAY, PAGES } from '../../constants'; export const loadOverallReportingRatesByFacilityReported = () => async (dispatch, getState) => { const docket = getState().ui.rrTab; @@ -29,9 +29,9 @@ export const fetchOverallReportingRatesByFacilityReported = () => async (dispatc params.period = getState().filters.fromDate ? moment(getState().filters.fromDate, 'MMM YYYY') .startOf('month') - .subtract(0, 'month') + .add(1, 'month') .format('YYYY,M') - : moment().subtract(1, 'month').add(16, 'days').format('YYYY,M'); + : moment().subtract(1, 'month').add(ETL_DAY, 'days').format('YYYY,M'); try { const response = await axios.get(`${DWH_API_URL}manifests/overallReportingByFacility/${docket}`, { params: params }); dispatch({ type: actionTypes.RR_OVERALL_REPORTING_RATES_BY_FACILITY_REPORTED_FETCH, payload: { filtered: getState().filters.filtered, docket: docket, list: response.data }}); diff --git a/src/actions/Shared/filterActions.js b/src/actions/Shared/filterActions.js index d678c836..fc95c2c4 100644 --- a/src/actions/Shared/filterActions.js +++ b/src/actions/Shared/filterActions.js @@ -25,6 +25,11 @@ export const filterByAgency = agencies => ({ payload: { agencies } }); +export const filterByPBFW = (pbfws) => ({ + type: actionTypes.FILTER_BY_PBFW, + payload: { pbfws }, +}); + export const filterByProject = projects => ({ type: actionTypes.FILTER_BY_PROJECT, payload: { projects } @@ -99,6 +104,14 @@ export const disableAgencyFilter = () => ({ type: actionTypes.DISABLE_AGENCY_FILTER }); +export const enablePBFWFilter = () => ({ + type: actionTypes.ENABLE_PBFW_FILTER, +}); + +export const disablePBFWFilter = () => ({ + type: actionTypes.DISABLE_PBFW_FILTER, +}); + export const enableFacilityFilter = () => ({ type: actionTypes.ENABLE_FACILITY_FILTER }); diff --git a/src/actions/types.js b/src/actions/types.js index 49985f72..1cf33da2 100644 --- a/src/actions/types.js +++ b/src/actions/types.js @@ -6,6 +6,7 @@ export const FILTER_BY_SUB_COUNTY = 'FILTER_BY_SUB_COUNTY' export const FILTER_BY_FACILITY = 'FILTER_BY_FACILITY' export const FILTER_BY_PARTNER = 'FILTER_BY_PARTNER' export const FILTER_BY_AGENCY = 'FILTER_BY_AGENCY' +export const FILTER_BY_PBFW = 'FILTER_BY_PBFW'; export const FILTER_BY_PROJECT = 'FILTER_BY_PROJECT' export const FILTER_BY_FROM_DATE = 'FILTER_BY_FROM_DATE' export const FILTER_BY_TO_DATE = 'FILTER_BY_TO_DATE' @@ -33,6 +34,7 @@ export const ENABLE_SUB_COUNTY_FILTER = 'ENABLE_SUB_COUNTY_FILTER' export const ENABLE_FACILITY_FILTER = 'ENABLE_FACILITY_FILTER' export const ENABLE_PARTNER_FILTER = 'ENABLE_PARTNER_FILTER' export const ENABLE_AGENCY_FILTER = 'ENABLE_AGENCY_FILTER' +export const ENABLE_PBFW_FILTER = 'ENABLE_PBFW_FILTER'; export const ENABLE_PROJECT_FILTER = 'ENABLE_PROJECT_FILTER' export const ENABLE_FROM_DATE_FILTER = 'ENABLE_FROM_DATE_FILTER' export const ENABLE_TO_DATE_FILTER = 'ENABLE_TO_DATE_FILTER' @@ -49,6 +51,7 @@ export const DISABLE_SUB_COUNTY_FILTER = 'DISABLE_SUB_COUNTY_FILTER' export const DISABLE_FACILITY_FILTER = 'DISABLE_FACILITY_FILTER' export const DISABLE_PARTNER_FILTER = 'DISABLE_PARTNER_FILTER' export const DISABLE_AGENCY_FILTER = 'DISABLE_AGENCY_FILTER' +export const DISABLE_PBFW_FILTER = 'DISABLE_PBFW_FILTER'; export const DISABLE_PROJECT_FILTER = 'DISABLE_PROJECT_FILTER' export const DISABLE_FROM_DATE_FILTER = 'DISABLE_FROM_DATE_FILTER' export const DISABLE_TO_DATE_FILTER = 'DISABLE_TO_DATE_FILTER' @@ -393,6 +396,12 @@ export const CT_VIRAL_LOAD_UPTAKE_BY_COUNTY_REQUEST_FAILED = 'CT_VIRAL_LOAD_UPTA export const CT_VIRAL_LOAD_UPTAKE_BY_PARTNER_REQUEST = 'CT_VIRAL_LOAD_UPTAKE_BY_PARTNER_REQUEST' export const CT_VIRAL_LOAD_UPTAKE_BY_PARTNER_FETCH = 'CT_VIRAL_LOAD_UPTAKE_BY_PARTNER_FETCH' export const CT_VIRAL_LOAD_UPTAKE_BY_PARTNER_REQUEST_FAILED = 'CT_VIRAL_LOAD_UPTAKE_BY_PARTNER_REQUEST_FAILED' +export const CT_VIRAL_LOAD_UPTAKE_U_TO_U_REQUEST = 'CT_VIRAL_LOAD_UPTAKE_U_TO_U_REQUEST' +export const CT_VIRAL_LOAD_UPTAKE_U_TO_U_FETCH = 'CT_VIRAL_LOAD_UPTAKE_U_TO_U_FETCH' +export const CT_VIRAL_LOAD_UPTAKE_U_TO_U_FAILED = 'CT_VIRAL_LOAD_UPTAKE_U_TO_U_FAILED' +export const CT_VIRAL_LOAD_CATEGORIZATION_U_TO_U_REQUEST = 'CT_VIRAL_LOAD_CATEGORIZATION_U_TO_U_REQUEST' +export const CT_VIRAL_LOAD_CATEGORIZATION_U_TO_U_FETCH = 'CT_VIRAL_LOAD_CATEGORIZATION_U_TO_U_FETCH' +export const CT_VIRAL_LOAD_CATEGORIZATION_U_TO_U_FAILED = 'CT_VIRAL_LOAD_CATEGORIZATION_U_TO_U_FAILED' export const CT_VIRAL_LOAD_OUTCOMES_BY_SEX_REQUEST = 'CT_VIRAL_LOAD_OUTCOMES_BY_SEX_REQUEST' export const CT_VIRAL_LOAD_OUTCOMES_BY_SEX_FETCH = 'CT_VIRAL_LOAD_OUTCOMES_BY_SEX_FETCH' diff --git a/src/constants.js b/src/constants.js index 66cdb8af..49295a42 100644 --- a/src/constants.js +++ b/src/constants.js @@ -81,3 +81,5 @@ export const GA_TRACKING_ID = 'UA-137589703-2'; export const DWH_API_URL = process.env.REACT_APP_ENDPOINT; export const HRH_API_URL = 'https://hrh.datacompanion.org'; + +export const ETL_DAY = 10; diff --git a/src/reducers/CT/ViralLoad/viralLoadCategorizationUToU.js b/src/reducers/CT/ViralLoad/viralLoadCategorizationUToU.js new file mode 100644 index 00000000..95e134eb --- /dev/null +++ b/src/reducers/CT/ViralLoad/viralLoadCategorizationUToU.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.CT_VIRAL_LOAD_CATEGORIZATION_U_TO_U_REQUEST: + newState.loading = true; + return newState; + case actionTypes.CT_VIRAL_LOAD_CATEGORIZATION_U_TO_U_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.CT_VIRAL_LOAD_CATEGORIZATION_U_TO_U_FAILED: + newState.loading = false; + return newState; + default: + return state + } +} diff --git a/src/reducers/CT/ViralLoad/viralLoadUptakeUToU.js b/src/reducers/CT/ViralLoad/viralLoadUptakeUToU.js new file mode 100644 index 00000000..e01956f5 --- /dev/null +++ b/src/reducers/CT/ViralLoad/viralLoadUptakeUToU.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.CT_VIRAL_LOAD_UPTAKE_U_TO_U_REQUEST: + newState.loading = true; + return newState; + case actionTypes.CT_VIRAL_LOAD_UPTAKE_U_TO_U_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.CT_VIRAL_LOAD_UPTAKE_U_TO_U_FAILED: + newState.loading = false; + return newState; + default: + return state + } +} diff --git a/src/reducers/Shared/filters.js b/src/reducers/Shared/filters.js index 05fc26f6..43c5cab0 100644 --- a/src/reducers/Shared/filters.js +++ b/src/reducers/Shared/filters.js @@ -8,6 +8,7 @@ const initialState = { facilities: [], partners: [], agencies: [], + pbfws: [], projects: [], genders: [], datimAgeGroups: [], @@ -23,6 +24,7 @@ const initialState = { facilityFilterEnabled: true, partnerFilterEnabled: true, agencyFilterEnabled: true, + pbfwFilterEnabled: false, projectFilterEnabled: false, fromDateFilterEnabled: false, toDateFilterEnabled: false, @@ -45,6 +47,7 @@ export default (state = initialState, action) => { state.facilities.length > 0 || state.partners.length > 0 || state.agencies.length > 0 || + state.pbfws.length > 0 || state.projects.length > 0 || state.genders.length > 0 || state.datimAgeGroups.length > 0 || @@ -66,6 +69,7 @@ export default (state = initialState, action) => { state.facilities.length > 0 || state.partners.length > 0 || state.agencies.length > 0 || + state.pbfws.length > 0 || state.projects.length > 0 || state.genders.length > 0 || state.datimAgeGroups.length > 0 || @@ -87,6 +91,7 @@ export default (state = initialState, action) => { action.payload.facilities.length > 0 || state.partners.length > 0 || state.agencies.length > 0 || + state.pbfws.length > 0 || state.projects.length > 0 || state.genders.length > 0 || state.datimAgeGroups.length > 0 || @@ -102,11 +107,13 @@ export default (state = initialState, action) => { facilities: action.payload.facilities, }; case actionTypes.FILTER_BY_PARTNER: - filtered = state.counties.length > 0 || + filtered = + state.counties.length > 0 || state.subCounties.length > 0 || state.facilities.length > 0 || action.payload.partners.length > 0 || state.agencies.length > 0 || + state.pbfws.length > 0 || state.projects.length > 0 || state.genders.length > 0 || state.datimAgeGroups.length > 0 || @@ -128,6 +135,7 @@ export default (state = initialState, action) => { state.facilities.length > 0 || state.partners.length > 0 || action.payload.agencies.length > 0 || + state.pbfws.length > 0 || state.projects.length > 0 || state.genders.length > 0 || state.datimAgeGroups.length > 0 || @@ -142,6 +150,24 @@ export default (state = initialState, action) => { filtered, agencies: action.payload.agencies, }; + case actionTypes.FILTER_BY_PBFW: + filtered = + state.counties.length > 0 || + state.subCounties.length > 0 || + state.facilities.length > 0 || + state.partners.length > 0 || + state.agencies.length > 0 || + action.payload.pbfws.length > 0 || + state.pbfws.length > 0 || + state.projects.length > 0 || + state.genders.length > 0 || + state.datimAgeGroups.length > 0 || + false; + return { + ...state, + filtered, + pbfws: action.payload.pbfws, + }; case actionTypes.FILTER_BY_PROJECT: filtered = state.counties.length > 0 || @@ -212,6 +238,7 @@ export default (state = initialState, action) => { state.facilities.length > 0 || state.partners.length > 0 || state.agencies.length > 0 || + state.pbfws.length > 0 || state.projects.length > 0 || action.payload.genders.length > 0 || state.datimAgeGroups.length > 0 || @@ -254,6 +281,7 @@ export default (state = initialState, action) => { state.facilities.length > 0 || state.partners.length > 0 || state.agencies.length > 0 || + state.pbfws.length > 0 || state.projects.length > 0 || state.genders.length > 0 || action.payload.datimAgeGroups.length > 0 || @@ -428,6 +456,17 @@ export default (state = initialState, action) => { agencyFilterEnabled: false, agencies: [], }; + case actionTypes.ENABLE_PBFW_FILTER: + return { + ...state, + pbfwFilterEnabled: true, + }; + case actionTypes.DISABLE_PBFW_FILTER: + return { + ...state, + pbfwFilterEnabled: false, + pbfws: [], + }; case actionTypes.ENABLE_PROJECT_FILTER: return { ...state, diff --git a/src/reducers/index.js b/src/reducers/index.js index e0f06ea0..a5652607 100644 --- a/src/reducers/index.js +++ b/src/reducers/index.js @@ -112,6 +112,8 @@ import viralLoad6MonthSuppressionByYearOfArtStart from './CT/ViralLoad/viralLoad import viralLoad12MonthSuppressionByYearOfArtStart from './CT/ViralLoad/viralLoad12MonthSuppressionByYearOfArtStart'; import viralLoad24MonthSuppressionByYearOfArtStart from './CT/ViralLoad/viralLoad24MonthSuppressionByYearOfArtStart'; import viralLoadOverallUptakeSuppressionBySexVlDone from './CT/ViralLoad/viralLoadOverallUptakeSuppressionBySexVlDone'; +import viralLoadUptakeUToU from './CT/ViralLoad/viralLoadUptakeUToU'; +import viralLoadCategorizationUToU from './CT/ViralLoad/viralLoadCategorizationUToU'; import practitionersCountByCountyQualification from './HRH/practitionersCountByCountyQualification'; import populationByCounty from './HRH/populationByCounty'; @@ -423,6 +425,8 @@ export default combineReducers({ viralLoadOverallUptakeGt1000CopiesReceivedFollowTests, viralLoadOverallUptakeGt1000CopiesReceivedFollowTestsAll, viralLoadOverallNumberTestsGt1000CopiesSecondlineRegiment, + viralLoadUptakeUToU, + viralLoadCategorizationUToU, treatmentOutcomesOverallLast12m, treatmentOutcomesBySex, diff --git a/src/routes.js b/src/routes.js index 533d25f5..c14d12b0 100644 --- a/src/routes.js +++ b/src/routes.js @@ -102,6 +102,12 @@ const routes = [ component: withTracker(Profile), private: true, }, + { + path: '/highlight/monthly', + exact: true, + name: 'Highlight of the month', + private: false, + }, ]; export default routes; diff --git a/src/selectors/CT/ArtVerification/pendingSurveys.js b/src/selectors/CT/ArtVerification/pendingSurveys.js index 0c21b2f5..126e9cec 100644 --- a/src/selectors/CT/ArtVerification/pendingSurveys.js +++ b/src/selectors/CT/ArtVerification/pendingSurveys.js @@ -18,7 +18,7 @@ const listUnfilteredVerified = (state) => state.currentOnArtVerifiedByCounty.listUnfiltered; const listFilteredVerified = (state) => state.currentOnArtVerifiedByCounty.listFiltered; - + const listUnfilteredVerifiedPartner = (state) => state.currentOnArtVerifiedByPartner.listUnfiltered; const listFilteredVerifiedPartner = (state) => @@ -129,7 +129,7 @@ export const getArtPendingUnverifiedByPartner = createSelector( return { partners: newList.map((e) => e.partners), - pending: newList.map((e) => e.Pending), + pending: newList.map((e) => e.Pending < 0 ? 0 : e.Pending), }; } ); @@ -152,7 +152,7 @@ export const getArtVerificationSubmissionByPartner = createSelector( partners = list.map((p) => p.SDIP); pending = list.map((p) => p.Pendingsurveys); - unverified = list.map((p) => p.Unverified); + unverified = list.map((p) => (p.TxCurr - p.NupiVerified)); received = list.map((p) => p.SurveysReceived); let submitted = _.sum(received); @@ -246,7 +246,7 @@ export const getArtPendingUnverifiedByCounty = createSelector( return { counties: newList.map((e) => e.county), - pending: newList.map((e) => e.Pending), + pending: newList.map((e) => e.Pending < 0 ? 0 : e.Pending), }; } ); @@ -272,9 +272,9 @@ export const getArtVerificationSubmissionByCounty = createSelector( unverified = list.map((p) => { return { percent: Number( - (p.Unverified * 100) / p.SurveysReceived + p.Unverified + ((p.TxCurr - p.NupiVerified) * 100) / p.SurveysReceived + p.Unverified ), - y: p.Unverified, + y: (p.TxCurr - p.NupiVerified), }; }); received = list.map((p) => { diff --git a/src/selectors/CT/Dsd/dsdAppointmentDurationByAge.js b/src/selectors/CT/Dsd/dsdAppointmentDurationByAge.js index b52abb37..15eb4643 100644 --- a/src/selectors/CT/Dsd/dsdAppointmentDurationByAge.js +++ b/src/selectors/CT/Dsd/dsdAppointmentDurationByAge.js @@ -44,14 +44,22 @@ export const getAppointmentDurationByAge = createSelector( let totalNonMMD = 0; if (arr[key].length > 1) { arr[key].forEach(z => { - const catValues = list.filter(obj => obj.AgeGroup.trim() === z.trim()); + const catValues = list.filter(obj =>{ + if(obj.AgeGroup && z){ + return obj.AgeGroup?.trim() === z?.trim() + } + }); if (catValues.length > 0) { - totalMMD = totalMMD + catValues[0].MMD; - totalNonMMD = totalNonMMD + catValues[0].NonMMD; + totalMMD += catValues[0].MMD; + totalNonMMD += catValues[0].NonMMD; } }); } else { - const catValues = list.filter(obj => obj.AgeGroup.trim() === arr[key][0]); + const catValues = list.filter(obj =>{ + if (obj.AgeGroup) { + return obj.AgeGroup?.trim() === arr[key][0] + } + }); if (catValues.length > 0) { totalMMD = totalMMD + catValues[0].MMD; totalNonMMD = totalNonMMD + catValues[0].NonMMD; diff --git a/src/selectors/CT/OTZ/otzEnrollmentAmongAlHivByAge.js b/src/selectors/CT/OTZ/otzEnrollmentAmongAlHivByAge.js index 2ef72b1c..cb52ddbc 100644 --- a/src/selectors/CT/OTZ/otzEnrollmentAmongAlHivByAge.js +++ b/src/selectors/CT/OTZ/otzEnrollmentAmongAlHivByAge.js @@ -12,6 +12,7 @@ export const getOtzEnrollmentAmongAlHivOnArtByAge = createSelector( const list = filtered ? listFiltered : listUnfiltered; const listAgeGroup = filtered ? listFilteredByAgeGroup : listUnfilteredByAgeGroup; + const arrayVal = []; for (const listAgeGroupElement of listAgeGroup) { const listItem = list.filter(obj => obj.ageGroup === listAgeGroupElement.ageGroup); diff --git a/src/selectors/CT/TreatmentOutcomes/treatmentOutcomesByAge.js b/src/selectors/CT/TreatmentOutcomes/treatmentOutcomesByAge.js index 9eeabbf0..2e8f9811 100644 --- a/src/selectors/CT/TreatmentOutcomes/treatmentOutcomesByAge.js +++ b/src/selectors/CT/TreatmentOutcomes/treatmentOutcomesByAge.js @@ -8,7 +8,7 @@ export const getTreatmentOutcomesByAge = createSelector( [listUnfiltered, listFiltered, filtered], (listUnfiltered, listFiltered, filtered) => { const list = filtered ? listFiltered : listUnfiltered; - const treatmentOutcomesCategories = ['Active', 'Dead', 'LTFU', 'Stopped', 'TransferOut']; + const treatmentOutcomesCategories = ['ACTIVE', 'DEAD', 'LOSS TO FOLLOW UP', 'STOPPED', 'TRANSFERRED OUT']; const ageCategories = [ ' Under 1', '01 to 04', @@ -40,15 +40,15 @@ export const getTreatmentOutcomesByAge = createSelector( if(treatmentOutcomesIndex === -1 || ageIndex === -1 ) { // unsupported continue; } - data[treatmentOutcomesIndex][ageIndex] = data[treatmentOutcomesIndex][ageIndex] + parseInt(list[i].totalOutcomes); + data[treatmentOutcomesIndex][ageIndex] += parseInt(list[i].totalOutcomes); } // combine and cleanup for(let i = 0; i < data.length; i++) { - data[i][1] = data[i][1] + data[i][0]; - data[i][7] = data[i][7] + data[i][6]; - data[i][9] = data[i][9] + data[i][8]; - data[i][11] = data[i][11] + data[i][10]; - data[i][13] = data[i][13] + data[i][12]; + data[i][1] += data[i][0]; + data[i][7] += data[i][6]; + data[i][9] += data[i][8]; + data[i][11] += data[i][10]; + data[i][13] += data[i][12]; data[i].splice(0, 1); data[i].splice(6-1, 1); data[i].splice(8-2, 1); diff --git a/src/selectors/CT/TreatmentOutcomes/treatmentOutcomesByFacility.js b/src/selectors/CT/TreatmentOutcomes/treatmentOutcomesByFacility.js index b5b5331c..df535bc9 100644 --- a/src/selectors/CT/TreatmentOutcomes/treatmentOutcomesByFacility.js +++ b/src/selectors/CT/TreatmentOutcomes/treatmentOutcomesByFacility.js @@ -19,15 +19,15 @@ export const getTreatmentOutcomesByFacility = createSelector( partner: list[i].partner, }; } - if (list[i].artOutcome === "Active") { + if (list[i].artOutcome === "ACTIVE") { outcomes[list[i].facility].active = parseInt(list[i].totalOutcomes, 10); - } else if (list[i].artOutcome === "Dead") { + } else if (list[i].artOutcome === "DEAD") { outcomes[list[i].facility].dead = parseInt(list[i].totalOutcomes, 10); - } else if (list[i].artOutcome === "LTFU") { + } else if (list[i].artOutcome === 'LOSS TO FOLLOW UP') { outcomes[list[i].facility].ltfu = parseInt(list[i].totalOutcomes, 10); - } else if (list[i].artOutcome === "Stopped") { + } else if (list[i].artOutcome === "STOPPED") { outcomes[list[i].facility].stopped = parseInt(list[i].totalOutcomes, 10); - } else if (list[i].artOutcome === "TransferOut") { + } else if (list[i].artOutcome === 'TRANSFERRED OUT') { outcomes[list[i].facility].to = parseInt(list[i].totalOutcomes, 10); } } @@ -51,9 +51,10 @@ export const getTreatmentOutcomesByCounty = createSelector( [listUnfiltered, listFiltered, filtered], (listUnfiltered, listFiltered, filtered) => { const list = filtered ? listFiltered : listUnfiltered; - const treatmentOutcomesCategories = ['Active', 'Dead', 'LTFU', 'Stopped', 'TransferOut']; + const treatmentOutcomesCategories = + ['ACTIVE', 'DEAD', 'LOSS TO FOLLOW UP', 'STOPPED', 'TRANSFERRED OUT']; const countyCategories = _.chain(list) - .filter(l => l.county && l.artOutcome === 'Active') + .filter(l => l.county && l.artOutcome === 'ACTIVE') .map(l => ({ ...l, county: l.county.toUpperCase() })) .groupBy('county') .map((objs, key) => ({ @@ -81,7 +82,7 @@ export const getTreatmentOutcomesByCounty = createSelector( if(outcomeIndex === -1 || countyIndex === -1 ) { // unsupported continue; } - data[outcomeIndex][countyIndex] = data[outcomeIndex][countyIndex] + parseInt(list[i].totalOutcomes); + data[outcomeIndex][countyIndex] += parseInt(list[i].totalOutcomes); } return { data, countyCategories }; } @@ -91,9 +92,10 @@ export const getTreatmentOutcomesByPartner = createSelector( [listUnfiltered, listFiltered, filtered], (listUnfiltered, listFiltered, filtered) => { const list = filtered ? listFiltered : listUnfiltered; - const treatmentOutcomesCategories = ['Active', 'Dead', 'LTFU', 'Stopped', 'TransferOut']; + const treatmentOutcomesCategories = + ['ACTIVE', 'DEAD', 'LOSS TO FOLLOW UP', 'STOPPED', 'TRANSFERRED OUT'] const partnerCategories = _.chain(list) - .filter(l => l.partner && l.artOutcome === 'Active') + .filter(l => l.partner && l.artOutcome === 'ACTIVE') .map(l => ({ ...l, partner: l.partner.toUpperCase() })) .groupBy('partner') .map((objs, key) => ({ @@ -120,8 +122,8 @@ export const getTreatmentOutcomesByPartner = createSelector( if(outcomeIndex === -1 || partnerIndex === -1 ) { // unsupported continue; } - data[outcomeIndex][partnerIndex] = data[outcomeIndex][partnerIndex] + parseInt(list[i].totalOutcomes); + data[outcomeIndex][partnerIndex] += parseInt(list[i].totalOutcomes); } return { data, partnerCategories }; } -); \ No newline at end of file +); diff --git a/src/selectors/CT/TreatmentOutcomes/treatmentOutcomesBySex.js b/src/selectors/CT/TreatmentOutcomes/treatmentOutcomesBySex.js index 657a4ad3..817dd796 100644 --- a/src/selectors/CT/TreatmentOutcomes/treatmentOutcomesBySex.js +++ b/src/selectors/CT/TreatmentOutcomes/treatmentOutcomesBySex.js @@ -10,7 +10,7 @@ export const getActive = createSelector( (listUnfiltered, listFiltered, filtered) => { const list = filtered ? listFiltered : listUnfiltered; return _.chain(list) - .filter(list => list.artOutcome === "Active") + .filter(list => list.artOutcome === "ACTIVE") .sumBy("totalOutcomes") .value(); } @@ -21,7 +21,7 @@ export const getDead = createSelector( (listUnfiltered, listFiltered, filtered) => { const list = filtered ? listFiltered : listUnfiltered; return _.chain(list) - .filter(list => list.artOutcome === "Dead") + .filter(list => list.artOutcome === "DEAD") .sumBy("totalOutcomes") .value(); } @@ -32,7 +32,7 @@ export const getLtfu = createSelector( (listUnfiltered, listFiltered, filtered) => { const list = filtered ? listFiltered : listUnfiltered; return _.chain(list) - .filter((list) => list.artOutcome === 'Loss To Follow Up') + .filter((list) => list.artOutcome === 'LOSS TO FOLLOW UP') .sumBy('totalOutcomes') .value(); } @@ -43,7 +43,7 @@ export const getStopped = createSelector( (listUnfiltered, listFiltered, filtered) => { const list = filtered ? listFiltered : listUnfiltered; return _.chain(list) - .filter(list => list.artOutcome === "Stopped") + .filter(list => list.artOutcome === "STOPPED") .sumBy("totalOutcomes") .value(); } @@ -54,7 +54,7 @@ export const getTransferOut = createSelector( (listUnfiltered, listFiltered, filtered) => { const list = filtered ? listFiltered : listUnfiltered; return _.chain(list) - .filter((list) => list.artOutcome === 'Transferred Out') + .filter((list) => list.artOutcome === 'TRANSFERRED OUT') .sumBy('totalOutcomes') .value(); } @@ -65,7 +65,7 @@ export const getUndocumentedLtfu = createSelector( (listUnfiltered, listFiltered, filtered) => { const list = filtered ? listFiltered : listUnfiltered; return _.chain(list) - .filter((list) => list.artOutcome === 'Undocumented Loss') + .filter((list) => list.artOutcome === 'UNDOCUMENTED LOSS') .sumBy('totalOutcomes') .value(); } @@ -76,11 +76,11 @@ export const getActiveBySex = createSelector( (listUnfiltered, listFiltered, filtered) => { const list = filtered ? listFiltered : listUnfiltered; const male = _.chain(list) - .filter(list => list.artOutcome === "Active" && list.gender === "Male") + .filter(list => list.artOutcome === "ACTIVE" && list.gender === "Male") .sumBy("totalOutcomes") .value(); const female = _.chain(list) - .filter(list => list.artOutcome === "Active" && list.gender === "Female") + .filter(list => list.artOutcome === "ACTIVE" && list.gender === "Female") .sumBy("totalOutcomes") .value(); return [male, female]; @@ -92,11 +92,11 @@ export const getDeadBySex = createSelector( (listUnfiltered, listFiltered, filtered) => { const list = filtered ? listFiltered : listUnfiltered; const male = _.chain(list) - .filter(list => list.artOutcome === "Dead" && list.gender === "Male") + .filter(list => list.artOutcome === "DEAD" && list.gender === "Male") .sumBy("totalOutcomes") .value(); const female = _.chain(list) - .filter(list => list.artOutcome === "Dead" && list.gender === "Female") + .filter(list => list.artOutcome === "DEAD" && list.gender === "Female") .sumBy("totalOutcomes") .value(); return [male, female]; @@ -110,7 +110,7 @@ export const getLtfuBySex = createSelector( const male = _.chain(list) .filter( (list) => - list.artOutcome === 'Loss To Follow Up' && + list.artOutcome === 'LOSS TO FOLLOW UP' && list.gender === 'Male' ) .sumBy('totalOutcomes') @@ -118,7 +118,7 @@ export const getLtfuBySex = createSelector( const female = _.chain(list) .filter( (list) => - list.artOutcome === 'Loss To Follow Up' && + list.artOutcome === 'LOSS TO FOLLOW UP' && list.gender === 'Female' ) .sumBy('totalOutcomes') @@ -132,11 +132,11 @@ export const getStoppedBySex = createSelector( (listUnfiltered, listFiltered, filtered) => { const list = filtered ? listFiltered : listUnfiltered; const male = _.chain(list) - .filter(list => list.artOutcome === "Stopped" && list.gender === "Male") + .filter(list => list.artOutcome === "STOPPED" && list.gender === "Male") .sumBy("totalOutcomes") .value(); const female = _.chain(list) - .filter(list => list.artOutcome === "Stopped" && list.gender === "Female") + .filter(list => list.artOutcome === "STOPPED" && list.gender === "Female") .sumBy("totalOutcomes") .value(); return [male, female]; @@ -150,7 +150,7 @@ export const getTransferOutBySex = createSelector( const male = _.chain(list) .filter( (list) => - list.artOutcome === 'Transferred Out' && + list.artOutcome === 'TRANSFERRED OUT' && list.gender === 'Male' ) .sumBy('totalOutcomes') @@ -158,7 +158,7 @@ export const getTransferOutBySex = createSelector( const female = _.chain(list) .filter( (list) => - list.artOutcome === 'Transferred Out' && + list.artOutcome === 'TRANSFERRED OUT' && list.gender === 'Female' ) .sumBy('totalOutcomes') @@ -174,7 +174,7 @@ export const getUndocumentedLtfuBySex = createSelector( const male = _.chain(list) .filter( (list) => - list.artOutcome === 'Undocumented Loss' && + list.artOutcome === 'UNDOCUMENTED LOSS' && list.gender === 'Male' ) .sumBy('totalOutcomes') @@ -182,7 +182,7 @@ export const getUndocumentedLtfuBySex = createSelector( const female = _.chain(list) .filter( (list) => - list.artOutcome === 'Undocumented Loss' && + list.artOutcome === 'UNDOCUMENTED LOSS' && list.gender === 'Female' ) .sumBy('totalOutcomes') diff --git a/src/selectors/CT/TreatmentOutcomes/treatmentOutcomesByYear.js b/src/selectors/CT/TreatmentOutcomes/treatmentOutcomesByYear.js index fafd5082..d1f67ab7 100644 --- a/src/selectors/CT/TreatmentOutcomes/treatmentOutcomesByYear.js +++ b/src/selectors/CT/TreatmentOutcomes/treatmentOutcomesByYear.js @@ -9,7 +9,8 @@ export const getTreatmentOutcomesByYear = createSelector( [listUnfiltered, listFiltered, filtered], (listUnfiltered, listFiltered, filtered) => { const list = filtered ? listFiltered : listUnfiltered; - const treatmentOutcomesCategories = ['Active', 'Dead', 'LTFU', 'Stopped', 'TransferOut']; + const treatmentOutcomesCategories = + ['ACTIVE', 'DEAD', 'LOSS TO FOLLOW UP', 'STOPPED', 'TRANSFERRED OUT']; const yearCategories = _.chain(list).map(l => parseInt(l.year)).filter(l => l >= 2011).uniq().sort().value(); let data = []; for(let i = 0; i < treatmentOutcomesCategories.length; i++) { @@ -24,7 +25,7 @@ export const getTreatmentOutcomesByYear = createSelector( if(treatmentOutcomesIndex === -1 || yearIndex === -1 ) { // unsupported continue; } - data[treatmentOutcomesIndex][yearIndex] = data[treatmentOutcomesIndex][yearIndex] + parseInt(list[i].totalOutcomes); + data[treatmentOutcomesIndex][yearIndex] += parseInt(list[i].totalOutcomes); } return { yearCategories, data }; } diff --git a/src/selectors/CT/TreatmentOutcomes/treatmentOutcomesOverallLast12m.js b/src/selectors/CT/TreatmentOutcomes/treatmentOutcomesOverallLast12m.js index df817402..3f9b0dd0 100644 --- a/src/selectors/CT/TreatmentOutcomes/treatmentOutcomesOverallLast12m.js +++ b/src/selectors/CT/TreatmentOutcomes/treatmentOutcomesOverallLast12m.js @@ -20,7 +20,7 @@ export const getActive = createSelector( (listUnfiltered, listFiltered, filtered) => { const list = filtered ? listFiltered : listUnfiltered; return _.chain(list) - .filter(list => list.artOutcome === "Active") + .filter(list => list.artOutcome === "ACTIVE") .sumBy("totalOutcomes") .value(); } @@ -31,7 +31,7 @@ export const getDead = createSelector( (listUnfiltered, listFiltered, filtered) => { const list = filtered ? listFiltered : listUnfiltered; return _.chain(list) - .filter(list => list.artOutcome === "Dead") + .filter(list => list.artOutcome === "DEAD") .sumBy("totalOutcomes") .value(); } @@ -42,7 +42,7 @@ export const getLtfu = createSelector( (listUnfiltered, listFiltered, filtered) => { const list = filtered ? listFiltered : listUnfiltered; return _.chain(list) - .filter((list) => list.artOutcome === 'Loss To Follow Up') + .filter((list) => list.artOutcome === 'LOSS TO FOLLOW UP') .sumBy('totalOutcomes') .value(); } @@ -53,7 +53,7 @@ export const getStopped = createSelector( (listUnfiltered, listFiltered, filtered) => { const list = filtered ? listFiltered : listUnfiltered; return _.chain(list) - .filter(list => list.artOutcome === "Stopped") + .filter(list => list.artOutcome === "STOPPED") .sumBy("totalOutcomes") .value(); } @@ -64,7 +64,7 @@ export const getTransferOut = createSelector( (listUnfiltered, listFiltered, filtered) => { const list = filtered ? listFiltered : listUnfiltered; return _.chain(list) - .filter((list) => list.artOutcome === 'Transferred Out') + .filter((list) => list.artOutcome === 'TRANSFERRED OUT') .sumBy('totalOutcomes') .value(); } @@ -75,7 +75,7 @@ export const getUndocumentedLtfu = createSelector( (listUnfiltered, listFiltered, filtered) => { const list = filtered ? listFiltered : listUnfiltered; return _.chain(list) - .filter((list) => list.artOutcome === 'Undocumented Loss') + .filter((list) => list.artOutcome === 'UNDOCUMENTED LOSS') .sumBy('totalOutcomes') .value(); } diff --git a/src/selectors/CT/ViralLoad/viralLoadUToU.js b/src/selectors/CT/ViralLoad/viralLoadUToU.js new file mode 100644 index 00000000..63bf60c6 --- /dev/null +++ b/src/selectors/CT/ViralLoad/viralLoadUToU.js @@ -0,0 +1,29 @@ +import { createSelector } from 'reselect'; + +const listUnfiltered = state => state.viralLoadUptakeUToU.listUnfiltered; +const listFiltered = (state) => state.viralLoadUptakeUToU.listFiltered; +const listCatUnfiltered = (state) => state.viralLoadCategorizationUToU.listUnfiltered; +const listCatFiltered = (state) => state.viralLoadCategorizationUToU.listFiltered; +const filtered = state => state.filters.filtered; + +export const getViralLoadUptakeUToU = createSelector( + [listUnfiltered, listFiltered, filtered], + (listUnfiltered, listFiltered, filtered) => { + return filtered ? listFiltered : listUnfiltered; + } +); + +export const getViralLoadCategorizationUToU = createSelector( + [listCatUnfiltered, listCatFiltered, filtered], + (listUnfiltered, listFiltered, filtered) => { + const list = filtered ? listFiltered : listUnfiltered; + + return [ + list.find((cat) => cat.ValidVLResultCategory === '<50')?.data, + list.find((cat) => cat.ValidVLResultCategory === '51-199')?.data, + list.find((cat) => cat.ValidVLResultCategory === '200-999')?.data, + list.find((cat) => cat.ValidVLResultCategory === '>1000')?.data, + ]; + + } +); diff --git a/src/selectors/HTS/Prep/PrepTrendsSelector.js b/src/selectors/HTS/Prep/PrepTrendsSelector.js index e3b13187..8ba9e6d6 100644 --- a/src/selectors/HTS/Prep/PrepTrendsSelector.js +++ b/src/selectors/HTS/Prep/PrepTrendsSelector.js @@ -219,11 +219,11 @@ export const getPrepEligibleVnewTrend = createSelector( parseFloat( ( ((listnew.find( - (x) => x.year == year && x.month == month + (x) => x.year === year && x.month === month )?.StartedPrep ?? 0) * 100) / (list.find( - (x) => x.year == year && x.month == month + (x) => x.year === year && x.month === month )?.EligiblePrep ?? 0) ).toFixed(1) ) diff --git a/src/views/CT/AdverseEvents/AdverseEvents.js b/src/views/CT/AdverseEvents/AdverseEvents.js index 9753da47..35ce8926 100644 --- a/src/views/CT/AdverseEvents/AdverseEvents.js +++ b/src/views/CT/AdverseEvents/AdverseEvents.js @@ -3,7 +3,7 @@ import classnames from 'classnames'; import Loadable from 'react-loadable'; import VisibilitySensor from 'react-visibility-sensor'; import { useDispatch, useSelector } from 'react-redux'; -import { Nav, NavItem, NavLink, TabContent, TabPane, Col, Row } from 'reactstrap'; +import { Nav, NavItem, NavLink, TabContent, TabPane, Col, Row, CardHeader, CardBody, Card } from 'reactstrap'; import { enableStickyFilter, disableStickyFilter } from "../../../actions/Shared/uiActions"; import { LOADING_DELAY } from "../../../constants"; import Loading from './../../Shared/Loading'; @@ -97,6 +97,25 @@ const AdverseEvents = () => { + + + Indicator Definition + + +
    +
  • + { + 'ADULTS 15+ ON ART AND DEVELOPED AEs => This is the total no. of distinct patients (15+) who are currently on ART and ever developed AEs.' + } +
  • +
  • + { + 'NUMBER OF AEs REPORTED CASES IN ADULTS 15+ => This is the total no. of AE cases ever reported among clients who are currently on ART (15+).' + } +
  • +
+
+
diff --git a/src/views/CT/ArtVerification/ArtVerificationSurveySubmission.js b/src/views/CT/ArtVerification/ArtVerificationSurveySubmission.js index 4de6e39e..f48a0e18 100644 --- a/src/views/CT/ArtVerification/ArtVerificationSurveySubmission.js +++ b/src/views/CT/ArtVerification/ArtVerificationSurveySubmission.js @@ -47,12 +47,12 @@ const ArtVerificationSurveySubmission = () => { data: [ { name: 'SUBMITTED SURVEY', - y: status.submitted, + y: notVerified - (status.submitted - notVerified), color: '#01058A', }, { name: 'NOT SUBMITTED SURVEY', - y: notVerified - status.submitted, + y: status.submitted - notVerified, sliced: true, selected: true, color: '#8E2C16', diff --git a/src/views/CT/ArtVerification/ArtVerificationSurveySubmissionByCounty.js b/src/views/CT/ArtVerification/ArtVerificationSurveySubmissionByCounty.js index 50e4005c..a1f3102a 100644 --- a/src/views/CT/ArtVerification/ArtVerificationSurveySubmissionByCounty.js +++ b/src/views/CT/ArtVerification/ArtVerificationSurveySubmissionByCounty.js @@ -66,7 +66,7 @@ const ArtVerificationSurveySubmissionByCounty = () => {
- SURVEYS SUMBITTED FOR UNVERIFIED PATIENTS BY COUNTY + SURVEYS SUBMITTED FOR UNVERIFIED PATIENTS BY COUNTY {countyData.loadingC ? ( diff --git a/src/views/CT/ArtVerification/ArtVerificationSurveySubmissionByPartner.js b/src/views/CT/ArtVerification/ArtVerificationSurveySubmissionByPartner.js index ea686ab8..a1fb8789 100644 --- a/src/views/CT/ArtVerification/ArtVerificationSurveySubmissionByPartner.js +++ b/src/views/CT/ArtVerification/ArtVerificationSurveySubmissionByPartner.js @@ -66,7 +66,7 @@ const ArtVerificationSurveySubmissionByPartner = () => {
- SURVEYS SUMBITTED FOR UNVERIFIED PATIENTS BY PARTNER + SURVEYS SUBMITTED FOR UNVERIFIED PATIENTS BY PARTNER {partnerData.loadingP ? ( diff --git a/src/views/CT/CT.js b/src/views/CT/CT.js index c78def44..1c28d7f7 100644 --- a/src/views/CT/CT.js +++ b/src/views/CT/CT.js @@ -19,7 +19,9 @@ import { disablePopulationTypeFilter, enableAgencyFilter, enableDatimAgePopulationFilter, - disableDatimAgePopulationFilter + disableDatimAgePopulationFilter, + enablePBFWFilter, + disablePBFWFilter } from '../../actions/Shared/filterActions'; import { loadLinkagePositiveTrends } from '../../actions/HTS/Linkage/linkagePositiveTrendsActions'; @@ -357,6 +359,8 @@ import { loadAppointmentKeepingWaterfall } from '../../actions/CT/TreatmentOutco import { loadQuaterlyIIT } from '../../actions/CT/TreatmentOutcomes/quaterlyIITActions'; 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'; const NewOnArt = Loadable({ loader: () => import('./NewOnArt/NewOnArt'), loading: Loading, delay: LOADING_DELAY }); const CurrentOnArt = Loadable({ @@ -398,6 +402,7 @@ const CT = () => { const agencies = useSelector(state => state.filters.agencies); const projects = useSelector(state => state.filters.projects); const genders = useSelector(state => state.filters.genders); + const pbfws = useSelector((state) => state.filters.pbfws); const datimAgeGroups = useSelector(state => state.filters.datimAgeGroups); const datimAgePopulation = useSelector(state => state.filters.datimAgePopulation); const latestPregnancies = useSelector(state => state.filters.latestPregnancies); @@ -406,7 +411,7 @@ const CT = () => { const toDate = useSelector(state => state.filters.toDate); const DEFAULT_ACTIVE_TAB = useSelector(state => state.ui.ctTab); - const { active_tab } = useParams(); + const { active_tab, mini_tab } = useParams(); const history = useHistory(); @@ -483,7 +488,15 @@ const CT = () => { dispatch(enableGenderFilter()); dispatch(enableDatimAgeGroupFilter()); } - }, [dispatch, active_tab]); + if ( + active_tab === 'vl' && + mini_tab === 'undetectable_untransmittable' + ) { + dispatch(enablePBFWFilter()); + } else { + dispatch(disablePBFWFilter()); + } + }, [dispatch, active_tab, mini_tab]); useEffect(() => { dispatch(loadCurrentOnArt()); @@ -618,6 +631,8 @@ const CT = () => { dispatch(load12MonthSuppressionByYearOfArtStart(active_tab)); dispatch(load24MonthSuppressionByYearOfArtStart(active_tab)); dispatch(loadViralLoadOutcomesHvlByFacility(active_tab)); + dispatch(loadViralLoadUptakeUToU(active_tab)); + dispatch(loadViralLoadCategorizationUToU(active_tab)); dispatch( loadViralLoadOverallUptakeSuppressionBySexVlDone(active_tab) ); @@ -805,6 +820,7 @@ const CT = () => { fromDate, toDate, genders, + pbfws, datimAgeGroups, datimAgePopulation, latestPregnancies, diff --git a/src/views/CT/CurrentOnArt/CurrentOnArtOverview.js b/src/views/CT/CurrentOnArt/CurrentOnArtOverview.js index 89bf65be..5f7f804c 100644 --- a/src/views/CT/CurrentOnArt/CurrentOnArtOverview.js +++ b/src/views/CT/CurrentOnArt/CurrentOnArtOverview.js @@ -10,7 +10,7 @@ import DataCardCT from '../../Shared/DataCardCT'; const CurrentOnArtOverview = () => { const currentOnArt = useSelector(currentOnArtSelectors.getCurrentOnArt); - + const currentOnArtVerified = useSelector( currentOnArtOverviewSelectors.getCurrentOnArtVerified ); @@ -42,7 +42,7 @@ const CurrentOnArtOverview = () => { @@ -55,7 +55,7 @@ const CurrentOnArtOverview = () => { diff --git a/src/views/CT/CurrentOnArt/CurrentOnArtVerifiedByAgeSex.js b/src/views/CT/CurrentOnArt/CurrentOnArtVerifiedByAgeSex.js index aa1eac3a..bdb9a170 100644 --- a/src/views/CT/CurrentOnArt/CurrentOnArtVerifiedByAgeSex.js +++ b/src/views/CT/CurrentOnArt/CurrentOnArtVerifiedByAgeSex.js @@ -7,6 +7,7 @@ import * as currentOnArtByAgeSexSelectors from '../../../selectors/CT/CurrentOnA import { useRecoilValue } from 'recoil'; import moment from 'moment'; import { roundNumber } from '../../../utils/utils'; +import { ETL_DAY } from '../../../constants'; const CurrentOnArtVerifiedByAgeSex = () => { const [currentOnArtByAgeSexChart, setCurrentOnArtByAgeSexChart] = useState( @@ -122,7 +123,7 @@ const CurrentOnArtVerifiedByAgeSex = () => {
- VERIFIED AND CURRENT ON ART BY AGE GROUP AND GENDER - {moment().subtract(2, 'months').add(16, 'days').format('MMMM')} + VERIFIED AND CURRENT ON ART BY AGE GROUP AND GENDER - {moment().subtract(2, 'months').add(ETL_DAY, 'days').format('MMMM')}
diff --git a/src/views/CT/NewOnArt/NewOnArt.js b/src/views/CT/NewOnArt/NewOnArt.js index fd559d09..8784c55d 100644 --- a/src/views/CT/NewOnArt/NewOnArt.js +++ b/src/views/CT/NewOnArt/NewOnArt.js @@ -7,7 +7,7 @@ import { enableStickyFilter, disableStickyFilter, } from '../../../actions/Shared/uiActions'; -import { LOADING_DELAY } from '../../../constants'; +import { ETL_DAY, LOADING_DELAY } from '../../../constants'; import Loading from './../../Shared/Loading'; import SectionFooter from '../../Shared/SectionFooter'; import SectionHeader from '../../Shared/SectionHeader'; @@ -105,7 +105,7 @@ const NewOnArt = () => { were started on ART within the month of{' '} {moment() .subtract(2, 'month') - .add(16, 'days') + .add(ETL_DAY, 'days') .format('MMMM YYYY')} diff --git a/src/views/CT/OTZ/OTZOverview.js b/src/views/CT/OTZ/OTZOverview.js index 0ae3934d..cb1b2169 100644 --- a/src/views/CT/OTZ/OTZOverview.js +++ b/src/views/CT/OTZ/OTZOverview.js @@ -9,6 +9,7 @@ import { formatNumber, roundNumber } from '../../../utils/utils'; import DataCard from '../../Shared/DataCard'; import moment from 'moment'; import DataCardCT from '../../Shared/DataCardCT'; +import { ETL_DAY } from '../../../constants'; const OTZOverview = () => { const [otzTotalAdolescents, setOtzTotalAdolescents] = useState({}); @@ -26,7 +27,7 @@ const OTZOverview = () => { 'ADOLESCENTS CURRENT ON ART as at ' + moment() .subtract(2, 'month') - .add(16, 'days') + .add(ETL_DAY, 'days') .format('MMM YYYY'); const loadOtzTotalAdolescents = useCallback(async () => { diff --git a/src/views/CT/TreatmentOutcomes/TreatmentOutcomes.js b/src/views/CT/TreatmentOutcomes/TreatmentOutcomes.js index 38fae6da..59b2a56c 100644 --- a/src/views/CT/TreatmentOutcomes/TreatmentOutcomes.js +++ b/src/views/CT/TreatmentOutcomes/TreatmentOutcomes.js @@ -4,7 +4,7 @@ import VisibilitySensor from 'react-visibility-sensor'; import { useDispatch, useSelector } from 'react-redux'; import { Card, CardBody, CardHeader, Col, Nav, NavItem, NavLink, Row, TabContent, TabPane } from 'reactstrap'; import { enableStickyFilter, disableStickyFilter } from "../../../actions/Shared/uiActions"; -import { LOADING_DELAY } from "../../../constants"; +import { ETL_DAY, LOADING_DELAY } from '../../../constants'; import Loading from './../../Shared/Loading'; import SectionFooter from '../../Shared/SectionFooter'; import SectionHeader from '../../Shared/SectionHeader'; @@ -128,7 +128,7 @@ const TreatmentOutcomes = () => { > CONTINUITY OF TREATMENT - + */} @@ -145,7 +145,7 @@ const TreatmentOutcomes = () => { : moment() .startOf('month') .subtract(13, 'month') - .add(16, 'days') // Because refresh happens on the 15th date should change on the 16th day of the month + .add(ETL_DAY, 'days') // Because refresh happens on the 15th date should change on the ETL_DAYth day of the month .format('MMM YYYY')} {' '} to   @@ -155,7 +155,7 @@ const TreatmentOutcomes = () => { : moment() .startOf('month') .subtract(2, 'month') - .add(16, 'days') + .add(ETL_DAY, 'days') .format('MMM YYYY')} @@ -167,7 +167,7 @@ const TreatmentOutcomes = () => { {moment() .startOf('month') .subtract(2, 'month') - .add(16, 'days') + .add(ETL_DAY, 'days') .format('MMM YYYY')} @@ -430,7 +430,7 @@ const TreatmentOutcomes = () => { - + */}
diff --git a/src/views/CT/ViralLoad/ViralLoad.js b/src/views/CT/ViralLoad/ViralLoad.js index 5f6241c3..3695862a 100644 --- a/src/views/CT/ViralLoad/ViralLoad.js +++ b/src/views/CT/ViralLoad/ViralLoad.js @@ -34,6 +34,7 @@ const ViralLoadSuppressionByYear12Month = Loadable({ loader: () => import('./Vir const ViralLoadSuppressionByYear24Month = Loadable({ loader: () => import('./ViralLoadSuppressionByYear24Month'), loading: Loading, delay: LOADING_DELAY }); const ViralLoadOutcomesOverview = Loadable({ loader: () => import('./ViralLoadOutcomesOverview'), loading: Loading, delay: LOADING_DELAY }); const ViralLoadOutcomesHvlByFacility = Loadable({ loader: () => import('./ViralLoadOutcomesHvlByFacility'), loading: Loading, delay: LOADING_DELAY }); +const ViralLoadU_U = Loadable({loader: () => import('./ViralLoadU_U'), loading: Loading, delay: LOADING_DELAY,}); const ViralLoad = () => { const branding = { title: "VIRAL LOAD", description: "OVERVIEW", overview: "Viral Load Monitoring" }; @@ -115,6 +116,19 @@ const ViralLoad = () => { VIRAL LOAD OUTCOMES UNSUPPRESSED + + { + setActiveTab('undetectable_untransmittable'); + toggle('undetectable_untransmittable'); + }} + > + {`UNDETECTABLE=UNTRANSMITTABLE (U=U)`} + + @@ -131,7 +145,8 @@ const ViralLoad = () => {
  • Valid Viral Load => 0 - 24 Years (New) - 6 months from Viral load date 25 Years and - Older (New) - 12 months from Viral load date. + Older (New) - 12 months from Viral load + date.
  • @@ -181,9 +196,9 @@ const ViralLoad = () => { of 50 – 199 copies/ml
  • - High Risk LLV => Patients who are - current on treatment with valid viral load - results of 200 – 999 copies/ml + High Risk LLV => Patients who are current + on treatment with valid viral load results + of 200 – 999 copies/ml
  • UNSUPPRESSED => Patients who are current @@ -234,6 +249,9 @@ const ViralLoad = () => { + + +
  • ); diff --git a/src/views/CT/ViralLoad/ViralLoadCategorizationU_U.js b/src/views/CT/ViralLoad/ViralLoadCategorizationU_U.js new file mode 100644 index 00000000..dc57d234 --- /dev/null +++ b/src/views/CT/ViralLoad/ViralLoadCategorizationU_U.js @@ -0,0 +1,86 @@ +import React, { useEffect, useState, useCallback } from 'react'; +import { useSelector } from 'react-redux'; +import { Card, CardBody, CardHeader } from 'reactstrap'; +import Highcharts from "highcharts"; +import HighchartsReact from "highcharts-react-official"; +import * as vlSelectors from '../../../selectors/CT/ViralLoad/viralLoadUToU'; + +const ViralLoadCategorizationU_U = () => { + const [viralLoadUptake, setViralLoadUptake] = useState({}); + const data = useSelector(vlSelectors.getViralLoadCategorizationUToU); + + const loadViralLoadUptake = useCallback(async () => { + setViralLoadUptake({ + chart: { + type: 'pie', + }, + title: { text: '' }, + plotOptions: { + pie: { + innerSize: '80%', + dataLabels: { + enabled: true, + format: '{point.name}:
    {point.percentage:.1f}%
    ({point.y:,.0f})', + style: { + textAlign: 'center', + }, + }, + }, + }, + legend: { align: 'left', verticalAlign: 'top', y: 0, x: 80 }, + series: [ + { + name: 'Number of Patients', + data: [ + { + name: '< 50 COPIES', + y: data[0], + color: '#57A14D', + }, + { + name: '50-199 COPIES', + y: data[1], + color: '#F69323', + }, + { + name: '200-999 COPIES', + y: data[2], + color: '#8E2B15', + }, + { + name: '1000+ COPIES', + y: data[3], + color: '#142359', + }, + ], + }, + ], + }); + }, [data]); + + useEffect(() => { + loadViralLoadUptake(); + }, [loadViralLoadUptake]); + + return ( +
    +
    + + + VALID VIRAL LOAD RESULTS CATEGORIZATION + + +
    + +
    +
    +
    +
    +
    + ); +}; + +export default ViralLoadCategorizationU_U; diff --git a/src/views/CT/ViralLoad/ViralLoadDurableU_UComparison.js b/src/views/CT/ViralLoad/ViralLoadDurableU_UComparison.js new file mode 100644 index 00000000..3b10cb20 --- /dev/null +++ b/src/views/CT/ViralLoad/ViralLoadDurableU_UComparison.js @@ -0,0 +1,76 @@ +import React, { useEffect, useState, useCallback } from 'react'; +import { useSelector } from 'react-redux'; +import { Card, CardBody, CardHeader } from 'reactstrap'; +import Highcharts from "highcharts"; +import HighchartsReact from "highcharts-react-official"; +import * as vlSelectors from '../../../selectors/CT/ViralLoad/viralLoadUToU'; + +const ViralLoadDurableU_UComparison = () => { + const [viralLoadUptake, setViralLoadUptake] = useState({}); + const vl = useSelector(vlSelectors.getViralLoadUptakeUToU); + + const loadViralLoadUptake = useCallback(async () => { + setViralLoadUptake({ + chart: { + type: 'pie', + }, + title: { text: '' }, + plotOptions: { + pie: { + innerSize: '80%', + dataLabels: { + enabled: true, + format: '{point.name}:
    {point.percentage:.2f}%
    ({point.y:,.0f})', + style: { + textAlign: 'center', + }, + }, + }, + }, + legend: { align: 'left', verticalAlign: 'top', y: 0, x: 80 }, + series: [ + { + name: 'Patients', + data: [ + { + name: 'DURABLE LDL', + y: vl?.DurableLDL, + color: '#57A14D', + }, + { + name: 'NON-DURABLE LDL', + y: vl?.TwoConsTests - vl?.DurableLDL, + color: '#8E2B15', + } + ], + }, + ], + }); + }, [vl]); + + useEffect(() => { + loadViralLoadUptake(); + }, [loadViralLoadUptake]); + + return ( +
    +
    + + + PROPORTION OF DURABLE PATIENTS + + +
    + +
    +
    +
    +
    +
    + ); +}; + +export default ViralLoadDurableU_UComparison; diff --git a/src/views/CT/ViralLoad/ViralLoadOutcomesOverview.js b/src/views/CT/ViralLoad/ViralLoadOutcomesOverview.js index 12b6ebbb..b7a69202 100644 --- a/src/views/CT/ViralLoad/ViralLoadOutcomesOverview.js +++ b/src/views/CT/ViralLoad/ViralLoadOutcomesOverview.js @@ -7,11 +7,6 @@ import DataCard from '../../Shared/DataCard'; import DataCardCT from '../../Shared/DataCardCT'; const ViralLoadOutcomesOverview = () => { - const currentOnArt = useSelector(currentOnArtOverviewSelectors.getCurrentOnArt); - const hasCurrentVl = useSelector(currentOnArtOverviewSelectors.getHasCurrentVl); - const virallySuppressed = useSelector(currentOnArtOverviewSelectors.getVirallySuppressed); - const lowLevelViremia = useSelector(currentOnArtOverviewSelectors.getLowLevelViremia); - const suppressed = virallySuppressed; const highRisk = useSelector(currentOnArtOverviewSelectors.getHighRisk); const lowRisk = useSelector(currentOnArtOverviewSelectors.getLowRisk); const ldl = useSelector(currentOnArtOverviewSelectors.getLDL); diff --git a/src/views/CT/ViralLoad/ViralLoadOverallNonSuppressedVlTest.js b/src/views/CT/ViralLoad/ViralLoadOverallNonSuppressedVlTest.js index 4847c564..6714aea4 100644 --- a/src/views/CT/ViralLoad/ViralLoadOverallNonSuppressedVlTest.js +++ b/src/views/CT/ViralLoad/ViralLoadOverallNonSuppressedVlTest.js @@ -95,7 +95,7 @@ const ViralLoadOverallNonSuppressedVlTest = () => { name: 'NUMBER OF PLHIV ON ART WITH VL > 1000 COPIES/ML WHO RECEIVED FOLLOW UP VL TESTS', y: viralLoadUptakeGt1000CopiesRecFollowTestAll, color: '#fad53f', - text: viralLoadUptakeGt1000CopiesRecFollowTestAll.toLocaleString('en') + text: viralLoadUptakeGt1000CopiesRecFollowTestAll.toLocaleString('en') }, { name: 'NUMBER VIRALLY SUPPRESSED ON FOLLOW UP VL TEST', @@ -103,12 +103,11 @@ const ViralLoadOverallNonSuppressedVlTest = () => { color: '#1c943e', text: ' NUMBER VIRALLY SUPPRESSED
    ON FOLLOW UP VL TEST ' + viralLoadUptakeGt1000CopiesRecFollowTest[0]?.Num.toLocaleString('en') + ' (' + parseFloat(((viralLoadUptakeGt1000CopiesRecFollowTest[0]?.Num / totalFollowTest) * 100).toString()).toFixed(0) + '%)' }, - // TODO:: Last bar of the chart { name: 'NUMBER WITH FOLLOW UP VL TEST AT VL > 1000 COPIES/ML SWITCHED TO SECOND LINE REGIMENT', y: viralLoadOverallNumberGt1000CopiesSecondlineRegimentData ?? 0, color: '#142459', - text: viralLoadOverallNumberGt1000CopiesSecondlineRegimentData?.toLocaleString('en') + text: viralLoadOverallNumberGt1000CopiesSecondlineRegimentData?.toLocaleString('en') } ] }, diff --git a/src/views/CT/ViralLoad/ViralLoadPropotionU_U.js b/src/views/CT/ViralLoad/ViralLoadPropotionU_U.js new file mode 100644 index 00000000..34a88bc5 --- /dev/null +++ b/src/views/CT/ViralLoad/ViralLoadPropotionU_U.js @@ -0,0 +1,95 @@ +import React, { useEffect, useState, useCallback } from 'react'; +import { useSelector } from 'react-redux'; +import { Card, CardBody, CardHeader } from 'reactstrap'; +import Highcharts from "highcharts"; +import HighchartsReact from "highcharts-react-official"; +import * as vlSelectors from '../../../selectors/CT/ViralLoad/viralLoadUToU'; + +const ViralLoadPropotionU_U = () => { + const [viralLoadUptake, setViralLoadUptake] = useState({}); + const vl = useSelector(vlSelectors.getViralLoadUptakeUToU); + + const loadViralLoadUptake = useCallback(async () => { + setViralLoadUptake({ + title: { text: '' }, + xAxis: [ + { + categories: [ + 'ELIGIBLE FOR AT LEAST 2 VL TEST', + 'LDL (LAST ONE TEST)', + 'DURABLE LDL (U=U)', + ], + title: { text: '' }, + crosshair: true, + }, + ], + yAxis: [ + { + title: { text: 'NUMBER OF PATIENTS' }, + labels: { format: '{value}' }, + }, + ], + plotOptions: { + column: { + stacking: 'normal', + dataLabels: { + enabled: true, + crop: false, + overflow: 'none', + color: 'black', + format: '{point.y:,.0f}', + verticalAlign: 'top', + y: -20, + }, + }, + }, + legend: { align: 'left', verticalAlign: 'top', y: 0, x: 80 }, + series: [ + { + name: 'ELIGIBLE FOR AT LEAST 2 VL TEST', + data: [vl?.TwoEligibleTests], + type: 'column', + color: '#F69323', + }, + { + name: 'LDL (LAST ONE TEST)', + data: [null, vl?.LDLLastOneTest], + type: 'column', + color: '#19A367', + }, + { + name: 'DURABLE LDL (U=U)', + data: [null, null, vl?.DurableLDL], + type: 'column', + color: '#142359', + }, + ], + }); + }, [vl]); + + useEffect(() => { + loadViralLoadUptake(); + }, [loadViralLoadUptake]); + + return ( +
    +
    + + + PROPORTION OF UNDETECTABLE LEVELS + + +
    + +
    +
    +
    +
    +
    + ); +}; + +export default ViralLoadPropotionU_U; diff --git a/src/views/CT/ViralLoad/ViralLoadU_U.js b/src/views/CT/ViralLoad/ViralLoadU_U.js new file mode 100644 index 00000000..1ae89672 --- /dev/null +++ b/src/views/CT/ViralLoad/ViralLoadU_U.js @@ -0,0 +1,80 @@ +import { useSelector } from "react-redux"; +import { Col, Row } from "reactstrap"; +import SectionHeader from "../../Shared/SectionHeader"; +import ViralLoadUptakeU_U from "./ViralLoadUptakeU_U"; +import ViralLoadCategorizationU_U from "./ViralLoadCategorizationU_U"; +import DataCardCT from "../../Shared/DataCardCT"; +import { formatNumber, roundNumber } from '../../../utils/utils'; +import * as vlSelectors from '../../../selectors/CT/ViralLoad/viralLoadUToU'; +import ViralLoadPropotionU_U from "./ViralLoadPropotionU_U"; +import ViralLoadDurableU_UComparison from './ViralLoadDurableU_UComparison'; + + +const ViralLoadU_U = () => { + const vl = useSelector(vlSelectors.getViralLoadUptakeUToU); + + return ( + <> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {/**/} + + ); + +} + +export default ViralLoadU_U; diff --git a/src/views/CT/ViralLoad/ViralLoadUptakeU_U.js b/src/views/CT/ViralLoad/ViralLoadUptakeU_U.js new file mode 100644 index 00000000..c402a4cf --- /dev/null +++ b/src/views/CT/ViralLoad/ViralLoadUptakeU_U.js @@ -0,0 +1,88 @@ +import React, { useEffect, useState, useCallback } from 'react'; +import { useSelector } from 'react-redux'; +import { Card, CardBody, CardHeader } from 'reactstrap'; +import Highcharts from "highcharts"; +import HighchartsReact from "highcharts-react-official"; +import * as vlSelectors from '../../../selectors/CT/ViralLoad/viralLoadUToU'; + +const ViralLoadUptakeU_U = () => { + const [viralLoadUptake, setViralLoadUptake] = useState({}); + const vl = useSelector(vlSelectors.getViralLoadUptakeUToU); + + const loadViralLoadUptake = useCallback(async () => { + setViralLoadUptake({ + title: { text: '' }, + xAxis: [ + { + categories: [ + 'ELIGIBLE FOR VL TEST', + 'VALID VIRAL LOAD RESULTS', + ], + title: { text: '' }, + crosshair: true, + }, + ], + yAxis: [ + { + title: { text: 'NUMBER OF PATIENTS' }, + labels: { format: '{value}' }, + }, + ], + plotOptions: { + column: { + stacking: 'normal', + dataLabels: { + enabled: true, + crop: false, + overflow: 'none', + color: 'black', + format: '{point.y:,.0f}', + verticalAlign: 'top', + y: -20, + }, + }, + }, + legend: { align: 'left', verticalAlign: 'top', y: 0, x: 80 }, + series: [ + { + name: 'ELIGIBLE FOR VL TEST', + data: [vl?.EligibleVL], + type: 'column', + color: '#F69323', + }, + { + name: 'VALID VIRAL LOAD RESULTS', + data: [null, vl?.HasValidVL], + type: 'column', + color: '#19A367', + }, + ], + }); + }, [vl]); + + useEffect(() => { + loadViralLoadUptake(); + }, [loadViralLoadUptake]); + + return ( +
    +
    + + + VIRAL LOAD UPTAKE + + +
    + +
    +
    +
    +
    +
    + ); +}; + +export default ViralLoadUptakeU_U; diff --git a/src/views/Home/HomeVLCascade.js b/src/views/Home/HomeVLCascade.js index 2fad36b1..ab293ccc 100644 --- a/src/views/Home/HomeVLCascade.js +++ b/src/views/Home/HomeVLCascade.js @@ -6,16 +6,27 @@ import * as currentOnArtSelectors from '../../selectors/CT/CurrentOnArt/currentO import { formatNumber, roundNumber } from '../../utils/utils'; import DataCard from '../Shared/DataCard'; import moment from 'moment'; +import { ETL_DAY } from '../../constants'; const HomeVLCascade = () => { // const currentOnArt = useSelector( // currentOnArtOverviewSelectors.getCurrentOnArt // ); - + const currentDate = moment(); + + // Check if the current date is before the 20th of the month + if (currentDate.date() < 19) { + // If true, subtract 2 months + currentDate.subtract(2, 'months'); + } else { + // If false, subtract 1 month + currentDate.subtract(1, 'months'); + } + // Format the date + const reporting_month = currentDate.format('MMM YYYY'); + const currentOnArt = useSelector(currentOnArtSelectors.getCurrentOnArt); - const currentOnArtText = - 'CURRENT ON ART as at ' + - moment().subtract(2, 'month').add(16, 'days').format('MMM YYYY'); + const currentOnArtText = 'CURRENT ON ART as at ' + reporting_month; const eligibleForVl = useSelector( currentOnArtOverviewSelectors.getEligibleForVl ); diff --git a/src/views/Pages/Resources/Resources.js b/src/views/Pages/Resources/Resources.js index c79af92a..239debb4 100644 --- a/src/views/Pages/Resources/Resources.js +++ b/src/views/Pages/Resources/Resources.js @@ -34,7 +34,7 @@ const Resources = ()=>{ }, { app: 'HIS Sites Portal', - link: 'https://prod.kenyahmis.org:3001/', + link: 'https://hisportal.kenyahmis.org/', description: 'HIS List management portal is a platform where partners can manage information about their supported HIS sites.', logo: kenyahmis, diff --git a/src/views/RR/RRCounty.js b/src/views/RR/RRCounty.js index 9970444c..7cc1835f 100644 --- a/src/views/RR/RRCounty.js +++ b/src/views/RR/RRCounty.js @@ -5,6 +5,7 @@ import Highcharts from "highcharts"; import HighchartsReact from "highcharts-react-official"; import { capitalize, getAll } from '../Shared/Api'; import { Card, CardBody, CardHeader } from 'reactstrap'; +import { ETL_DAY } from '../../constants'; const _ = require("lodash"); const RRCounty = () => { @@ -30,12 +31,10 @@ const RRCounty = () => { partner: filters.partners, agency: filters.agencies, project: filters.projects, - fromDate: filters.fromDate - ? filters.fromDate - : moment() - .subtract(2, 'month') - .add(16, 'days') - .format('MMM YYYY'), + fromDate: filters.fromDate || moment() + .subtract(2, 'month') + .add(ETL_DAY, 'days') + .format('MMM YYYY'), }; params.period = filters.fromDate ? moment(params.fromDate, 'MMM YYYY') @@ -44,15 +43,15 @@ const RRCounty = () => { .format('YYYY,M') : moment() .subtract(2, 'month') - .add(16, 'days') + .add(ETL_DAY, 'days') .format('YYYY,M'); const overallReportingRateResult = await getAll('manifests/recencyreportingbycounty/' + rrTab, params); params.period = filters.fromDate ? moment(params.fromDate, "MMM YYYY").startOf('month').subtract(1, 'month').format('YYYY,M') : - moment().subtract(3, 'month').add(16, 'days').format('YYYY,M'); + moment().subtract(3, 'month').add(ETL_DAY, 'days').format('YYYY,M'); const consistencyResult = await getAll('manifests/consistencyreportingbycountypartner/' + rrTab + '?reportingType=county', params); const rrData = await getAll('manifests/expectedPartnerCounty/' + rrTab + '?reportingType=county', params); - + /* Overall reporting */ const overAllReportingData = _.orderBy(overallReportingRateResult, [function(resultItem) { return parseInt(resultItem.Percentage, 10); }], ['desc']); @@ -80,12 +79,13 @@ const RRCounty = () => { const consistency_values = []; let expected = 0; for (const [key, value] of Object.entries(consistencyResult)) { - const expectedCounty = rrData.filter(obj => obj.county === key); + const expectedCounty = rrData.filter(obj => obj.county.toUpperCase() === key.toUpperCase()); if (expectedCounty.length > 0) { expected = expectedCounty[0].totalexpected; } const cos = expected === 0 ? 0 : parseInt(((value/expected)*100).toString()); + if (cos <= 50) { consistency_values.push({ county: key, diff --git a/src/views/RR/RRIndicatorDefinition.js b/src/views/RR/RRIndicatorDefinition.js index ae397904..950be9ba 100644 --- a/src/views/RR/RRIndicatorDefinition.js +++ b/src/views/RR/RRIndicatorDefinition.js @@ -1,17 +1,18 @@ import { Card, CardBody, CardHeader } from 'reactstrap'; import React from 'react'; import moment from 'moment'; +import { ETL_DAY } from '../../constants'; const RRIndicatorDefinition = () => { const today = new Date(); const year = today.getFullYear(); - const month = moment().subtract(2, 'month').add(16, 'days').format('MMMM'); - const previousMonthDate = moment().subtract(3, 'month').add(16, 'days'); + const month = moment().subtract(2, 'month').add(ETL_DAY, 'days').format('MMMM'); + const previousMonthDate = moment().subtract(3, 'month').add(ETL_DAY, 'days'); const previousMonth = new Date(previousMonthDate).toLocaleString( 'default', { month: 'long' } ); - const twoMonthsBackDate = moment().subtract(4, 'month').add(16, 'days'); + const twoMonthsBackDate = moment().subtract(4, 'month').add(ETL_DAY, 'days'); const twoMonthsBackMonth = new Date(twoMonthsBackDate).toLocaleString( 'default', { month: 'long' } diff --git a/src/views/RR/RROverview.js b/src/views/RR/RROverview.js index 1fdf8021..facabc5e 100644 --- a/src/views/RR/RROverview.js +++ b/src/views/RR/RROverview.js @@ -5,6 +5,7 @@ import moment from 'moment'; import { getAll } from '../Shared/Api'; import CsvDownloader from 'react-csv-downloader'; import { Spinner } from 'reactstrap'; +import { ETL_DAY } from '../../constants'; const RROverview = () => { const filters = useSelector(state => state.filters); @@ -12,6 +13,8 @@ const RROverview = () => { const [expected, setExpected] = useState('0'); const [consistencyStats, setConsistnecy] = useState({ consistency: [], stats: '0', statsPerc: 0 }); const [recencyStats, setRecency] = useState({ recency: [], stats: '0', statsPerc: 0 }); + const [infrastructureStats, setInfrastructure] = useState({}); + const [implementationStats, setImplementationStats] = useState(0); const overallReportingRatesByFacilityReportedFiltered = useSelector(state => state.overallReportingRatesByFacilityReported.listFiltered); const overallReportingRatesByFacilityReportedUnFiltered = useSelector(state => state.overallReportingRatesByFacilityReported.listUnfiltered); @@ -48,7 +51,7 @@ const RROverview = () => { partner: filters.partners, agency: filters.agencies, project: filters.projects, - fromDate: filters.fromDate ? filters.fromDate : moment().format("MMM YYYY") + fromDate: filters.fromDate || moment().format("MMM YYYY") }; params.period = filters.fromDate ? moment(params.fromDate, "MMM YYYY").startOf('month').subtract(1, 'month').format('YYYY,M') : @@ -65,7 +68,7 @@ const RROverview = () => { partner: filters.partners, agency: filters.agencies, project: filters.projects, - fromDate: filters.fromDate ? filters.fromDate : moment().subtract(2, 'month').add(16, 'days').format('MMM YYYY') + fromDate: filters.fromDate || moment().subtract(2, 'month').add(ETL_DAY, 'days').format('MMM YYYY') }; params.period = filters.fromDate ? moment(params.fromDate, 'MMM YYYY') @@ -74,7 +77,7 @@ const RROverview = () => { .format('YYYY,M') : moment() .subtract(2, 'month') - .add(16, 'days') + .add(ETL_DAY, 'days') .format('YYYY,M'); const data = await getAll('manifests/consistency/' + rrTab, params); setConsistnecy({ consistency: [], stats: data.consistency ? data.consistency.toLocaleString('en') : [], statsPerc: getPerc(data.consistency , expected) }); @@ -100,10 +103,81 @@ const RROverview = () => { .add(1, 'month') .format('YYYY,M') : moment() - .subtract(16, 'days') + .subtract(19, 'days') .format('YYYY,M'); const data = await getAll('manifests/recency/' + rrTab, params); - setRecency({ recency: [], stats: data.recency ? data.recency.toLocaleString('en') : [], statsPerc: getPerc(data.recency , expected) }); + setRecency({ recency: [], stats: data.recency ? data.recency.toLocaleString('en') : 0, statsPerc: getPerc(data.recency , expected) }); + }, [filters, rrTab, expected]); + + const loadFacilityInfrastructureType = useCallback(async () => { + let params = { + county: filters.counties, + subCounty: filters.subCounties, + facility: filters.facilities, + partner: filters.partners, + agency: filters.agencies, + project: filters.projects, + fromDate: filters.fromDate + ? filters.fromDate + : moment() + .subtract(16, 'days') + .format('MMM YYYY'), + }; + params.period = filters.fromDate + ? moment(params.fromDate, 'MMM YYYY') + .startOf('month') + .add(1, 'month') + .format('YYYY,M') + : moment() + .subtract(16, 'days') + .format('YYYY,M'); + const data = await getAll('manifests/emrinfo/' + rrTab, params); + setInfrastructure({ + onCloud: data.find((e) => e?.infrastructure_type === 'On Cloud'), + onPremises: data.find( + (e) => e?.infrastructure_type === 'On Premises' + ), + }); + }, [filters, rrTab, expected]); + + const loadImplementationDate = useCallback(async () => { + let params = { + county: filters.counties, + subCounty: filters.subCounties, + facility: filters.facilities, + partner: filters.partners, + agency: filters.agencies, + project: filters.projects, + fromDate: filters.fromDate || moment() + .subtract(16, 'days') + .format('MMM YYYY'), + }; + params.period = filters.fromDate + ? moment(params.fromDate, 'MMM YYYY') + .startOf('month') + .add(1, 'month') + .format('YYYY,M') + : moment() + .subtract(16, 'days') + .format('YYYY,M'); + params.year = filters.fromDate + ? moment(params.fromDate, 'MMM YYYY') + .startOf('month') + .format('YYYY') + : moment() + .subtract(16, 'days') + .format('YYYY') + params.month = filters.fromDate + ? moment(params.fromDate, 'MMM YYYY') + .format('M') + : moment() + .subtract(16, 'days') + .format('M') + const data = await getAll( + 'manifests/implementationDate/' + rrTab, + params + ); + setImplementationStats(data?.facilities_number); }, [filters, rrTab, expected]); useEffect(() => { @@ -158,9 +232,7 @@ const RROverview = () => {   {' '} - {recencyStats.statsPerc - ? recencyStats.statsPerc - : 0} + {recencyStats.statsPerc || 0} {' '} % @@ -221,14 +293,10 @@ const RROverview = () => { sub_county: l.subCounty, agency: l.Agency, partner: l.Partner, - reporting_date: filters.fromDate - ? filters.fromDate - : moment() - .startOf('month') - .subtract(2, 'month') - .add(16, 'days') - - .format('MMM YYYY'), + reporting_date: filters.fromDate || moment() + .subtract(2, 'month') + .add(ETL_DAY, 'days') + .format('MMM YYYY'), }) )} text="Download facilities not reporting" @@ -276,12 +344,10 @@ const RROverview = () => { sub_county: l.Subcounty, agency: l.Agency, partner: l.Partner, - reporting_date: filters.fromDate - ? filters.fromDate - : moment() - .startOf('month') - .subtract(1, 'month') - .format('MMM YYYY'), + reporting_date: filters.fromDate || moment() + .startOf('month') + .subtract(1, 'month') + .format('MMM YYYY'), number_of_uploads: l.NumberOfUploads, }) )} diff --git a/src/views/RR/RROverviewTrends.js b/src/views/RR/RROverviewTrends.js index d5229905..d4aaf897 100644 --- a/src/views/RR/RROverviewTrends.js +++ b/src/views/RR/RROverviewTrends.js @@ -5,6 +5,7 @@ import Highcharts from "highcharts"; import HighchartsReact from "highcharts-react-official"; import moment from 'moment'; import { getAll } from '../Shared/Api'; +import { ETL_DAY } from '../../constants'; const RROverviewTrends = () => { const filters = useSelector(state => state.filters); @@ -98,13 +99,13 @@ const RROverviewTrends = () => { let endDate = moment() .endOf('month'); if (filters.toDate || filters.fromDate) { - endDate = moment(filters.toDate ? filters.toDate: filters.fromDate, 'MMM YYYY').endOf('month'); + endDate = moment(filters.toDate || filters.fromDate, 'MMM YYYY').endOf('month'); } const startDate = endDate .clone() .subtract(numberOfMonths, 'month') .subtract(2, 'month') - .add(16, 'days') + .add(ETL_DAY, 'days') .startOf('month'); params.startDate = startDate.format('YYYY-MM-DD'); params.endDate = endDate.format('YYYY-MM-DD'); diff --git a/src/views/RR/RRPartner.js b/src/views/RR/RRPartner.js index 17b1ecb0..4a3db309 100644 --- a/src/views/RR/RRPartner.js +++ b/src/views/RR/RRPartner.js @@ -5,6 +5,7 @@ import Highcharts from "highcharts"; import HighchartsReact from "highcharts-react-official"; import { capitalize, getAll } from '../Shared/Api'; import { Card, CardBody, CardHeader } from 'reactstrap'; +import { ETL_DAY } from '../../constants'; const _ = require("lodash"); const RRPartner = () => { @@ -31,16 +32,14 @@ const RRPartner = () => { partner: filters.partners, agency: filters.agencies, project: filters.projects, - fromDate: filters.fromDate - ? filters.fromDate - : moment() - .subtract(2, 'month') - .add(16, 'days') - .format('MMM YYYY'), + fromDate: filters.fromDate || moment() + .subtract(2, 'month') + .add(ETL_DAY, 'days') + .format('MMM YYYY'), }; params.period = filters.fromDate ? moment(params.fromDate, "MMM YYYY").startOf('month').subtract(0, 'month').format('YYYY,M') : - moment().subtract(2, 'month').add(16, 'days').format('YYYY,M'); + moment().subtract(2, 'month').add(ETL_DAY, 'days').format('YYYY,M'); const overallReportingRateResult = await getAll('manifests/recencyreportingbypartner/' + rrTab, params); params.period = filters.fromDate ? moment(params.fromDate, 'MMM YYYY') @@ -49,7 +48,7 @@ const RRPartner = () => { .format('YYYY,M') : moment() .subtract(3, 'month') - .add(16, 'days') + .add(ETL_DAY, 'days') .format('YYYY,M'); const consistencyResult = await getAll('manifests/consistencyreportingbycountypartner/' + rrTab + '?reportingType=partner', params); const rrData = await getAll('manifests/expectedPartnerCounty/' + rrTab + '?reportingType=partner', params); diff --git a/src/views/Shared/SectionHeader.js b/src/views/Shared/SectionHeader.js index 4674ac7e..196b68dc 100644 --- a/src/views/Shared/SectionHeader.js +++ b/src/views/Shared/SectionHeader.js @@ -16,7 +16,7 @@ const SectionHeader = ({ title, description}) => { - +
    {title}
    diff --git a/src/views/Shared/UniversalFilter.js b/src/views/Shared/UniversalFilter.js index 166e8f85..c8940b41 100644 --- a/src/views/Shared/UniversalFilter.js +++ b/src/views/Shared/UniversalFilter.js @@ -1,8 +1,8 @@ -import React, { useEffect, useState, useCallback } from 'react'; +import React, { useEffect, useState, useCallback } from 'react'; import { BrowserRouter as Router, Link, useLocation } from 'react-router-dom'; import { useHistory, useParams } from 'react-router-dom'; import moment from 'moment'; -import { DateInput, MonthInput, MonthRangeInput, YearInput } from 'semantic-ui-calendar-react'; +import { MonthRangeInput } from 'semantic-ui-calendar-react'; import { Dropdown, Message } from 'semantic-ui-react'; import { PAGES } from '../../constants'; import { Row, Col } from 'reactstrap'; @@ -33,6 +33,7 @@ const UniversalFilter = () => { const [agencies, setAgencies] = useState([]); const [projects, setProjects] = useState([]); const [genders, setGenders] = useState([]); + const [pbfws, setPbfws] = useState([]); const [emr, setEmr] = useState([]); const [datimAgeGroups, setDatimAgeGroups] = useState([]); const [datimAgePopulations, setDatimAgePopulations] = useState([]); @@ -70,7 +71,7 @@ const UniversalFilter = () => { } let ageGroups; - if (active_tab === 'comparison') + if (active_tab === 'comparison') { ageGroups = [ 'Under 1', '1 to 9', @@ -79,7 +80,7 @@ const UniversalFilter = () => { '20 to 24', '25+', ]; - else + } else { ageGroups = [ 'Under 1', '1 to 4', @@ -97,6 +98,7 @@ const UniversalFilter = () => { '60 to 64', '65+', ]; + } const loadSites = useCallback(async () => { switch (ui.currentPage) { @@ -206,6 +208,26 @@ const UniversalFilter = () => { setDatimAgeGroups( ageGroups.map((c) => ({ value: c, key: c, text: c })) ); + setPbfws( + [ + { + val: 'Known Positives|Yes', + txt: 'Pregnant Known Positives', + }, + { + val: 'Known Positives|No', + txt: 'Breastfeeding Known Positives', + }, + { + val: 'New Positives|Yes', + txt: 'Pregnant New Positives', + }, + { + val: 'New Positives|No', + txt: 'Breastfeeding New Positives', + }, + ].map((c) => ({ value: c.val, key: c.val, text: c.txt })) + ); setDatimAgePopulations( ['<18', '>18'].map((c) => ({ value: c, key: c, text: c })) ); @@ -577,6 +599,33 @@ const UniversalFilter = () => {
    ) : null} + {filters.pbfwFilterEnabled ? ( + +
    + + { + dispatch(actions.filterByPBFW(data.value)); + }} + /> +
    + + ) : null} {filters.datimAgePopulationFilterEnabled ? ( { selection search options={indicators} - value={ - filters.indicators - ? filters.indicators - : 'Tx_New' - } + value={filters.indicators || 'Tx_New'} onChange={(e, data) => { dispatch( actions.filterByIndicator(data.value)