@kenyaemr/esm-facility-dashboard-app 5.4.1-pre.2025 → 5.4.1-pre.2029
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.turbo/turbo-build.log +6 -6
- package/dist/300.js +1 -1
- package/dist/74.js +1 -1
- package/dist/74.js.map +1 -1
- package/dist/kenyaemr-esm-facility-dashboard-app.js.buildmanifest.json +9 -9
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +1 -1
- package/src/constants.ts +0 -16
- package/src/surveillance/charts/delayed-eac-charts.component.tsx +20 -35
- package/src/surveillance/charts/dna-pcr-pending-chart.component.tsx +20 -34
- package/src/surveillance/charts/hei-final-outcome.component.tsx +23 -35
- package/src/surveillance/charts/hiv-not-linked-to-art.component.tsx +27 -20
- package/src/surveillance/charts/missed-opportunity-vl-chart.component.tsx +20 -36
- package/src/surveillance/charts/pbfw-not-in-prep.component.tsx +27 -20
- package/src/surveillance/surveillance-dashboard.component.tsx +40 -20
- package/src/surveillance/surveillance-filters.component.tsx +46 -41
- package/src/types/index.ts +4 -0
- package/translations/en.json +9 -6
package/dist/routes.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"kenyaemrCharts":"^1.6.7"},"extensions":[{"component":"surveillanceDashboardLink","name":"surveillance-dashboard-link","slot":"facility-dashboard-left-panel-slot"},{"component":"aboveSiteDashboardLink","name":"above-site-dashboard-link","slot":"facility-dashboard-left-panel-slot"}],"workspaces":[],"modals":[],"pages":[{"component":"root","route":"facility-dashboard"}],"version":"5.4.1-pre.
|
|
1
|
+
{"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"kenyaemrCharts":"^1.6.7"},"extensions":[{"component":"surveillanceDashboardLink","name":"surveillance-dashboard-link","slot":"facility-dashboard-left-panel-slot"},{"component":"aboveSiteDashboardLink","name":"above-site-dashboard-link","slot":"facility-dashboard-left-panel-slot"}],"workspaces":[],"modals":[],"pages":[{"component":"root","route":"facility-dashboard"}],"version":"5.4.1-pre.2029"}
|
package/package.json
CHANGED
package/src/constants.ts
CHANGED
|
@@ -20,22 +20,6 @@ export const formatNewDate = (date: Date | null | undefined) => {
|
|
|
20
20
|
return date ? new Date(date) : '';
|
|
21
21
|
};
|
|
22
22
|
|
|
23
|
-
// Generate Dammy dates
|
|
24
|
-
export const sevenDaysRunningDates = (index: number, endDate: Date = new Date()): string => {
|
|
25
|
-
const date = new Date(endDate);
|
|
26
|
-
date.setDate(date.getDate() - index);
|
|
27
|
-
return date.toISOString().split('T')[0];
|
|
28
|
-
};
|
|
29
|
-
|
|
30
23
|
export const formattedDate = (date: Date) => {
|
|
31
24
|
return date ? dayjs(date).format(DATE_PICKER_FORMAT) : '';
|
|
32
25
|
};
|
|
33
|
-
|
|
34
|
-
export const getNumberOfDays = (startDate?: Date, endDate?: Date) => {
|
|
35
|
-
if (!startDate || !endDate) {
|
|
36
|
-
return 7;
|
|
37
|
-
}
|
|
38
|
-
const start = dayjs(startDate);
|
|
39
|
-
const end = dayjs(endDate);
|
|
40
|
-
return end.diff(start, 'day');
|
|
41
|
-
};
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import '@carbon/charts/styles.css';
|
|
2
|
-
import React
|
|
2
|
+
import React from 'react';
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
import BaseIndicatorTrendChart from './base-indicator-trend-chart.component';
|
|
5
|
-
import BaseProgressTrackingChart from './base-progress-tracking-chart.component';
|
|
6
|
-
import { getNumberOfDays, sevenDaysRunningDates } from '../../constants';
|
|
7
5
|
import styles from './charts.scss';
|
|
6
|
+
import EmptyState from '../empty-state/empty-state-log.components';
|
|
7
|
+
import useFacilityDashboardSurveillance from '../../hooks/useFacilityDashboardSurveillance';
|
|
8
|
+
import { useSurveillanceData } from '../../hooks/useSurveillanceData';
|
|
9
|
+
import { InlineLoading } from '@carbon/react';
|
|
8
10
|
type DelayedEACChartsProps = {
|
|
9
11
|
startDate?: Date;
|
|
10
12
|
endDate?: Date;
|
|
@@ -12,40 +14,23 @@ type DelayedEACChartsProps = {
|
|
|
12
14
|
|
|
13
15
|
const DelayedEACCharts: React.FC<DelayedEACChartsProps> = ({ startDate, endDate }) => {
|
|
14
16
|
const { t } = useTranslation();
|
|
15
|
-
const
|
|
16
|
-
return Array.from({ length: numRecords }, (_, i) => ({
|
|
17
|
-
day: sevenDaysRunningDates(i, endDate),
|
|
18
|
-
value: Math.floor(Math.random() * 50),
|
|
19
|
-
}));
|
|
20
|
-
};
|
|
17
|
+
const { error, isLoading, surveillanceSummary } = useFacilityDashboardSurveillance(startDate, endDate);
|
|
21
18
|
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
const generateRandomDataProgress = (numRecords: number) => {
|
|
25
|
-
const data = [];
|
|
26
|
-
for (let i = 1; i <= numRecords; i++) {
|
|
27
|
-
data.push({
|
|
28
|
-
group: 'Pending',
|
|
29
|
-
key: sevenDaysRunningDates(i, endDate),
|
|
30
|
-
value: Math.floor(Math.random() * 50),
|
|
31
|
-
});
|
|
32
|
-
data.push({
|
|
33
|
-
group: 'Completed',
|
|
34
|
-
key: sevenDaysRunningDates(i, endDate),
|
|
35
|
-
value: Math.floor(Math.random() * 50),
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
return data;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const data = useMemo(() => generateRandomDataProgress(numberSequence), [numberSequence, startDate, endDate]);
|
|
42
|
-
const values = useMemo(() => generateRandomData(numberSequence), [numberSequence, startDate, endDate]);
|
|
19
|
+
const delayedEACValue = useSurveillanceData(surveillanceSummary, 'getMonthlyVirallyUnsuppressedWithoutEAC');
|
|
43
20
|
return (
|
|
44
|
-
<
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
21
|
+
<div className={styles.chart}>
|
|
22
|
+
{isLoading ? (
|
|
23
|
+
<InlineLoading status="active" iconDescription="Loading" description="Loading data..." />
|
|
24
|
+
) : delayedEACValue.length > 0 ? (
|
|
25
|
+
<BaseIndicatorTrendChart
|
|
26
|
+
data={delayedEACValue}
|
|
27
|
+
title={t('delayedEAC', 'Delayed enhanced adherence counselling')}
|
|
28
|
+
yAxisTitle={t('numberDelayedEAC', 'Number of Delayed EAC')}
|
|
29
|
+
/>
|
|
30
|
+
) : (
|
|
31
|
+
<EmptyState subTitle={t('noDelayedEAC', 'No Delayed EAC data to display')} />
|
|
32
|
+
)}
|
|
33
|
+
</div>
|
|
49
34
|
);
|
|
50
35
|
};
|
|
51
36
|
|
|
@@ -1,49 +1,35 @@
|
|
|
1
1
|
import '@carbon/charts/styles.css';
|
|
2
|
-
import React
|
|
2
|
+
import React from 'react';
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
import BaseIndicatorTrendChart from './base-indicator-trend-chart.component';
|
|
5
|
-
import BaseProgressTrackingChart from './base-progress-tracking-chart.component';
|
|
6
|
-
import { getNumberOfDays, sevenDaysRunningDates } from '../../constants';
|
|
7
5
|
import styles from './charts.scss';
|
|
6
|
+
import useFacilityDashboardSurveillance from '../../hooks/useFacilityDashboardSurveillance';
|
|
7
|
+
import { useSurveillanceData } from '../../hooks/useSurveillanceData';
|
|
8
|
+
import EmptyState from '../empty-state/empty-state-log.components';
|
|
9
|
+
import { InlineLoading } from '@carbon/react';
|
|
8
10
|
type DNAPCRPendingChartsProps = {
|
|
9
11
|
startDate?: Date;
|
|
10
12
|
endDate?: Date;
|
|
11
13
|
};
|
|
12
14
|
const DNAPCRPendingCharts: React.FC<DNAPCRPendingChartsProps> = ({ startDate, endDate }) => {
|
|
13
15
|
const { t } = useTranslation();
|
|
14
|
-
const
|
|
15
|
-
return Array.from({ length: numRecords }, (_, i) => ({
|
|
16
|
-
day: sevenDaysRunningDates(i, endDate),
|
|
17
|
-
value: Math.floor(Math.random() * 50),
|
|
18
|
-
}));
|
|
19
|
-
};
|
|
16
|
+
const { error, isLoading, surveillanceSummary } = useFacilityDashboardSurveillance(startDate, endDate);
|
|
20
17
|
|
|
21
|
-
const
|
|
22
|
-
const generateRandomDataProgress = (numRecords: number) => {
|
|
23
|
-
const data = [];
|
|
24
|
-
for (let i = 1; i <= numRecords; i++) {
|
|
25
|
-
data.push({
|
|
26
|
-
group: 'Pending',
|
|
27
|
-
key: sevenDaysRunningDates(i, endDate),
|
|
28
|
-
value: Math.floor(Math.random() * 50),
|
|
29
|
-
});
|
|
30
|
-
data.push({
|
|
31
|
-
group: 'Completed',
|
|
32
|
-
key: sevenDaysRunningDates(i, endDate),
|
|
33
|
-
value: Math.floor(Math.random() * 50),
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
return data;
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const data = useMemo(() => generateRandomDataProgress(numberSequence), [numberSequence, startDate, endDate]);
|
|
40
|
-
const values = useMemo(() => generateRandomData(numberSequence), [numberSequence, startDate, endDate]);
|
|
18
|
+
const PendingPCRDNAResultsValue = useSurveillanceData(surveillanceSummary, 'getMonthlyHeiDNAPCRPending');
|
|
41
19
|
return (
|
|
42
|
-
<
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
20
|
+
<div className={styles.chart}>
|
|
21
|
+
{isLoading ? (
|
|
22
|
+
<InlineLoading status="active" iconDescription="Loading" description="Loading data..." />
|
|
23
|
+
) : PendingPCRDNAResultsValue.length > 0 ? (
|
|
24
|
+
<BaseIndicatorTrendChart
|
|
25
|
+
data={PendingPCRDNAResultsValue}
|
|
26
|
+
title={t('dnapcrPending', 'HEI (6-8 weeks) without DNA-PCR results')}
|
|
27
|
+
yAxisTitle={t('numberOfPendingPCRDNAResults', 'Number of HEI (6-8 weeks) without DNA-PCR results')}
|
|
28
|
+
/>
|
|
29
|
+
) : (
|
|
30
|
+
<EmptyState subTitle={t('noPendingPCRDNAResults', 'No pending PCR DNA results data to display')} />
|
|
31
|
+
)}
|
|
32
|
+
</div>
|
|
47
33
|
);
|
|
48
34
|
};
|
|
49
35
|
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import '@carbon/charts/styles.css';
|
|
2
|
-
import React
|
|
2
|
+
import React from 'react';
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
import BaseIndicatorTrendChart from './base-indicator-trend-chart.component';
|
|
5
|
-
import BaseProgressTrackingChart from './base-progress-tracking-chart.component';
|
|
6
|
-
import { getNumberOfDays, sevenDaysRunningDates } from '../../constants';
|
|
7
5
|
import styles from './charts.scss';
|
|
6
|
+
import useFacilityDashboardSurveillance from '../../hooks/useFacilityDashboardSurveillance';
|
|
7
|
+
import { useSurveillanceData } from '../../hooks/useSurveillanceData';
|
|
8
|
+
import EmptyState from '../empty-state/empty-state-log.components';
|
|
9
|
+
import { InlineLoading } from '@carbon/react';
|
|
8
10
|
type HEIFinalOutcomesChartProps = {
|
|
9
11
|
startDate?: Date;
|
|
10
12
|
endDate?: Date;
|
|
@@ -12,40 +14,26 @@ type HEIFinalOutcomesChartProps = {
|
|
|
12
14
|
|
|
13
15
|
const HEIFinalOutcomesChart: React.FC<HEIFinalOutcomesChartProps> = ({ startDate, endDate }) => {
|
|
14
16
|
const { t } = useTranslation();
|
|
15
|
-
const
|
|
16
|
-
return Array.from({ length: numRecords }, (_, i) => ({
|
|
17
|
-
day: sevenDaysRunningDates(i),
|
|
18
|
-
value: Math.floor(Math.random() * 50),
|
|
19
|
-
}));
|
|
20
|
-
};
|
|
17
|
+
const { error, isLoading, surveillanceSummary } = useFacilityDashboardSurveillance(startDate, endDate);
|
|
21
18
|
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
for (let i = 1; i <= numRecords; i++) {
|
|
27
|
-
data.push({
|
|
28
|
-
group: 'Pending',
|
|
29
|
-
key: sevenDaysRunningDates(i, endDate),
|
|
30
|
-
value: Math.floor(Math.random() * 50),
|
|
31
|
-
});
|
|
32
|
-
data.push({
|
|
33
|
-
group: 'Completed',
|
|
34
|
-
key: sevenDaysRunningDates(i, endDate),
|
|
35
|
-
value: Math.floor(Math.random() * 50),
|
|
36
|
-
});
|
|
37
|
-
}
|
|
38
|
-
return data;
|
|
39
|
-
};
|
|
40
|
-
|
|
41
|
-
const data = useMemo(() => generateRandomDataProgress(numberSequence), [numberSequence, startDate, endDate]);
|
|
42
|
-
const values = useMemo(() => generateRandomData(numberSequence), [numberSequence, startDate, endDate]);
|
|
19
|
+
const heiFinalOutcomesValue = useSurveillanceData(
|
|
20
|
+
surveillanceSummary,
|
|
21
|
+
'getMonthlyHei24MonthsWithoutDocumentedOutcome',
|
|
22
|
+
);
|
|
43
23
|
return (
|
|
44
|
-
<
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
24
|
+
<div className={styles.chart}>
|
|
25
|
+
{isLoading ? (
|
|
26
|
+
<InlineLoading status="active" iconDescription="Loading" description="Loading data..." />
|
|
27
|
+
) : heiFinalOutcomesValue.length > 0 ? (
|
|
28
|
+
<BaseIndicatorTrendChart
|
|
29
|
+
data={heiFinalOutcomesValue}
|
|
30
|
+
title={t('heiFinalOutcomes', 'Undocumented final outcome')}
|
|
31
|
+
yAxisTitle={t('numberHEIOutcome', 'Number of undocumented final outcome')}
|
|
32
|
+
/>
|
|
33
|
+
) : (
|
|
34
|
+
<EmptyState subTitle={t('noheiFinalOutcomes', 'No undocumented final outcome data to display')} />
|
|
35
|
+
)}
|
|
36
|
+
</div>
|
|
49
37
|
);
|
|
50
38
|
};
|
|
51
39
|
|
|
@@ -7,6 +7,7 @@ import useFacilityDashboardSurveillance from '../../hooks/useFacilityDashboardSu
|
|
|
7
7
|
import { useSurveillanceData } from '../../hooks/useSurveillanceData';
|
|
8
8
|
import EmptyState from '../empty-state/empty-state-log.components';
|
|
9
9
|
import styles from './charts.scss';
|
|
10
|
+
import { InlineLoading } from '@carbon/react';
|
|
10
11
|
type HIVPositiveNotLinkedToARTProps = {
|
|
11
12
|
startDate?: Date;
|
|
12
13
|
endDate?: Date;
|
|
@@ -24,26 +25,32 @@ const HIVPositiveNotLinkedToART: React.FC<HIVPositiveNotLinkedToARTProps> = ({ s
|
|
|
24
25
|
);
|
|
25
26
|
|
|
26
27
|
return (
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
28
|
+
<div>
|
|
29
|
+
{isLoading ? (
|
|
30
|
+
<InlineLoading status="active" iconDescription="Loading" description="Loading data..." />
|
|
31
|
+
) : (
|
|
32
|
+
<>
|
|
33
|
+
<div className={styles.chart}>
|
|
34
|
+
{hivPositivePatientValue.length > 0 ? (
|
|
35
|
+
<BaseIndicatorTrendChart
|
|
36
|
+
data={hivPositivePatientValue}
|
|
37
|
+
title={t('hivPositiveNotLinkedToART', 'HIV +VE Not linked to ART')}
|
|
38
|
+
yAxisTitle={t('numberTestedPositiveNotLinked', 'Number tested positive not linked')}
|
|
39
|
+
/>
|
|
40
|
+
) : (
|
|
41
|
+
<EmptyState subTitle={t('noHivPositiveNotLinked', 'No HIV +VE Not linked to ART data to display')} />
|
|
42
|
+
)}
|
|
43
|
+
</div>
|
|
44
|
+
<div className={styles.chart}>
|
|
45
|
+
{monthlyHivPositivePatientData.length > 0 ? (
|
|
46
|
+
<BaseArtProgressTrackingChart data={monthlyHivPositivePatientData} />
|
|
47
|
+
) : (
|
|
48
|
+
<EmptyState subTitle={'No Linkage to ART data to display'} />
|
|
49
|
+
)}
|
|
50
|
+
</div>
|
|
51
|
+
</>
|
|
52
|
+
)}
|
|
53
|
+
</div>
|
|
47
54
|
);
|
|
48
55
|
};
|
|
49
56
|
|
|
@@ -1,50 +1,34 @@
|
|
|
1
|
-
import '
|
|
2
|
-
import React, { useMemo } from 'react';
|
|
1
|
+
import React from 'react';
|
|
3
2
|
import { useTranslation } from 'react-i18next';
|
|
4
3
|
import BaseIndicatorTrendChart from './base-indicator-trend-chart.component';
|
|
5
|
-
import BaseProgressTrackingChart from './base-progress-tracking-chart.component';
|
|
6
|
-
import { getNumberOfDays, sevenDaysRunningDates } from '../../constants';
|
|
7
4
|
import styles from './charts.scss';
|
|
5
|
+
import useFacilityDashboardSurveillance from '../../hooks/useFacilityDashboardSurveillance';
|
|
6
|
+
import { useSurveillanceData } from '../../hooks/useSurveillanceData';
|
|
7
|
+
import EmptyState from '../empty-state/empty-state-log.components';
|
|
8
|
+
import { InlineLoading } from '@carbon/react';
|
|
8
9
|
type MissedOpportunityChartProps = {
|
|
9
10
|
startDate?: Date;
|
|
10
11
|
endDate?: Date;
|
|
11
12
|
};
|
|
12
13
|
const MissedOpportunityChart: React.FC<MissedOpportunityChartProps> = ({ startDate, endDate }) => {
|
|
13
14
|
const { t } = useTranslation();
|
|
14
|
-
const
|
|
15
|
-
return Array.from({ length: numRecords }, (_, i) => ({
|
|
16
|
-
day: sevenDaysRunningDates(i, endDate),
|
|
17
|
-
value: Math.floor(Math.random() * 50),
|
|
18
|
-
}));
|
|
19
|
-
};
|
|
15
|
+
const { error, isLoading, surveillanceSummary } = useFacilityDashboardSurveillance(startDate, endDate);
|
|
20
16
|
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
const generateRandomDataProgress = (numRecords: number) => {
|
|
24
|
-
const data = [];
|
|
25
|
-
for (let i = 1; i <= numRecords; i++) {
|
|
26
|
-
data.push({
|
|
27
|
-
group: 'Pending',
|
|
28
|
-
key: sevenDaysRunningDates(i, endDate),
|
|
29
|
-
value: Math.floor(Math.random() * 50),
|
|
30
|
-
});
|
|
31
|
-
data.push({
|
|
32
|
-
group: 'Completed',
|
|
33
|
-
key: sevenDaysRunningDates(i, endDate),
|
|
34
|
-
value: Math.floor(Math.random() * 50),
|
|
35
|
-
});
|
|
36
|
-
}
|
|
37
|
-
return data;
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
const data = useMemo(() => generateRandomDataProgress(numberSequence), [numberSequence, startDate, endDate]);
|
|
41
|
-
const values = useMemo(() => generateRandomData(numberSequence), [numberSequence, startDate, endDate]);
|
|
17
|
+
const missedoppotunityVLValue = useSurveillanceData(surveillanceSummary, 'getMonthlyEligibleForVlSampleNotTaken');
|
|
42
18
|
return (
|
|
43
|
-
<
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
19
|
+
<div className={styles.chart}>
|
|
20
|
+
{isLoading ? (
|
|
21
|
+
<InlineLoading status="active" iconDescription="Loading" description="Loading data..." />
|
|
22
|
+
) : missedoppotunityVLValue.length > 0 ? (
|
|
23
|
+
<BaseIndicatorTrendChart
|
|
24
|
+
data={missedoppotunityVLValue}
|
|
25
|
+
title={t('missedoppotunityVL', 'Missed opportunity in viral load testing')}
|
|
26
|
+
yAxisTitle={t('numberMissedVL', 'Number of missed opportunity VL')}
|
|
27
|
+
/>
|
|
28
|
+
) : (
|
|
29
|
+
<EmptyState subTitle={t('nomissedoppotunityVL', 'No missed opportunity VL data to display')} />
|
|
30
|
+
)}
|
|
31
|
+
</div>
|
|
48
32
|
);
|
|
49
33
|
};
|
|
50
34
|
|
|
@@ -7,6 +7,7 @@ import useFacilityDashboardSurveillance from '../../hooks/useFacilityDashboardSu
|
|
|
7
7
|
import { useSurveillanceData } from '../../hooks/useSurveillanceData';
|
|
8
8
|
import EmptyState from '../empty-state/empty-state-log.components';
|
|
9
9
|
import styles from './charts.scss';
|
|
10
|
+
import { InlineLoading } from '@carbon/react';
|
|
10
11
|
|
|
11
12
|
type PBFWNotInPrepProps = {
|
|
12
13
|
startDate?: Date;
|
|
@@ -23,26 +24,32 @@ const PBFWNotInPrep: React.FC<PBFWNotInPrepProps> = ({ startDate, endDate }) =>
|
|
|
23
24
|
);
|
|
24
25
|
|
|
25
26
|
return (
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
<
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
27
|
+
<div>
|
|
28
|
+
{isLoading ? (
|
|
29
|
+
<InlineLoading status="active" iconDescription="Loading" description="Loading data..." />
|
|
30
|
+
) : (
|
|
31
|
+
<>
|
|
32
|
+
<div className={styles.chart}>
|
|
33
|
+
{highRiskPBFWNotOnPrepValue.length > 0 ? (
|
|
34
|
+
<BaseIndicatorTrendChart
|
|
35
|
+
data={highRiskPBFWNotOnPrepValue}
|
|
36
|
+
title={t('prepNotlinked', 'High risk +ve PBFW not on PrEP')}
|
|
37
|
+
yAxisTitle={t('numberHightRiskPBFW', 'Number of High risk PBFW Not on PrEP')}
|
|
38
|
+
/>
|
|
39
|
+
) : (
|
|
40
|
+
<EmptyState subTitle={t('noHighRiskPBFW', 'No High risk PBFW Not on PrEP data to display')} />
|
|
41
|
+
)}
|
|
42
|
+
</div>
|
|
43
|
+
<div className={styles.chart}>
|
|
44
|
+
{monthlyhighRiskPBFWNotOnPrepPatientData.length > 0 ? (
|
|
45
|
+
<BaseProgressTrackingChart data={monthlyhighRiskPBFWNotOnPrepPatientData} />
|
|
46
|
+
) : (
|
|
47
|
+
<EmptyState subTitle={t('noHighRiskPBFW', 'No High risk PBFW Not on PrEP data to display')} />
|
|
48
|
+
)}
|
|
49
|
+
</div>
|
|
50
|
+
</>
|
|
51
|
+
)}
|
|
52
|
+
</div>
|
|
46
53
|
);
|
|
47
54
|
};
|
|
48
55
|
|
|
@@ -10,35 +10,55 @@ import MissedOpportunityChart from './charts/missed-opportunity-vl-chart.compone
|
|
|
10
10
|
import DNAPCRPendingCharts from './charts/dna-pcr-pending-chart.component';
|
|
11
11
|
import HEIFinalOutcomesChart from './charts/hei-final-outcome.component';
|
|
12
12
|
import { SurveillanceindicatorsFilter } from '../types';
|
|
13
|
+
import { Tabs, TabList, Tab, TabPanels, TabPanel } from '@carbon/react';
|
|
13
14
|
const SurveillancelanceDashboard = () => {
|
|
14
15
|
const { t } = useTranslation();
|
|
15
16
|
const [currFilters, setCurrFilters] = useState<SurveillanceindicatorsFilter>({
|
|
16
17
|
indicator: 'getHivPositiveNotLinked',
|
|
17
18
|
});
|
|
19
|
+
const [activeTab, setActiveTab] = useState(0);
|
|
18
20
|
|
|
19
21
|
return (
|
|
20
22
|
<div>
|
|
21
23
|
<FacilityDashboardHeader title={t('surveillance', 'Surveillance')} />
|
|
22
|
-
|
|
23
|
-
<
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
<
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
24
|
+
|
|
25
|
+
<Tabs onChange={({ selectedIndex }) => setActiveTab(selectedIndex)}>
|
|
26
|
+
<TabList>
|
|
27
|
+
<Tab>{t('kpi', 'KPI')}</Tab>
|
|
28
|
+
<Tab>{t('monitoringCharts', 'Monitoring charts')}</Tab>
|
|
29
|
+
</TabList>
|
|
30
|
+
<TabPanels>
|
|
31
|
+
<TabPanel>
|
|
32
|
+
<>
|
|
33
|
+
<SurveillanceFilters filters={currFilters} onFiltersChange={setCurrFilters} tabSelected={activeTab} />
|
|
34
|
+
<SurveillanceSummaryCards startDate={currFilters.startdate} endDate={currFilters.endDate} />
|
|
35
|
+
</>
|
|
36
|
+
</TabPanel>
|
|
37
|
+
<TabPanel>
|
|
38
|
+
<>
|
|
39
|
+
<SurveillanceFilters filters={currFilters} onFiltersChange={setCurrFilters} tabSelected={activeTab} />
|
|
40
|
+
{currFilters.indicator === 'getHivPositiveNotLinked' && (
|
|
41
|
+
<HIVPositiveNotLinkedToART startDate={currFilters.startdate} endDate={currFilters.endDate} />
|
|
42
|
+
)}
|
|
43
|
+
{currFilters.indicator === 'getPregnantPostpartumNotInPrep' && (
|
|
44
|
+
<PBFWNotInPrep startDate={currFilters.startdate} endDate={currFilters.endDate} />
|
|
45
|
+
)}
|
|
46
|
+
{currFilters.indicator === 'getEligibleForVlSampleNotTaken' && (
|
|
47
|
+
<DelayedEACCharts startDate={currFilters.startdate} endDate={currFilters.endDate} />
|
|
48
|
+
)}
|
|
49
|
+
{currFilters.indicator === 'getVirallyUnsuppressedWithoutEAC' && (
|
|
50
|
+
<MissedOpportunityChart startDate={currFilters.startdate} endDate={currFilters.endDate} />
|
|
51
|
+
)}
|
|
52
|
+
{currFilters.indicator === 'getHeiSixToEightWeeksWithoutPCRResults' && (
|
|
53
|
+
<DNAPCRPendingCharts startDate={currFilters.startdate} endDate={currFilters.endDate} />
|
|
54
|
+
)}
|
|
55
|
+
{currFilters.indicator === 'getHei24MonthsWithoutDocumentedOutcome' && (
|
|
56
|
+
<HEIFinalOutcomesChart startDate={currFilters.startdate} endDate={currFilters.endDate} />
|
|
57
|
+
)}
|
|
58
|
+
</>
|
|
59
|
+
</TabPanel>
|
|
60
|
+
</TabPanels>
|
|
61
|
+
</Tabs>
|
|
42
62
|
</div>
|
|
43
63
|
);
|
|
44
64
|
};
|
|
@@ -9,8 +9,9 @@ import { formatDatetime } from '@openmrs/esm-framework';
|
|
|
9
9
|
type Props = {
|
|
10
10
|
filters: SurveillanceindicatorsFilter;
|
|
11
11
|
onFiltersChange?: (filters: SurveillanceindicatorsFilter) => void;
|
|
12
|
+
tabSelected?: number;
|
|
12
13
|
};
|
|
13
|
-
const SurveillanceFilters: React.FC<Props> = ({ filters, onFiltersChange }) => {
|
|
14
|
+
const SurveillanceFilters: React.FC<Props> = ({ filters, onFiltersChange, tabSelected }) => {
|
|
14
15
|
const { t } = useTranslation();
|
|
15
16
|
const MaxDate: Date = today();
|
|
16
17
|
const indicators = [
|
|
@@ -23,48 +24,52 @@ const SurveillanceFilters: React.FC<Props> = ({ filters, onFiltersChange }) => {
|
|
|
23
24
|
];
|
|
24
25
|
return (
|
|
25
26
|
<div className={styles.filtersContainer}>
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
onFiltersChange
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
27
|
+
{tabSelected === 0 && (
|
|
28
|
+
<DatePicker
|
|
29
|
+
datePickerType="range"
|
|
30
|
+
minDate={formatDatetime(MaxDate)}
|
|
31
|
+
locale="en"
|
|
32
|
+
dateFormat={DATE_PICKER_CONTROL_FORMAT}
|
|
33
|
+
onChange={(dates: Array<Date>) => {
|
|
34
|
+
if (onFiltersChange) {
|
|
35
|
+
onFiltersChange({
|
|
36
|
+
...filters,
|
|
37
|
+
startdate: dates[0],
|
|
38
|
+
endDate: dates[1],
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}}>
|
|
42
|
+
<DatePickerInput
|
|
43
|
+
id="date-picker-input-id-start"
|
|
44
|
+
placeholder={DATE_PICKER_FORMAT}
|
|
45
|
+
labelText={t('startDate', 'Start date')}
|
|
46
|
+
size="md"
|
|
47
|
+
/>
|
|
48
|
+
<DatePickerInput
|
|
49
|
+
id="date-picker-input-id-finish"
|
|
50
|
+
placeholder={DATE_PICKER_FORMAT}
|
|
51
|
+
labelText={t('endDate', 'End date')}
|
|
52
|
+
size="md"
|
|
53
|
+
/>
|
|
54
|
+
</DatePicker>
|
|
55
|
+
)}
|
|
56
|
+
|
|
57
|
+
{tabSelected === 1 && (
|
|
58
|
+
<Dropdown
|
|
59
|
+
className={styles.filterInput}
|
|
60
|
+
autoAlign
|
|
61
|
+
id="filters"
|
|
62
|
+
itemToString={(item: { key: string; label: string }) =>
|
|
63
|
+
indicators.find(({ key }) => key === item.key)?.label ?? ''
|
|
38
64
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
/>
|
|
46
|
-
<DatePickerInput
|
|
47
|
-
id="date-picker-input-id-finish"
|
|
48
|
-
placeholder={DATE_PICKER_FORMAT}
|
|
49
|
-
labelText={t('endDate', 'End date')}
|
|
50
|
-
size="md"
|
|
65
|
+
items={indicators}
|
|
66
|
+
selectedItem={indicators.find(({ key }) => key === filters.indicator)}
|
|
67
|
+
label={t('indicator', 'Indicator')}
|
|
68
|
+
onChange={({ selectedItem: { key } }) => {
|
|
69
|
+
onFiltersChange({ ...filters, indicator: key });
|
|
70
|
+
}}
|
|
51
71
|
/>
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
<Dropdown
|
|
55
|
-
className={styles.filterInput}
|
|
56
|
-
autoAlign
|
|
57
|
-
id="filters"
|
|
58
|
-
itemToString={(item: { key: string; label: string }) =>
|
|
59
|
-
indicators.find(({ key }) => key === item.key)?.label ?? ''
|
|
60
|
-
}
|
|
61
|
-
items={indicators}
|
|
62
|
-
selectedItem={indicators.find(({ key }) => key === filters.indicator)}
|
|
63
|
-
label={t('indicator', 'Indicator')}
|
|
64
|
-
onChange={({ selectedItem: { key } }) => {
|
|
65
|
-
onFiltersChange({ ...filters, indicator: key });
|
|
66
|
-
}}
|
|
67
|
-
/>
|
|
72
|
+
)}
|
|
68
73
|
</div>
|
|
69
74
|
);
|
|
70
75
|
};
|
package/src/types/index.ts
CHANGED
|
@@ -17,6 +17,10 @@ export type SurveillanceSummary = {
|
|
|
17
17
|
getMonthlyHivPositiveNotLinkedPatients: HivPositiveNotLinkedData;
|
|
18
18
|
getMonthlyHighRiskPBFWNotOnPrep: HivPositiveNotLinkedData;
|
|
19
19
|
getMonthlyHighRiskPBFWNotOnPrepPatients: HivPositiveNotLinkedData;
|
|
20
|
+
getMonthlyHeiDNAPCRPending: HivPositiveNotLinkedData;
|
|
21
|
+
getMonthlyEligibleForVlSampleNotTaken: HivPositiveNotLinkedData;
|
|
22
|
+
getMonthlyHei24MonthsWithoutDocumentedOutcome: HivPositiveNotLinkedData;
|
|
23
|
+
getMonthlyVirallyUnsuppressedWithoutEAC: HivPositiveNotLinkedData;
|
|
20
24
|
};
|
|
21
25
|
|
|
22
26
|
export type IndicationMode = 'decreasing' | 'increasing';
|