@kenyaemr/esm-adr-app 5.4.2-pre.2257
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 +32 -0
- package/dist/144.js +2 -0
- package/dist/144.js.LICENSE.txt +19 -0
- package/dist/144.js.map +1 -0
- package/dist/216.js +2 -0
- package/dist/216.js.LICENSE.txt +9 -0
- package/dist/216.js.map +1 -0
- package/dist/218.js +2 -0
- package/dist/218.js.LICENSE.txt +5 -0
- package/dist/218.js.map +1 -0
- package/dist/300.js +1 -0
- package/dist/389.js +1 -0
- package/dist/389.js.map +1 -0
- package/dist/405.js +2 -0
- package/dist/405.js.LICENSE.txt +5 -0
- package/dist/405.js.map +1 -0
- package/dist/41.js +2 -0
- package/dist/41.js.LICENSE.txt +9 -0
- package/dist/41.js.map +1 -0
- package/dist/410.js +1 -0
- package/dist/410.js.map +1 -0
- package/dist/470.js +1 -0
- package/dist/470.js.map +1 -0
- package/dist/495.js +1 -0
- package/dist/495.js.map +1 -0
- package/dist/537.js +1 -0
- package/dist/537.js.map +1 -0
- package/dist/562.js +2 -0
- package/dist/562.js.LICENSE.txt +5 -0
- package/dist/562.js.map +1 -0
- package/dist/780.js +1 -0
- package/dist/780.js.map +1 -0
- package/dist/837.js +2 -0
- package/dist/837.js.LICENSE.txt +29 -0
- package/dist/837.js.map +1 -0
- package/dist/876.js +1 -0
- package/dist/876.js.map +1 -0
- package/dist/89.js +1 -0
- package/dist/89.js.map +1 -0
- package/dist/913.js +2 -0
- package/dist/913.js.LICENSE.txt +32 -0
- package/dist/913.js.map +1 -0
- package/dist/914.js +1 -0
- package/dist/914.js.map +1 -0
- package/dist/917.js +2 -0
- package/dist/917.js.LICENSE.txt +48 -0
- package/dist/917.js.map +1 -0
- package/dist/kenyaemr-esm-adr-app.js +1 -0
- package/dist/kenyaemr-esm-adr-app.js.buildmanifest.json +602 -0
- package/dist/kenyaemr-esm-adr-app.js.map +1 -0
- package/dist/main.js +2 -0
- package/dist/main.js.LICENSE.txt +15 -0
- package/dist/main.js.map +1 -0
- package/dist/routes.json +1 -0
- package/jest.config.js +8 -0
- package/package.json +52 -0
- package/src/components/dashboard/dashboard-view.component.tsx +17 -0
- package/src/components/dashboard/dashboard-view.scss +5 -0
- package/src/components/dashboard/home-dashboard.component.tsx +30 -0
- package/src/components/dashboard/home-dashboard.scss +28 -0
- package/src/components/encounters/adr-encounter.component.tsx +165 -0
- package/src/components/encounters/encounter.resource.tsx +13 -0
- package/src/components/encounters/encounter.scss +26 -0
- package/src/components/header/header.component.tsx +8 -0
- package/src/components/patient-adr.workspace.tsx +79 -0
- package/src/components/side-menu/side-menu.component.tsx +6 -0
- package/src/components/summary/summary.component.tsx +84 -0
- package/src/components/summary/summary.scss +35 -0
- package/src/config-schema.ts +1 -0
- package/src/declarations.d.ts +2 -0
- package/src/index.ts +30 -0
- package/src/root.component.tsx +27 -0
- package/src/routes.json +47 -0
- package/src/types/index.ts +21 -0
- package/translations/en.json +12 -0
- package/tsconfig.json +5 -0
- package/webpack.config.js +1 -0
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import React, { useMemo, useCallback } from 'react';
|
|
2
|
+
import { useTranslation } from 'react-i18next';
|
|
3
|
+
import { InlineLoading, InlineNotification } from '@carbon/react';
|
|
4
|
+
import { DefaultWorkspaceProps, ExtensionSlot, usePatient } from '@openmrs/esm-framework';
|
|
5
|
+
import { useVisitOrOfflineVisit } from '@openmrs/esm-patient-common-lib';
|
|
6
|
+
|
|
7
|
+
type encounter = {
|
|
8
|
+
formUuid: string;
|
|
9
|
+
encounterDatetime: string;
|
|
10
|
+
encounterType: string;
|
|
11
|
+
encounterUuid: string;
|
|
12
|
+
visitUuid: string;
|
|
13
|
+
patientUuid: string;
|
|
14
|
+
visitTypeUuid: string;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
type PatientAdrWorkspaceProps = DefaultWorkspaceProps & {
|
|
18
|
+
encounter?: encounter;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
export function PatientAdrWorkspace(props: PatientAdrWorkspaceProps) {
|
|
22
|
+
const { t } = useTranslation();
|
|
23
|
+
const { closeWorkspace, closeWorkspaceWithSavedChanges, promptBeforeClosing, encounter } = props;
|
|
24
|
+
const { formUuid, encounterUuid, visitUuid, patientUuid, visitTypeUuid } = encounter || {};
|
|
25
|
+
const { isLoading: isLoadingVisit, currentVisit, error: visitError } = useVisitOrOfflineVisit(patientUuid);
|
|
26
|
+
const { patient, isLoading: isLoadingPatient, error: patientError } = usePatient(patientUuid);
|
|
27
|
+
const state = useMemo<Record<string, unknown>>(
|
|
28
|
+
() => ({
|
|
29
|
+
view: 'form',
|
|
30
|
+
formUuid: formUuid ?? null,
|
|
31
|
+
visitUuid: visitUuid ?? null,
|
|
32
|
+
visitTypeUuid: visitTypeUuid ?? null,
|
|
33
|
+
patientUuid: patientUuid ?? null,
|
|
34
|
+
patient,
|
|
35
|
+
encounterUuid: encounterUuid ?? null,
|
|
36
|
+
closeWorkspaceWithSavedChanges,
|
|
37
|
+
closeWorkspace,
|
|
38
|
+
promptBeforeClosing,
|
|
39
|
+
}),
|
|
40
|
+
[
|
|
41
|
+
patientUuid,
|
|
42
|
+
encounterUuid,
|
|
43
|
+
patient,
|
|
44
|
+
closeWorkspace,
|
|
45
|
+
promptBeforeClosing,
|
|
46
|
+
formUuid,
|
|
47
|
+
visitUuid,
|
|
48
|
+
visitTypeUuid,
|
|
49
|
+
closeWorkspaceWithSavedChanges,
|
|
50
|
+
],
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const isLoading = isLoadingVisit || isLoadingPatient;
|
|
54
|
+
const error = visitError || patientError;
|
|
55
|
+
|
|
56
|
+
if (isLoading) {
|
|
57
|
+
return <InlineLoading description={t('loading', 'Loading')} iconDescription={t('loading', 'Loading data...')} />;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (error) {
|
|
61
|
+
return (
|
|
62
|
+
<InlineNotification
|
|
63
|
+
aria-label={t('error', 'Error')}
|
|
64
|
+
kind="error"
|
|
65
|
+
onClose={() => {}}
|
|
66
|
+
onCloseButtonClick={() => {}}
|
|
67
|
+
statusIconDescription="notification"
|
|
68
|
+
subtitle={t('errorLoadingPatientWorkspace', 'Error loading patient workspace {{errorMessage}}', {
|
|
69
|
+
errorMessage: error?.message,
|
|
70
|
+
})}
|
|
71
|
+
title={t('error', 'Error')}
|
|
72
|
+
/>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return <div>{patient && <ExtensionSlot name="form-widget-slot" state={state} />}</div>;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export default PatientAdrWorkspace;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import dayjs from 'dayjs';
|
|
3
|
+
|
|
4
|
+
import styles from './summary.scss';
|
|
5
|
+
import AdrEncounter from '../encounters/adr-encounter.component';
|
|
6
|
+
import { useTranslation } from 'react-i18next';
|
|
7
|
+
import { useAdrAssessmentEncounter } from '../encounters/encounter.resource';
|
|
8
|
+
import { DataTableSkeleton, DatePicker, DatePickerInput } from '@carbon/react';
|
|
9
|
+
|
|
10
|
+
type SummaryProps = {};
|
|
11
|
+
|
|
12
|
+
const Summary: React.FC<SummaryProps> = () => {
|
|
13
|
+
const { t } = useTranslation();
|
|
14
|
+
const defaultDateRange: [Date, Date] = [dayjs().startOf('day').toDate(), dayjs().endOf('day').toDate()];
|
|
15
|
+
const [dateRange, setDateRange] = useState<[Date, Date]>(defaultDateRange);
|
|
16
|
+
const formattedStartDate = dayjs(dateRange[0]).startOf('day').format('YYYY-MM-DDTHH:mm:ss');
|
|
17
|
+
const formattedEndDate = dayjs(dateRange[1]).endOf('day').format('YYYY-MM-DDTHH:mm:ss');
|
|
18
|
+
const { encounters, isLoading } = useAdrAssessmentEncounter(formattedStartDate, formattedEndDate);
|
|
19
|
+
const [counts, setCounts] = useState({
|
|
20
|
+
assessment: 0,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const handleDateRangeChange = ([start, end]: Array<Date>) => {
|
|
24
|
+
if (start && end) {
|
|
25
|
+
setDateRange([start, end]);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
useEffect(() => {
|
|
30
|
+
const adrAssessmentCount = encounters.filter(
|
|
31
|
+
(encounter) => encounter.encounterTypeUuid === 'd18d6d8a-4be2-4115-ac7e-86cc0ec2b263',
|
|
32
|
+
).length;
|
|
33
|
+
|
|
34
|
+
setCounts({
|
|
35
|
+
assessment: adrAssessmentCount,
|
|
36
|
+
});
|
|
37
|
+
}, [encounters]);
|
|
38
|
+
|
|
39
|
+
if (isLoading) {
|
|
40
|
+
return (
|
|
41
|
+
<div className={styles.loaderContainer}>
|
|
42
|
+
<DataTableSkeleton showHeader={false} showToolbar={false} zebra />
|
|
43
|
+
</div>
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<>
|
|
49
|
+
<div className={styles.filterContainer}>
|
|
50
|
+
<DatePicker
|
|
51
|
+
maxDate={new Date()}
|
|
52
|
+
datePickerType="range"
|
|
53
|
+
className={styles.dateRangePicker}
|
|
54
|
+
value={[...dateRange]}
|
|
55
|
+
onChange={handleDateRangeChange}>
|
|
56
|
+
<DatePickerInput
|
|
57
|
+
id="date-picker-input-id-start"
|
|
58
|
+
placeholder="mm/dd/yyyy"
|
|
59
|
+
labelText={t('startDate', 'Start date')}
|
|
60
|
+
size="md"
|
|
61
|
+
/>
|
|
62
|
+
<DatePickerInput
|
|
63
|
+
id="date-picker-input-id-finish"
|
|
64
|
+
placeholder="mm/dd/yyyy"
|
|
65
|
+
labelText={t('endDate', 'End date')}
|
|
66
|
+
size="md"
|
|
67
|
+
/>
|
|
68
|
+
</DatePicker>
|
|
69
|
+
</div>
|
|
70
|
+
<div className={styles.summaryContainer}>
|
|
71
|
+
<div className={styles.summaryCard}>
|
|
72
|
+
<h4>{t('adrAssessment', 'ADR Assessment')}</h4>
|
|
73
|
+
<div>
|
|
74
|
+
<h6>{t('totalAdrAssessment', 'Total ADR assessment')}</h6>
|
|
75
|
+
<p>{counts.assessment}</p>
|
|
76
|
+
</div>
|
|
77
|
+
</div>
|
|
78
|
+
</div>
|
|
79
|
+
<AdrEncounter encounters={encounters} />
|
|
80
|
+
</>
|
|
81
|
+
);
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export default Summary;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
@use '@carbon/layout';
|
|
2
|
+
@use '@carbon/colors';
|
|
3
|
+
|
|
4
|
+
.summaryContainer {
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: row;
|
|
7
|
+
justify-content: space-between;
|
|
8
|
+
gap: layout.$spacing-05;
|
|
9
|
+
padding: layout.$spacing-05;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
.summaryCard {
|
|
13
|
+
display: flex;
|
|
14
|
+
flex-direction: column;
|
|
15
|
+
justify-content: space-between;
|
|
16
|
+
gap: layout.$spacing-05;
|
|
17
|
+
padding: layout.$spacing-05;
|
|
18
|
+
border: 1px solid colors.$gray-20;
|
|
19
|
+
width: 100%;
|
|
20
|
+
height: 7.875rem;
|
|
21
|
+
background-color: colors.$white;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.dateRangePicker {
|
|
25
|
+
flex-grow: unset;
|
|
26
|
+
}
|
|
27
|
+
.filterContainer {
|
|
28
|
+
display: flex;
|
|
29
|
+
flex-wrap: wrap;
|
|
30
|
+
gap: layout.$spacing-05;
|
|
31
|
+
text-align: end;
|
|
32
|
+
align-items: center;
|
|
33
|
+
justify-content: flex-end;
|
|
34
|
+
padding: layout.$spacing-05;
|
|
35
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const configSchema = {};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { defineConfigSchema, getAsyncLifecycle, getSyncLifecycle } from '@openmrs/esm-framework';
|
|
2
|
+
import { configSchema } from './config-schema';
|
|
3
|
+
import AdrAssessmentApp from './root.component';
|
|
4
|
+
import { createDashboardLink } from '@openmrs/esm-patient-common-lib';
|
|
5
|
+
import SideMenu from './components/side-menu/side-menu.component';
|
|
6
|
+
import Summary from './components/summary/summary.component';
|
|
7
|
+
|
|
8
|
+
const moduleName = '@kenyaemr/esm-adr-app';
|
|
9
|
+
|
|
10
|
+
const options = {
|
|
11
|
+
featureName: 'adr-assessment',
|
|
12
|
+
moduleName,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const importTranslation = require.context('../translations', false, /.json$/, 'lazy');
|
|
16
|
+
|
|
17
|
+
export const adrAssessmentApp = getSyncLifecycle(AdrAssessmentApp, options);
|
|
18
|
+
export const adrAssessmentSideNav = getSyncLifecycle(SideMenu, options);
|
|
19
|
+
export const adrAssessmentSummary = getSyncLifecycle(Summary, options);
|
|
20
|
+
export const patientAdrWorkspace = getAsyncLifecycle(() => import('./components/patient-adr.workspace'), options);
|
|
21
|
+
|
|
22
|
+
// Dashboard link for the search page
|
|
23
|
+
export const overviewDashboardLink = getSyncLifecycle(
|
|
24
|
+
createDashboardLink({ moduleName, path: 'overview', title: 'Overview', icon: '' }),
|
|
25
|
+
options,
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
export function startupApp() {
|
|
29
|
+
defineConfigSchema(moduleName, configSchema);
|
|
30
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React, { useEffect } from 'react';
|
|
2
|
+
import { Routes, BrowserRouter, Route } from 'react-router-dom';
|
|
3
|
+
import { setLeftNav, unsetLeftNav, WorkspaceContainer } from '@openmrs/esm-framework';
|
|
4
|
+
import Dashboard from './components/dashboard/home-dashboard.component';
|
|
5
|
+
|
|
6
|
+
const AdrAssessmentApp = () => {
|
|
7
|
+
const spaBasePath = `${window.spaBase}/adr-assessment`;
|
|
8
|
+
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
setLeftNav({ name: 'adr-assessment-page-dashboard-slot', basePath: spaBasePath });
|
|
11
|
+
return () => unsetLeftNav('adr-assessment-page-dashboard-slot');
|
|
12
|
+
}, [spaBasePath]);
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<main>
|
|
16
|
+
<BrowserRouter basename={window.spaBase}>
|
|
17
|
+
<Routes>
|
|
18
|
+
<Route path="/adr-assessment" element={<Dashboard />} />
|
|
19
|
+
<Route path="/adr-assessment/:dashboard/*" element={<Dashboard />} />
|
|
20
|
+
</Routes>
|
|
21
|
+
<WorkspaceContainer key="adr-assessment" contextKey="adr-assessment"></WorkspaceContainer>
|
|
22
|
+
</BrowserRouter>
|
|
23
|
+
</main>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export default AdrAssessmentApp;
|
package/src/routes.json
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json.openmrs.org/routes.schema.json",
|
|
3
|
+
"backendDependencies": {
|
|
4
|
+
"kenyaemr": "^19.0.0"
|
|
5
|
+
},
|
|
6
|
+
"pages": [
|
|
7
|
+
{
|
|
8
|
+
"component": "adrAssessmentApp",
|
|
9
|
+
"route": "adr-assessment"
|
|
10
|
+
}
|
|
11
|
+
],
|
|
12
|
+
"workspaces": [
|
|
13
|
+
{
|
|
14
|
+
"name": "patient-adr-workspace",
|
|
15
|
+
"title": "ADR Assessment Review",
|
|
16
|
+
"component": "patientAdrWorkspace",
|
|
17
|
+
"type": "workspace",
|
|
18
|
+
"canMaximize": true,
|
|
19
|
+
"canHide": true,
|
|
20
|
+
"width": "extra-wide"
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
],
|
|
25
|
+
"extensions": [
|
|
26
|
+
{
|
|
27
|
+
"name": "adr-assessment-side-nav",
|
|
28
|
+
"slot": "adr-assessment-side-nav-slot",
|
|
29
|
+
"component": "adrAssessmentSideNav"
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"name": "adr-assessment-search-dashboard-db-link",
|
|
33
|
+
"slot": "adr-assessment-page-dashboard-slot",
|
|
34
|
+
"component": "overviewDashboardLink",
|
|
35
|
+
"meta": {
|
|
36
|
+
"name": "adr-assessment-overview",
|
|
37
|
+
"title": "Overview",
|
|
38
|
+
"slot": "adr-assessment-overview-dashboard-slot"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"name": "adr-assessment-summary",
|
|
43
|
+
"component": "adrAssessmentSummary",
|
|
44
|
+
"slot": "adr-assessment-overview-dashboard-slot"
|
|
45
|
+
}
|
|
46
|
+
]
|
|
47
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type DashboardConfig = {
|
|
2
|
+
name: string;
|
|
3
|
+
slot: string;
|
|
4
|
+
title: string;
|
|
5
|
+
};
|
|
6
|
+
|
|
7
|
+
export interface MappedAdrEncounter {
|
|
8
|
+
encounterUuid: string;
|
|
9
|
+
encounterTypeUuid: string;
|
|
10
|
+
patientUuid: string;
|
|
11
|
+
patientName: string;
|
|
12
|
+
encounterType: string;
|
|
13
|
+
encounterDatetime: string;
|
|
14
|
+
visitTypeName: string;
|
|
15
|
+
formName: string;
|
|
16
|
+
location: string;
|
|
17
|
+
provider: string;
|
|
18
|
+
formUuid?: string;
|
|
19
|
+
visitUuid?: string;
|
|
20
|
+
visitTypeUuid?: string;
|
|
21
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"adrAssessment": "ADR Assessment",
|
|
3
|
+
"adrAssessmentEncounters": "ADR Assessment Encounters",
|
|
4
|
+
"encounters": "Encounters",
|
|
5
|
+
"endDate": "End date",
|
|
6
|
+
"noAdrEncountersFound": "Adr assessment encounters",
|
|
7
|
+
"noEncountersFoundTitle": "No encounters found",
|
|
8
|
+
"review": "Review",
|
|
9
|
+
"startDate": "Start date",
|
|
10
|
+
"summaryOfAdrAssessmentEncounters": "Summary of ADR assessment encounters",
|
|
11
|
+
"totalAdrAssessment": "Total ADR assessment"
|
|
12
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
module.exports = require('openmrs/default-webpack-config');
|