@kenyaemr/esm-facility-dashboard-app 5.4.1-pre.1941 → 5.4.1-pre.1946
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 +19 -19
- package/dist/152.js +1 -1
- package/dist/152.js.map +1 -1
- package/dist/300.js +1 -1
- package/dist/90.js +2 -0
- package/dist/90.js.map +1 -0
- package/dist/kenyaemr-esm-facility-dashboard-app.js +1 -1
- package/dist/kenyaemr-esm-facility-dashboard-app.js.buildmanifest.json +37 -37
- package/dist/kenyaemr-esm-facility-dashboard-app.js.map +1 -1
- 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 +20 -0
- package/src/hooks/useFacilityDashboardSurveillance.ts +15 -10
- package/src/surveillance/charts/base-indicator-trend-chart.component.tsx +1 -1
- package/src/surveillance/charts/base-progress-tracking-chart.component.tsx +3 -3
- package/src/surveillance/charts/charts.scss +18 -0
- package/src/surveillance/charts/delayed-eac-charts.component.tsx +19 -11
- package/src/surveillance/charts/dna-pcr-pending-chart.component.tsx +19 -11
- package/src/surveillance/charts/hei-final-outcome.component.tsx +19 -11
- package/src/surveillance/charts/hiv-not-linked-to-art.component.tsx +21 -11
- package/src/surveillance/charts/missed-opportunity-vl-chart.component.tsx +17 -9
- package/src/surveillance/charts/pbfw-not-in-prep.component.tsx +19 -11
- package/src/surveillance/summary-cards/surveillance-summary-cards.component.tsx +10 -3
- package/src/surveillance/surveillance-dashboard.component.tsx +5 -2
- package/src/surveillance/surveillance-filters.component.tsx +30 -13
- package/translations/en.json +6 -5
- package/dist/889.js +0 -2
- package/dist/889.js.map +0 -1
- /package/dist/{889.js.LICENSE.txt → 90.js.LICENSE.txt} +0 -0
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.1946"}
|
package/package.json
CHANGED
package/src/constants.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import dayjs from 'dayjs';
|
|
2
|
+
|
|
1
3
|
export const moduleName = '@kenyaemr/esm-facility-dashboard-app';
|
|
2
4
|
export const etlBasePath = `${window.spaBase}`;
|
|
3
5
|
|
|
@@ -6,6 +8,12 @@ export const today = () => {
|
|
|
6
8
|
return new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
|
7
9
|
};
|
|
8
10
|
|
|
11
|
+
export const sevenDaysAgo = () => {
|
|
12
|
+
const date = today(); // Get today's date
|
|
13
|
+
date.setDate(date.getDate() - 7); // Subtract 7 days
|
|
14
|
+
return date;
|
|
15
|
+
};
|
|
16
|
+
|
|
9
17
|
export const DATE_PICKER_CONTROL_FORMAT = 'd/m/Y';
|
|
10
18
|
|
|
11
19
|
export const DATE_PICKER_FORMAT = 'DD/MM/YYYY';
|
|
@@ -13,3 +21,15 @@ export const DATE_PICKER_FORMAT = 'DD/MM/YYYY';
|
|
|
13
21
|
export const formatNewDate = (date: Date | null | undefined) => {
|
|
14
22
|
return date ? new Date(date) : '';
|
|
15
23
|
};
|
|
24
|
+
|
|
25
|
+
// Generate Dammy dates
|
|
26
|
+
export const sevenDaysRunningDates = (index: number) => {
|
|
27
|
+
const date = new Date(today());
|
|
28
|
+
date.setDate(today().getDate() - index);
|
|
29
|
+
const formattedDate = date.toISOString().split('T')[0];
|
|
30
|
+
return formattedDate;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
export const formattedDate = (date: Date) => {
|
|
34
|
+
return date ? dayjs(date).format(DATE_PICKER_FORMAT) : '';
|
|
35
|
+
};
|
|
@@ -1,20 +1,25 @@
|
|
|
1
|
-
import { FetchResponse, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
|
|
1
|
+
import { FetchResponse, formatDatetime, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
|
|
2
2
|
import useSWR from 'swr';
|
|
3
3
|
import { IndicationMode, type SurveillanceSummary } from '../types';
|
|
4
4
|
import { useCallback } from 'react';
|
|
5
|
+
import { formattedDate, sevenDaysAgo, today } from '../constants';
|
|
6
|
+
|
|
7
|
+
const useFacilityDashboardSurveillance = (startDate?: Date, endDate?: Date) => {
|
|
8
|
+
const url =
|
|
9
|
+
startDate && endDate
|
|
10
|
+
? `${restBaseUrl}/kenyaemr/facility-dashboard?startDate=${formattedDate(startDate)}&endDate=${formattedDate(
|
|
11
|
+
endDate,
|
|
12
|
+
)}`
|
|
13
|
+
: `${restBaseUrl}/kenyaemr/facility-dashboard?startDate=${formattedDate(today())}&endDate=${formattedDate(
|
|
14
|
+
sevenDaysAgo(),
|
|
15
|
+
)}`;
|
|
5
16
|
|
|
6
|
-
const useFacilityDashboardSurveillance = () => {
|
|
7
|
-
const url = `${restBaseUrl}/kenyaemr/facility-dashboard`;
|
|
8
17
|
const { data, error, isLoading, mutate } = useSWR<FetchResponse<SurveillanceSummary>>(url, openmrsFetch);
|
|
9
18
|
const getIndication = useCallback((indicator: number, denominator: number, threshold: number): IndicationMode => {
|
|
10
|
-
if (denominator === 0) {
|
|
11
|
-
return 'decreasing';
|
|
12
|
-
}
|
|
13
|
-
if (indicator > denominator * threshold) {
|
|
14
|
-
return 'increasing';
|
|
15
|
-
} else {
|
|
19
|
+
if (denominator === 0 || indicator <= denominator * threshold) {
|
|
16
20
|
return 'decreasing';
|
|
17
21
|
}
|
|
22
|
+
return 'increasing';
|
|
18
23
|
}, []);
|
|
19
24
|
const getPercentage = useCallback((indicator: number, denominator: number): string => {
|
|
20
25
|
if (indicator === null || indicator === undefined || denominator === null || denominator === undefined) {
|
|
@@ -28,7 +33,7 @@ const useFacilityDashboardSurveillance = () => {
|
|
|
28
33
|
}, []);
|
|
29
34
|
|
|
30
35
|
return {
|
|
31
|
-
surveillanceSummary: data?.data,
|
|
36
|
+
surveillanceSummary: error ? null : data?.data,
|
|
32
37
|
getIndication,
|
|
33
38
|
getPercentage,
|
|
34
39
|
isLoading,
|
|
@@ -29,7 +29,7 @@ const BaseIndicatorTrendChart: React.FC<Props> = ({ data, title, yAxisTitle }) =
|
|
|
29
29
|
mapsTo: 'abnomallPercentage',
|
|
30
30
|
},
|
|
31
31
|
bottom: {
|
|
32
|
-
title: t('
|
|
32
|
+
title: t('durationInDays', 'Duration in days'),
|
|
33
33
|
scaleType: ScaleTypes.LABELS,
|
|
34
34
|
mapsTo: 'week',
|
|
35
35
|
},
|
|
@@ -18,7 +18,7 @@ const BaseProgressTrackingChart: React.FC<Props> = ({ data }) => {
|
|
|
18
18
|
},
|
|
19
19
|
axes: {
|
|
20
20
|
bottom: {
|
|
21
|
-
title: t('
|
|
21
|
+
title: t('days', 'Days'),
|
|
22
22
|
mapsTo: 'key',
|
|
23
23
|
scaleType: ScaleTypes.LABELS,
|
|
24
24
|
},
|
|
@@ -31,8 +31,8 @@ const BaseProgressTrackingChart: React.FC<Props> = ({ data }) => {
|
|
|
31
31
|
},
|
|
32
32
|
color: {
|
|
33
33
|
scale: {
|
|
34
|
-
Pending: '#
|
|
35
|
-
Completed: '#
|
|
34
|
+
Pending: '#0000ff',
|
|
35
|
+
Completed: '#ff0000',
|
|
36
36
|
},
|
|
37
37
|
},
|
|
38
38
|
height: '400px',
|
|
@@ -5,3 +5,21 @@
|
|
|
5
5
|
.chartContainer {
|
|
6
6
|
padding: layout.$spacing-05;
|
|
7
7
|
}
|
|
8
|
+
|
|
9
|
+
.chartGroupContainer {
|
|
10
|
+
display: flex !important;
|
|
11
|
+
flex-flow: row wrap !important;
|
|
12
|
+
}
|
|
13
|
+
.tendChart {
|
|
14
|
+
flex: 50%;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
.trackingChart {
|
|
18
|
+
flex: 50;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
@media (max-width: 800px) {
|
|
22
|
+
.chartGroupContainer {
|
|
23
|
+
flex-direction: column;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -3,12 +3,14 @@ import React, { useMemo } from 'react';
|
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
import BaseIndicatorTrendChart from './base-indicator-trend-chart.component';
|
|
5
5
|
import BaseProgressTrackingChart from './base-progress-tracking-chart.component';
|
|
6
|
+
import { sevenDaysRunningDates } from '../../constants';
|
|
7
|
+
import styles from './charts.scss';
|
|
6
8
|
|
|
7
9
|
const DelayedEACCharts = () => {
|
|
8
10
|
const { t } = useTranslation();
|
|
9
11
|
const generateRandomData = (numRecords: number) => {
|
|
10
12
|
return Array.from({ length: numRecords }, (_, i) => ({
|
|
11
|
-
week:
|
|
13
|
+
week: sevenDaysRunningDates(i),
|
|
12
14
|
abnomallPercentage: Math.floor(Math.random() * 50),
|
|
13
15
|
}));
|
|
14
16
|
};
|
|
@@ -18,29 +20,35 @@ const DelayedEACCharts = () => {
|
|
|
18
20
|
for (let i = 1; i <= numRecords; i++) {
|
|
19
21
|
data.push({
|
|
20
22
|
group: 'Pending',
|
|
21
|
-
key:
|
|
23
|
+
key: sevenDaysRunningDates(i),
|
|
22
24
|
value: Math.floor(Math.random() * 50),
|
|
23
25
|
});
|
|
24
26
|
data.push({
|
|
25
27
|
group: 'Completed',
|
|
26
|
-
key:
|
|
28
|
+
key: sevenDaysRunningDates(i),
|
|
27
29
|
value: Math.floor(Math.random() * 50),
|
|
28
30
|
});
|
|
29
31
|
}
|
|
30
32
|
return data;
|
|
31
33
|
};
|
|
32
34
|
|
|
33
|
-
const data = useMemo(() => generateRandomDataProgress(
|
|
35
|
+
const data = useMemo(() => generateRandomDataProgress(7), []);
|
|
34
36
|
|
|
35
|
-
const values = useMemo(() => generateRandomData(
|
|
37
|
+
const values = useMemo(() => generateRandomData(7), []);
|
|
36
38
|
return (
|
|
37
39
|
<>
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
<div className={styles.chartGroupContainer}>
|
|
41
|
+
<div className={styles.tendChart}>
|
|
42
|
+
<BaseIndicatorTrendChart
|
|
43
|
+
data={values}
|
|
44
|
+
title={t('delayedEAC', 'Delayed EAC')}
|
|
45
|
+
yAxisTitle={t('percentageDelatedEAC', '% Delayed EAC')}
|
|
46
|
+
/>
|
|
47
|
+
</div>
|
|
48
|
+
<div className={styles.trackingChart}>
|
|
49
|
+
<BaseProgressTrackingChart data={data} />
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
44
52
|
</>
|
|
45
53
|
);
|
|
46
54
|
};
|
|
@@ -3,12 +3,14 @@ import React, { useMemo } from 'react';
|
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
import BaseIndicatorTrendChart from './base-indicator-trend-chart.component';
|
|
5
5
|
import BaseProgressTrackingChart from './base-progress-tracking-chart.component';
|
|
6
|
+
import { sevenDaysRunningDates } from '../../constants';
|
|
7
|
+
import styles from './charts.scss';
|
|
6
8
|
|
|
7
9
|
const DNAPCRPendingCharts = () => {
|
|
8
10
|
const { t } = useTranslation();
|
|
9
11
|
const generateRandomData = (numRecords: number) => {
|
|
10
12
|
return Array.from({ length: numRecords }, (_, i) => ({
|
|
11
|
-
week:
|
|
13
|
+
week: sevenDaysRunningDates(i),
|
|
12
14
|
abnomallPercentage: Math.floor(Math.random() * 50),
|
|
13
15
|
}));
|
|
14
16
|
};
|
|
@@ -18,29 +20,35 @@ const DNAPCRPendingCharts = () => {
|
|
|
18
20
|
for (let i = 1; i <= numRecords; i++) {
|
|
19
21
|
data.push({
|
|
20
22
|
group: 'Pending',
|
|
21
|
-
key:
|
|
23
|
+
key: sevenDaysRunningDates(i),
|
|
22
24
|
value: Math.floor(Math.random() * 50),
|
|
23
25
|
});
|
|
24
26
|
data.push({
|
|
25
27
|
group: 'Completed',
|
|
26
|
-
key:
|
|
28
|
+
key: sevenDaysRunningDates(i),
|
|
27
29
|
value: Math.floor(Math.random() * 50),
|
|
28
30
|
});
|
|
29
31
|
}
|
|
30
32
|
return data;
|
|
31
33
|
};
|
|
32
34
|
|
|
33
|
-
const data = useMemo(() => generateRandomDataProgress(
|
|
35
|
+
const data = useMemo(() => generateRandomDataProgress(7), []);
|
|
34
36
|
|
|
35
|
-
const values = useMemo(() => generateRandomData(
|
|
37
|
+
const values = useMemo(() => generateRandomData(7), []);
|
|
36
38
|
return (
|
|
37
39
|
<>
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
<div className={styles.chartGroupContainer}>
|
|
41
|
+
<div className={styles.tendChart}>
|
|
42
|
+
<BaseIndicatorTrendChart
|
|
43
|
+
data={values}
|
|
44
|
+
title={t('dnapcrPending', 'Pending DNA-PCR Results')}
|
|
45
|
+
yAxisTitle={t('percentagePendingPCRDNAResults', '% of pending PCR-DNA Results')}
|
|
46
|
+
/>
|
|
47
|
+
</div>
|
|
48
|
+
<div className={styles.trackingChart}>
|
|
49
|
+
<BaseProgressTrackingChart data={data} />
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
44
52
|
</>
|
|
45
53
|
);
|
|
46
54
|
};
|
|
@@ -3,12 +3,14 @@ import React, { useMemo } from 'react';
|
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
import BaseIndicatorTrendChart from './base-indicator-trend-chart.component';
|
|
5
5
|
import BaseProgressTrackingChart from './base-progress-tracking-chart.component';
|
|
6
|
+
import { sevenDaysRunningDates } from '../../constants';
|
|
7
|
+
import styles from './charts.scss';
|
|
6
8
|
|
|
7
9
|
const HEIFinalOutcomesChart = () => {
|
|
8
10
|
const { t } = useTranslation();
|
|
9
11
|
const generateRandomData = (numRecords: number) => {
|
|
10
12
|
return Array.from({ length: numRecords }, (_, i) => ({
|
|
11
|
-
week:
|
|
13
|
+
week: sevenDaysRunningDates(i),
|
|
12
14
|
abnomallPercentage: Math.floor(Math.random() * 50),
|
|
13
15
|
}));
|
|
14
16
|
};
|
|
@@ -18,29 +20,35 @@ const HEIFinalOutcomesChart = () => {
|
|
|
18
20
|
for (let i = 1; i <= numRecords; i++) {
|
|
19
21
|
data.push({
|
|
20
22
|
group: 'Pending',
|
|
21
|
-
key:
|
|
23
|
+
key: sevenDaysRunningDates(i),
|
|
22
24
|
value: Math.floor(Math.random() * 50),
|
|
23
25
|
});
|
|
24
26
|
data.push({
|
|
25
27
|
group: 'Completed',
|
|
26
|
-
key:
|
|
28
|
+
key: sevenDaysRunningDates(i),
|
|
27
29
|
value: Math.floor(Math.random() * 50),
|
|
28
30
|
});
|
|
29
31
|
}
|
|
30
32
|
return data;
|
|
31
33
|
};
|
|
32
34
|
|
|
33
|
-
const data = useMemo(() => generateRandomDataProgress(
|
|
35
|
+
const data = useMemo(() => generateRandomDataProgress(7), []);
|
|
34
36
|
|
|
35
|
-
const values = useMemo(() => generateRandomData(
|
|
37
|
+
const values = useMemo(() => generateRandomData(7), []);
|
|
36
38
|
return (
|
|
37
39
|
<>
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
<div className={styles.chartGroupContainer}>
|
|
41
|
+
<div className={styles.tendChart}>
|
|
42
|
+
<BaseIndicatorTrendChart
|
|
43
|
+
data={values}
|
|
44
|
+
title={t('heiFinalOutcomes', 'HEI Final Outcomes')}
|
|
45
|
+
yAxisTitle={t('percentageHEIOutcome', '% 24 month old HEI without documented outcomes')}
|
|
46
|
+
/>
|
|
47
|
+
</div>
|
|
48
|
+
<div className={styles.trackingChart}>
|
|
49
|
+
<BaseProgressTrackingChart data={data} />
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
44
52
|
</>
|
|
45
53
|
);
|
|
46
54
|
};
|
|
@@ -3,11 +3,14 @@ import React, { useMemo } from 'react';
|
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
import BaseIndicatorTrendChart from './base-indicator-trend-chart.component';
|
|
5
5
|
import BaseProgressTrackingChart from './base-progress-tracking-chart.component';
|
|
6
|
+
import { sevenDaysRunningDates } from '../../constants';
|
|
7
|
+
import styles from './charts.scss';
|
|
6
8
|
const HIVPositiveNotLinkedToART = () => {
|
|
7
9
|
const { t } = useTranslation();
|
|
10
|
+
|
|
8
11
|
const generateRandomData = (numRecords: number) => {
|
|
9
12
|
return Array.from({ length: numRecords }, (_, i) => ({
|
|
10
|
-
week:
|
|
13
|
+
week: sevenDaysRunningDates(i),
|
|
11
14
|
abnomallPercentage: Math.floor(Math.random() * 50),
|
|
12
15
|
}));
|
|
13
16
|
};
|
|
@@ -15,31 +18,38 @@ const HIVPositiveNotLinkedToART = () => {
|
|
|
15
18
|
const generateRandomDataProgress = (numRecords: number) => {
|
|
16
19
|
const data = [];
|
|
17
20
|
for (let i = 1; i <= numRecords; i++) {
|
|
21
|
+
const formattedDate = sevenDaysRunningDates(i);
|
|
18
22
|
data.push({
|
|
19
23
|
group: 'Pending',
|
|
20
|
-
key:
|
|
24
|
+
key: formattedDate,
|
|
21
25
|
value: Math.floor(Math.random() * 50),
|
|
22
26
|
});
|
|
23
27
|
data.push({
|
|
24
28
|
group: 'Completed',
|
|
25
|
-
key:
|
|
29
|
+
key: formattedDate,
|
|
26
30
|
value: Math.floor(Math.random() * 50),
|
|
27
31
|
});
|
|
28
32
|
}
|
|
29
33
|
return data;
|
|
30
34
|
};
|
|
31
35
|
|
|
32
|
-
const data = useMemo(() => generateRandomDataProgress(
|
|
36
|
+
const data = useMemo(() => generateRandomDataProgress(7), []);
|
|
33
37
|
|
|
34
|
-
const values = useMemo(() => generateRandomData(
|
|
38
|
+
const values = useMemo(() => generateRandomData(7), []);
|
|
35
39
|
return (
|
|
36
40
|
<>
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
41
|
+
<div className={styles.chartGroupContainer}>
|
|
42
|
+
<div className={styles.tendChart}>
|
|
43
|
+
<BaseIndicatorTrendChart
|
|
44
|
+
data={values}
|
|
45
|
+
title={t('hivPositiveNotLinkedToART', 'HIV +VE Not linked to ART')}
|
|
46
|
+
yAxisTitle={t('percentageTestedPositiveNotLinked', '% tested positive not linked')}
|
|
47
|
+
/>
|
|
48
|
+
</div>
|
|
49
|
+
<div className={styles.trackingChart}>
|
|
50
|
+
<BaseProgressTrackingChart data={data} />
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
43
53
|
</>
|
|
44
54
|
);
|
|
45
55
|
};
|
|
@@ -3,12 +3,14 @@ import React, { useMemo } from 'react';
|
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
import BaseIndicatorTrendChart from './base-indicator-trend-chart.component';
|
|
5
5
|
import BaseProgressTrackingChart from './base-progress-tracking-chart.component';
|
|
6
|
+
import { sevenDaysRunningDates } from '../../constants';
|
|
7
|
+
import styles from './charts.scss';
|
|
6
8
|
|
|
7
9
|
const MissedOpportunityChart = () => {
|
|
8
10
|
const { t } = useTranslation();
|
|
9
11
|
const generateRandomData = (numRecords: number) => {
|
|
10
12
|
return Array.from({ length: numRecords }, (_, i) => ({
|
|
11
|
-
week:
|
|
13
|
+
week: sevenDaysRunningDates(i),
|
|
12
14
|
abnomallPercentage: Math.floor(Math.random() * 50),
|
|
13
15
|
}));
|
|
14
16
|
};
|
|
@@ -18,12 +20,12 @@ const MissedOpportunityChart = () => {
|
|
|
18
20
|
for (let i = 1; i <= numRecords; i++) {
|
|
19
21
|
data.push({
|
|
20
22
|
group: 'Pending',
|
|
21
|
-
key:
|
|
23
|
+
key: sevenDaysRunningDates(i),
|
|
22
24
|
value: Math.floor(Math.random() * 50),
|
|
23
25
|
});
|
|
24
26
|
data.push({
|
|
25
27
|
group: 'Completed',
|
|
26
|
-
key:
|
|
28
|
+
key: sevenDaysRunningDates(i),
|
|
27
29
|
value: Math.floor(Math.random() * 50),
|
|
28
30
|
});
|
|
29
31
|
}
|
|
@@ -35,12 +37,18 @@ const MissedOpportunityChart = () => {
|
|
|
35
37
|
const values = useMemo(() => generateRandomData(40), []);
|
|
36
38
|
return (
|
|
37
39
|
<>
|
|
38
|
-
<
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
40
|
+
<div className={styles.chartGroupContainer}>
|
|
41
|
+
<div className={styles.tendChart}>
|
|
42
|
+
<BaseIndicatorTrendChart
|
|
43
|
+
data={values}
|
|
44
|
+
title={t('missedoppotunityVL', 'Missed opportunity VL')}
|
|
45
|
+
yAxisTitle={t('percentageMissedVL', '% of missed opportunity VL')}
|
|
46
|
+
/>
|
|
47
|
+
</div>
|
|
48
|
+
<div className={styles.trackingChart}>
|
|
49
|
+
<BaseProgressTrackingChart data={data} />
|
|
50
|
+
</div>
|
|
51
|
+
</div>
|
|
44
52
|
</>
|
|
45
53
|
);
|
|
46
54
|
};
|
|
@@ -3,11 +3,13 @@ import React, { useMemo } from 'react';
|
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
import BaseIndicatorTrendChart from './base-indicator-trend-chart.component';
|
|
5
5
|
import BaseProgressTrackingChart from './base-progress-tracking-chart.component';
|
|
6
|
+
import { sevenDaysRunningDates } from '../../constants';
|
|
7
|
+
import styles from './charts.scss';
|
|
6
8
|
const PBFWNotInPrep = () => {
|
|
7
9
|
const { t } = useTranslation();
|
|
8
10
|
const generateRandomData = (numRecords: number) => {
|
|
9
11
|
return Array.from({ length: numRecords }, (_, i) => ({
|
|
10
|
-
week:
|
|
12
|
+
week: sevenDaysRunningDates(i),
|
|
11
13
|
abnomallPercentage: Math.floor(Math.random() * 50),
|
|
12
14
|
}));
|
|
13
15
|
};
|
|
@@ -17,29 +19,35 @@ const PBFWNotInPrep = () => {
|
|
|
17
19
|
for (let i = 1; i <= numRecords; i++) {
|
|
18
20
|
data.push({
|
|
19
21
|
group: 'Pending',
|
|
20
|
-
key:
|
|
22
|
+
key: sevenDaysRunningDates(i),
|
|
21
23
|
value: Math.floor(Math.random() * 50),
|
|
22
24
|
});
|
|
23
25
|
data.push({
|
|
24
26
|
group: 'Completed',
|
|
25
|
-
key:
|
|
27
|
+
key: sevenDaysRunningDates(i),
|
|
26
28
|
value: Math.floor(Math.random() * 50),
|
|
27
29
|
});
|
|
28
30
|
}
|
|
29
31
|
return data;
|
|
30
32
|
};
|
|
31
33
|
|
|
32
|
-
const data = useMemo(() => generateRandomDataForProgress(
|
|
34
|
+
const data = useMemo(() => generateRandomDataForProgress(7), []);
|
|
33
35
|
|
|
34
|
-
const values = useMemo(() => generateRandomData(
|
|
36
|
+
const values = useMemo(() => generateRandomData(7), []);
|
|
35
37
|
return (
|
|
36
38
|
<>
|
|
37
|
-
<
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
39
|
+
<div className={styles.chartGroupContainer}>
|
|
40
|
+
<div className={styles.tendChart}>
|
|
41
|
+
<BaseIndicatorTrendChart
|
|
42
|
+
data={values}
|
|
43
|
+
title={t('prepNotlinked', 'High risk +ve PBFW not on PrEP')}
|
|
44
|
+
yAxisTitle={t('percentageHightRiskPBFW', '% High risk PBFW Not in PrEP')}
|
|
45
|
+
/>
|
|
46
|
+
</div>
|
|
47
|
+
<div className={styles.trackingChart}>
|
|
48
|
+
<BaseProgressTrackingChart data={data} />
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
43
51
|
</>
|
|
44
52
|
);
|
|
45
53
|
};
|
|
@@ -5,9 +5,16 @@ import { useTranslation } from 'react-i18next';
|
|
|
5
5
|
import useFacilityDashboardSurveillance from '../../hooks/useFacilityDashboardSurveillance';
|
|
6
6
|
import SummaryCard from './summary-card.component';
|
|
7
7
|
import styles from './summary-card.scss';
|
|
8
|
-
|
|
8
|
+
type SurveillanceSummaryCardsProps = {
|
|
9
|
+
startDate?: Date;
|
|
10
|
+
endDate?: Date;
|
|
11
|
+
};
|
|
12
|
+
const SurveillanceSummaryCards: React.FC<SurveillanceSummaryCardsProps> = ({ startDate, endDate }) => {
|
|
9
13
|
const { t } = useTranslation();
|
|
10
|
-
const { error, isLoading, surveillanceSummary, getIndication, getPercentage } = useFacilityDashboardSurveillance(
|
|
14
|
+
const { error, isLoading, surveillanceSummary, getIndication, getPercentage } = useFacilityDashboardSurveillance(
|
|
15
|
+
startDate,
|
|
16
|
+
endDate,
|
|
17
|
+
);
|
|
11
18
|
|
|
12
19
|
if (isLoading) {
|
|
13
20
|
return (
|
|
@@ -40,7 +47,7 @@ const SurveillanceSummaryCards = () => {
|
|
|
40
47
|
)}
|
|
41
48
|
/>
|
|
42
49
|
<SummaryCard
|
|
43
|
-
header={t('prepNotlinked', 'High risk
|
|
50
|
+
header={t('prepNotlinked', 'High risk -ve PBFW not on PrEP')}
|
|
44
51
|
title={t('pbfwNotLinked', 'High risk -ve PBFW Not linked to PrEP')}
|
|
45
52
|
value={`${surveillanceSummary?.getPregnantPostpartumNotInPrep}/${surveillanceSummary?.getPregnantOrPostpartumClients}`}
|
|
46
53
|
percentage={getPercentage(
|
|
@@ -12,12 +12,15 @@ import HEIFinalOutcomesChart from './charts/hei-final-outcome.component';
|
|
|
12
12
|
import { SurveillanceindicatorsFilter } from '../types';
|
|
13
13
|
const SurveillancelanceDashboard = () => {
|
|
14
14
|
const { t } = useTranslation();
|
|
15
|
-
const [currFilters, setCurrFilters] = useState<SurveillanceindicatorsFilter>({
|
|
15
|
+
const [currFilters, setCurrFilters] = useState<SurveillanceindicatorsFilter>({
|
|
16
|
+
indicator: 'getHivPositiveNotLinked',
|
|
17
|
+
});
|
|
18
|
+
|
|
16
19
|
return (
|
|
17
20
|
<div>
|
|
18
21
|
<FacilityDashboardHeader title={t('surveillance', 'Surveillance')} />
|
|
19
22
|
<SurveillanceFilters filters={currFilters} onFiltersChange={setCurrFilters} />
|
|
20
|
-
<SurveillanceSummaryCards />
|
|
23
|
+
<SurveillanceSummaryCards startDate={currFilters.startdate} endDate={currFilters.endDate} />
|
|
21
24
|
{currFilters.indicator === 'getHivPositiveNotLinked' && <HIVPositiveNotLinkedToART />}
|
|
22
25
|
{currFilters.indicator === 'getPregnantPostpartumNotInPrep' && <PBFWNotInPrep />}
|
|
23
26
|
{currFilters.indicator === 'getEligibleForVlSampleNotTaken' && <DelayedEACCharts />}
|
|
@@ -3,6 +3,8 @@ import React from 'react';
|
|
|
3
3
|
import { useTranslation } from 'react-i18next';
|
|
4
4
|
import { SurveillanceindicatorsFilter } from '../types';
|
|
5
5
|
import styles from './surveillance.scss';
|
|
6
|
+
import { DATE_PICKER_CONTROL_FORMAT, DATE_PICKER_FORMAT, today } from '../constants';
|
|
7
|
+
import { formatDatetime } from '@openmrs/esm-framework';
|
|
6
8
|
|
|
7
9
|
type Props = {
|
|
8
10
|
filters: SurveillanceindicatorsFilter;
|
|
@@ -10,7 +12,7 @@ type Props = {
|
|
|
10
12
|
};
|
|
11
13
|
const SurveillanceFilters: React.FC<Props> = ({ filters, onFiltersChange }) => {
|
|
12
14
|
const { t } = useTranslation();
|
|
13
|
-
const
|
|
15
|
+
const MaxDate: Date = today();
|
|
14
16
|
const indicators = [
|
|
15
17
|
{ key: 'getHivPositiveNotLinked', label: 'HIV +ve not linked' },
|
|
16
18
|
{ key: 'getPregnantPostpartumNotInPrep', label: 'High risk +ve PBFW not on PrEP' },
|
|
@@ -21,19 +23,34 @@ const SurveillanceFilters: React.FC<Props> = ({ filters, onFiltersChange }) => {
|
|
|
21
23
|
];
|
|
22
24
|
return (
|
|
23
25
|
<div className={styles.filtersContainer}>
|
|
24
|
-
<DatePicker
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
<DatePicker
|
|
27
|
+
datePickerType="range"
|
|
28
|
+
minDate={formatDatetime(MaxDate)}
|
|
29
|
+
locale="en"
|
|
30
|
+
dateFormat={DATE_PICKER_CONTROL_FORMAT}
|
|
31
|
+
onChange={(dates: Array<Date>) => {
|
|
32
|
+
if (onFiltersChange) {
|
|
33
|
+
onFiltersChange({
|
|
34
|
+
...filters,
|
|
35
|
+
startdate: dates[0],
|
|
36
|
+
endDate: dates[1],
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
}}>
|
|
40
|
+
<DatePickerInput
|
|
41
|
+
id="date-picker-input-id-start"
|
|
42
|
+
placeholder={DATE_PICKER_FORMAT}
|
|
43
|
+
labelText={t('startDate', 'Start date')}
|
|
44
|
+
size="md"
|
|
45
|
+
/>
|
|
46
|
+
<DatePickerInput
|
|
47
|
+
id="date-picker-input-id-finish"
|
|
48
|
+
placeholder={DATE_PICKER_FORMAT}
|
|
49
|
+
labelText={t('endDate', 'End date')}
|
|
50
|
+
size="md"
|
|
51
|
+
/>
|
|
27
52
|
</DatePicker>
|
|
28
|
-
|
|
29
|
-
className={styles.filterInput}
|
|
30
|
-
autoAlign
|
|
31
|
-
id="filters"
|
|
32
|
-
itemToString={(item) => item?.label ?? ''}
|
|
33
|
-
items={reportingPeriods}
|
|
34
|
-
selectedItem={reportingPeriods[0]}
|
|
35
|
-
label={t('reportingPeriod', 'Reporting Period')}
|
|
36
|
-
/>
|
|
53
|
+
|
|
37
54
|
<Dropdown
|
|
38
55
|
className={styles.filterInput}
|
|
39
56
|
autoAlign
|
package/translations/en.json
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"aboveSiteDashboard": "Above site facility Dashboard",
|
|
3
3
|
"aboveSiteFacilityDashboard": "Above site facility Dashboard",
|
|
4
|
+
"days": "Days",
|
|
4
5
|
"delayedEAC": "Delayed EAC",
|
|
5
6
|
"delayedVLTesting": "Number of client on ART that visited, were eligible for VL sampling and no VL was done",
|
|
6
7
|
"dnapcrPending": "DNA-PCR Pending",
|
|
7
|
-
"
|
|
8
|
+
"durationInDays": "Duration in days",
|
|
9
|
+
"endDate": "End date",
|
|
8
10
|
"facilitydashboard": "Facility Dashboard",
|
|
9
11
|
"facilitydashboardLeftPannel": "facility Dashboard Left Panel",
|
|
10
12
|
"heiFinalOutcomes": "HEI Final Outcomes",
|
|
@@ -23,12 +25,11 @@
|
|
|
23
25
|
"percentageMissedVL": "% of missed opportunity VL",
|
|
24
26
|
"percentagePendingPCRDNAResults": "% of pending PCR-DNA Results",
|
|
25
27
|
"percentageTestedPositiveNotLinked": "% tested positive not linked",
|
|
26
|
-
"prepNotlinked": "High risk
|
|
28
|
+
"prepNotlinked": "High risk -ve PBFW not on PrEP",
|
|
27
29
|
"progresstracking": "Progress tracking",
|
|
28
|
-
"
|
|
30
|
+
"startDate": "Start date",
|
|
29
31
|
"surveillance": "Surveillance",
|
|
30
32
|
"surveillanceSummary": "Surveillance Summary",
|
|
31
33
|
"viewOnSuperset": "View on Superset",
|
|
32
|
-
"virallyUnSuppressedWithoutAdherenceCounselingFor2Weeks": "Virally Unsuppressed clients without enhanced adherence counseling (EAC) within 2 weeks"
|
|
33
|
-
"weeks": "Weeks"
|
|
34
|
+
"virallyUnSuppressedWithoutAdherenceCounselingFor2Weeks": "Virally Unsuppressed clients without enhanced adherence counseling (EAC) within 2 weeks"
|
|
34
35
|
}
|