@pih/esm-commons-app 2.1.1-pre.15 → 2.1.1-pre.151
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 +5 -35
- package/dist/117.js +1 -0
- package/dist/117.js.map +1 -0
- package/dist/125.js +1 -0
- package/dist/125.js.map +1 -0
- package/dist/163.js +1 -0
- package/dist/163.js.map +1 -0
- package/dist/240.js +1 -0
- package/dist/240.js.map +1 -0
- package/dist/294.js +1 -0
- package/dist/294.js.map +1 -0
- package/dist/324.js +1 -0
- package/dist/324.js.map +1 -0
- package/dist/349.js +1 -0
- package/dist/349.js.map +1 -0
- package/dist/378.js +1 -0
- package/dist/378.js.map +1 -0
- package/dist/466.js +1 -0
- package/dist/466.js.map +1 -0
- package/dist/508.js +1 -0
- package/dist/508.js.map +1 -0
- package/dist/61.js +1 -0
- package/dist/61.js.map +1 -0
- package/dist/614.js +33 -0
- package/dist/614.js.map +1 -0
- package/dist/646.js +17 -0
- package/dist/646.js.map +1 -0
- package/dist/689.js +1 -0
- package/dist/689.js.map +1 -0
- package/dist/701.js +1 -0
- package/dist/701.js.map +1 -0
- package/dist/712.js +1 -0
- package/dist/712.js.map +1 -0
- package/dist/72.js +1 -0
- package/dist/72.js.map +1 -0
- package/dist/771.js +1 -0
- package/dist/771.js.map +1 -0
- package/dist/82.js +11 -0
- package/dist/82.js.map +1 -0
- package/dist/830.js +1 -0
- package/dist/830.js.map +1 -0
- package/dist/852.js +1 -0
- package/dist/852.js.map +1 -0
- package/dist/855.js +1 -0
- package/dist/855.js.map +1 -0
- package/dist/859.js +1 -0
- package/dist/859.js.map +1 -0
- package/dist/867.js +1 -0
- package/dist/867.js.map +1 -0
- package/dist/88.js +1 -0
- package/dist/88.js.map +1 -0
- package/dist/912.js +1 -0
- package/dist/912.js.map +1 -0
- package/dist/913.js +1 -0
- package/dist/913.js.map +1 -0
- package/dist/985.js +1 -0
- package/dist/985.js.map +1 -0
- package/dist/989.js +1 -0
- package/dist/989.js.map +1 -0
- package/dist/main.js +3 -1
- package/dist/main.js.map +1 -1
- package/dist/pih-esm-commons-app.js +3 -1
- package/dist/pih-esm-commons-app.js.buildmanifest.json +570 -110
- package/dist/pih-esm-commons-app.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +7 -10
- package/rspack.config.js +1 -0
- package/src/config-schema.ts +85 -0
- package/src/declarations.d.ts +2 -0
- package/src/dispensing/zebra-label-print-action.extension.tsx +66 -0
- package/src/dispensing/zebra-label-print.resource.ts +49 -0
- package/src/dispensing/zebra-pdf-print.ts +108 -0
- package/src/hooks/useConceptReferenceRanges.ts +128 -0
- package/src/hooks/useEncounters.ts +22 -0
- package/src/hooks/useVisitAttribute.ts +37 -0
- package/src/index.ts +39 -12
- package/src/routes.json +65 -11
- package/src/service-queues-app/maternal-triage-form.workspace.tsx +111 -0
- package/src/service-queues-app/maternal-triage-queue-actions.extension.tsx +132 -0
- package/src/service-queues-app/maternal-triage-queue-actions.resource.ts +84 -0
- package/src/service-queues-app/maternal-trial-queue-actions.scss +14 -0
- package/src/service-queues-app/types.ts +58 -0
- package/src/ward-app/VitalsTable.tsx +361 -0
- package/src/ward-app/inborn-outborn-tag-row.extension.tsx +79 -0
- package/src/ward-app/inborn-outborn-tag-row.scss +57 -0
- package/src/ward-app/o2-iframe.component.tsx +13 -4
- package/src/ward-app/o2-pregnancy-infant-dashboard.extension.tsx +29 -10
- package/src/ward-app/o2-pregnancy-infant-dashboard.scss +5 -0
- package/src/ward-app/o2-visit-summary-action-button.component.tsx +17 -0
- package/src/ward-app/o2-visit-summary-workspace.component.tsx +104 -26
- package/src/ward-app/o2-vital-signs-action-button.component.tsx +17 -0
- package/src/ward-app/o2-vital-signs-workspace.component.tsx +65 -0
- package/src/ward-app/types.ts +7 -4
- package/translations/en.json +24 -3
- package/translations/es.json +7 -1
- package/translations/fr.json +7 -1
- package/dist/138.js +0 -1
- package/dist/138.js.map +0 -1
- package/dist/291.js +0 -1
- package/dist/291.js.map +0 -1
- package/dist/300.js +0 -1
- package/dist/372.js +0 -1
- package/dist/372.js.map +0 -1
- package/dist/41.js +0 -2
- package/dist/41.js.LICENSE.txt +0 -9
- package/dist/41.js.map +0 -1
- package/dist/411.js +0 -2
- package/dist/411.js.LICENSE.txt +0 -5
- package/dist/411.js.map +0 -1
- package/dist/449.js +0 -2
- package/dist/449.js.LICENSE.txt +0 -9
- package/dist/449.js.map +0 -1
- package/dist/55.js +0 -1
- package/dist/641.js +0 -1
- package/dist/641.js.map +0 -1
- package/dist/660.js +0 -1
- package/dist/660.js.map +0 -1
- package/dist/831.js +0 -2
- package/dist/831.js.LICENSE.txt +0 -5
- package/dist/831.js.map +0 -1
- package/dist/90.js +0 -1
- package/dist/90.js.map +0 -1
- package/dist/99.js +0 -1
- package/src/ward-app/o2-visit-summary-action-button.extension.tsx +0 -18
- package/webpack.config.js +0 -1
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { restBaseUrl, useOpenmrsFetchAll } from '@openmrs/esm-framework';
|
|
2
|
+
import { type Encounter } from '../service-queues-app/types';
|
|
3
|
+
|
|
4
|
+
interface EncounterSearchParams {
|
|
5
|
+
patient: string;
|
|
6
|
+
visit?: string;
|
|
7
|
+
encounterType?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export function useEncounters(params: EncounterSearchParams) {
|
|
11
|
+
const url = `${restBaseUrl}/encounter?`;
|
|
12
|
+
const searchParams = new URLSearchParams();
|
|
13
|
+
searchParams.append('patient', params.patient);
|
|
14
|
+
if (params.visit) {
|
|
15
|
+
searchParams.append('visit', params.visit);
|
|
16
|
+
}
|
|
17
|
+
if (params.encounterType) {
|
|
18
|
+
searchParams.append('encounterType', params.encounterType);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return useOpenmrsFetchAll<Encounter>(url + searchParams.toString());
|
|
22
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { restBaseUrl, useOpenmrsFetchAll } from '@openmrs/esm-framework';
|
|
2
|
+
|
|
3
|
+
export interface VisitAttribute {
|
|
4
|
+
uuid: string;
|
|
5
|
+
display?: string;
|
|
6
|
+
value: boolean | string | number;
|
|
7
|
+
attributeType: {
|
|
8
|
+
uuid: string;
|
|
9
|
+
display?: string;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// prettier-ignore
|
|
14
|
+
const visitAttributeCustomRepresentation =
|
|
15
|
+
'custom:(uuid,value,attributeType:(uuid,display))';
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Fetches the attribute of a given type for a given visit.
|
|
19
|
+
*
|
|
20
|
+
* The default visit representation used by the ward admission/request hooks does not include
|
|
21
|
+
* attribute values, so this hook queries the visit's attribute subresource directly and returns the
|
|
22
|
+
* single attribute matching `attributeTypeUuid` (or `undefined` if the visit has no such attribute).
|
|
23
|
+
*
|
|
24
|
+
* @param visitUuid the visit whose attributes to fetch. When falsy, no request is made.
|
|
25
|
+
* @param attributeTypeUuid the visit attribute type to look for.
|
|
26
|
+
*/
|
|
27
|
+
export function useVisitAttribute(visitUuid: string, attributeTypeUuid: string) {
|
|
28
|
+
const apiUrl = visitUuid
|
|
29
|
+
? `${restBaseUrl}/visit/${visitUuid}/attribute?v=${visitAttributeCustomRepresentation}`
|
|
30
|
+
: null;
|
|
31
|
+
|
|
32
|
+
const { data, ...rest } = useOpenmrsFetchAll<VisitAttribute>(apiUrl);
|
|
33
|
+
|
|
34
|
+
const attribute = data?.find((attr) => attr.attributeType?.uuid === attributeTypeUuid);
|
|
35
|
+
|
|
36
|
+
return { attribute, value: attribute?.value, ...rest };
|
|
37
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -5,31 +5,58 @@
|
|
|
5
5
|
* microfrontend.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { getAsyncLifecycle } from '@openmrs/esm-framework';
|
|
8
|
+
import { getAsyncLifecycle, defineConfigSchema } from '@openmrs/esm-framework';
|
|
9
|
+
import { configSchema } from './config-schema';
|
|
9
10
|
|
|
10
11
|
const moduleName = '@pih/esm-commons-app';
|
|
11
|
-
const options = {
|
|
12
|
-
featureName: 'commons',
|
|
13
|
-
moduleName,
|
|
14
|
-
};
|
|
15
12
|
|
|
16
13
|
export const importTranslation = require.context('../translations', false, /.json$/, 'lazy');
|
|
17
14
|
|
|
18
|
-
// export const root = getAsyncLifecycle(() => import('./root.component'), options);
|
|
19
|
-
|
|
20
15
|
export const o2VisitSummaryWorkspaceSideRailIcon = getAsyncLifecycle(
|
|
21
|
-
() => import('./ward-app/o2-visit-summary-action-button.
|
|
22
|
-
|
|
16
|
+
() => import('./ward-app/o2-visit-summary-action-button.component'),
|
|
17
|
+
{ featureName: 'o2VisitSummaryWorkspaceSideRailIcon', moduleName },
|
|
23
18
|
);
|
|
24
19
|
|
|
25
20
|
export const o2VisitSummaryWorkspace = getAsyncLifecycle(
|
|
26
21
|
() => import('./ward-app/o2-visit-summary-workspace.component'),
|
|
27
|
-
|
|
22
|
+
{ featureName: 'o2VisitSummaryWorkspace', moduleName },
|
|
23
|
+
);
|
|
24
|
+
|
|
25
|
+
export const o2VitalSignsWorkspaceSideRailIcon = getAsyncLifecycle(
|
|
26
|
+
() => import('./ward-app/o2-vital-signs-action-button.component'),
|
|
27
|
+
{ featureName: 'o2VitalSignsWorkspaceSideRailIcon', moduleName },
|
|
28
28
|
);
|
|
29
29
|
|
|
30
|
+
export const o2VitalSignsWorkspace = getAsyncLifecycle(() => import('./ward-app/o2-vital-signs-workspace.component'), {
|
|
31
|
+
featureName: 'o2VitalSignsWorkspace',
|
|
32
|
+
moduleName,
|
|
33
|
+
});
|
|
34
|
+
|
|
30
35
|
export const o2PregnancyInfantDashboard = getAsyncLifecycle(
|
|
31
36
|
() => import('./ward-app/o2-pregnancy-infant-dashboard.extension'),
|
|
32
|
-
|
|
37
|
+
{ featureName: 'o2PregnancyInfantDashboard', moduleName },
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
export const maternalTriageFormWorkspace = getAsyncLifecycle(
|
|
41
|
+
() => import('./service-queues-app/maternal-triage-form.workspace'),
|
|
42
|
+
{ featureName: 'maternalTriageFormWorkspace', moduleName },
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
export const triageWaitingQueueActions = getAsyncLifecycle(
|
|
46
|
+
() => import('./service-queues-app/maternal-triage-queue-actions.extension'),
|
|
47
|
+
{ featureName: 'triageWaitingQueueActions', moduleName },
|
|
33
48
|
);
|
|
34
49
|
|
|
35
|
-
export
|
|
50
|
+
export const zebraLabelPrintAction = getAsyncLifecycle(
|
|
51
|
+
() => import('./dispensing/zebra-label-print-action.extension'),
|
|
52
|
+
{ featureName: 'zebraLabelPrintAction', moduleName },
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
export const inbornOutbornTag = getAsyncLifecycle(() => import('./ward-app/inborn-outborn-tag-row.extension'), {
|
|
56
|
+
featureName: 'inbornOutbornTag',
|
|
57
|
+
moduleName,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
export function startupApp() {
|
|
61
|
+
defineConfigSchema(moduleName, configSchema);
|
|
62
|
+
}
|
package/src/routes.json
CHANGED
|
@@ -1,28 +1,82 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.openmrs.org/routes.schema.json",
|
|
3
3
|
"pages": [
|
|
4
|
-
],
|
|
5
|
-
"extensions": [
|
|
6
4
|
{
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
|
|
5
|
+
"component": "root",
|
|
6
|
+
"route": "appointments"
|
|
7
|
+
},
|
|
8
|
+
{
|
|
9
|
+
"component": "root",
|
|
10
|
+
"route": "service-queues"
|
|
10
11
|
},
|
|
12
|
+
{
|
|
13
|
+
"component": "root",
|
|
14
|
+
"route": "dispensing2"
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
"component": "root",
|
|
18
|
+
"route": "ward"
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"extensions": [
|
|
11
22
|
{
|
|
12
23
|
"name": "o2-pregnancy-infant-dashboard",
|
|
13
24
|
"component": "o2PregnancyInfantDashboard",
|
|
14
25
|
"slot": "ward-patient-workspace-content-slot"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"name": "triage-waiting-queue-actions",
|
|
29
|
+
"component": "triageWaitingQueueActions",
|
|
30
|
+
"slot": "queue-table-triage-waiting-queue-actions-slot"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"name": "zebra-label-print-action",
|
|
34
|
+
"component": "zebraLabelPrintAction",
|
|
35
|
+
"slot": "medication-dispense-action-slot"
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"name": "inborn-outborn-tag",
|
|
39
|
+
"component": "inbornOutbornTag",
|
|
40
|
+
"slot": "maternal-ward-patient-card-header-slot"
|
|
15
41
|
}
|
|
16
42
|
],
|
|
17
|
-
"
|
|
43
|
+
"workspaces2": [
|
|
18
44
|
{
|
|
19
45
|
"name": "o2-visit-summary-workspace",
|
|
20
46
|
"component": "o2VisitSummaryWorkspace",
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
"
|
|
25
|
-
"
|
|
47
|
+
"window": "o2-visit-summary-window"
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"name": "o2-vital-signs-workspace",
|
|
51
|
+
"component": "o2VitalSignsWorkspace",
|
|
52
|
+
"window": "o2-vital-signs-window"
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"name": "maternal-triage-form-workspace",
|
|
56
|
+
"component": "maternalTriageFormWorkspace",
|
|
57
|
+
"window": "maternal-triage-form-window"
|
|
58
|
+
}
|
|
59
|
+
],
|
|
60
|
+
"workspaceWindows2": [
|
|
61
|
+
{
|
|
62
|
+
"name": "o2-visit-summary-window",
|
|
63
|
+
"icon": "o2VisitSummaryWorkspaceSideRailIcon",
|
|
64
|
+
"group": "ward-patient",
|
|
65
|
+
"width": "extra-wide",
|
|
66
|
+
"canMaximize": true
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"name": "o2-vital-signs-window",
|
|
70
|
+
"icon": "o2VitalSignsWorkspaceSideRailIcon",
|
|
71
|
+
"group": "ward-patient",
|
|
72
|
+
"width": "extra-wide",
|
|
73
|
+
"canMaximize": true
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"name": "maternal-triage-form-window",
|
|
77
|
+
"group": "service-queues",
|
|
78
|
+
"width": "extra-wide",
|
|
79
|
+
"canMaximize": true
|
|
26
80
|
}
|
|
27
81
|
]
|
|
28
82
|
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { type Patient, showSnackbar, Workspace2, type Workspace2DefinitionProps } from '@openmrs/esm-framework';
|
|
2
|
+
import React, { useEffect } from 'react';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import O2IFrame from '../ward-app/o2-iframe.component';
|
|
5
|
+
import { updateQueueEntry, useMutateQueueEntries } from './maternal-triage-queue-actions.resource';
|
|
6
|
+
import { type QueueEntry } from './types';
|
|
7
|
+
import { useEncounters } from '../hooks/useEncounters';
|
|
8
|
+
import { Loading } from '@carbon/react';
|
|
9
|
+
import { InlineNotification } from '@carbon/react';
|
|
10
|
+
|
|
11
|
+
interface MaternalTriageFormWorkspaceProps {
|
|
12
|
+
queueEntry: QueueEntry;
|
|
13
|
+
patient: Patient;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const MATERNAL_TRIAGE_FORM_ENCOUNTER_TYPE = '41911448-71a1-43d7-bba8-dc86339850da';
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Workspace to display the Maternal Triage HTML Form, rendered in a iframe
|
|
20
|
+
*/
|
|
21
|
+
const MaternalTriageFormWorkspace: React.FC<Workspace2DefinitionProps<MaternalTriageFormWorkspaceProps>> = ({
|
|
22
|
+
workspaceProps: { queueEntry, patient },
|
|
23
|
+
closeWorkspace,
|
|
24
|
+
}) => {
|
|
25
|
+
const { t } = useTranslation();
|
|
26
|
+
const { mutateQueueEntries } = useMutateQueueEntries();
|
|
27
|
+
const {
|
|
28
|
+
data: filledOutTriageForms,
|
|
29
|
+
isLoading,
|
|
30
|
+
error,
|
|
31
|
+
} = useEncounters({
|
|
32
|
+
patient: patient.uuid,
|
|
33
|
+
visit: queueEntry.visit.uuid,
|
|
34
|
+
encounterType: MATERNAL_TRIAGE_FORM_ENCOUNTER_TYPE,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
const onMessage = async (event: MessageEvent) => {
|
|
39
|
+
if (event.data == 'triageFormSubmitted') {
|
|
40
|
+
const endQueueEntry = () => {
|
|
41
|
+
return updateQueueEntry(queueEntry.uuid, {
|
|
42
|
+
endedAt: new Date().toISOString(),
|
|
43
|
+
});
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
await endQueueEntry();
|
|
47
|
+
await mutateQueueEntries();
|
|
48
|
+
closeWorkspace({ discardUnsavedChanges: true });
|
|
49
|
+
showSnackbar({
|
|
50
|
+
isLowContrast: true,
|
|
51
|
+
kind: 'success',
|
|
52
|
+
title: t('triageFormSubmitted', 'Triage form successfully submitted for {{patient}}.', {
|
|
53
|
+
patient: patient.person.display,
|
|
54
|
+
}),
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
window.addEventListener('message', onMessage);
|
|
60
|
+
return () => window.removeEventListener('message', onMessage);
|
|
61
|
+
}, []);
|
|
62
|
+
|
|
63
|
+
const patientUuid = patient.uuid;
|
|
64
|
+
const visitUuid = queueEntry.visit.uuid;
|
|
65
|
+
|
|
66
|
+
const elementsToHide = [
|
|
67
|
+
'header',
|
|
68
|
+
'#breadcrumbs',
|
|
69
|
+
|
|
70
|
+
// prevent O2 success toast from showing; After submitting form for a patient,
|
|
71
|
+
// the success toast for that patient shows when opening form for another patient
|
|
72
|
+
'.toast-type-success',
|
|
73
|
+
// UHM-8822: remove Cancel button from the triage form
|
|
74
|
+
'.cancel',
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
const customJavaScript = `
|
|
78
|
+
htmlForm.setSuccessFunction(() => window.top.postMessage('triageFormSubmitted'))
|
|
79
|
+
`;
|
|
80
|
+
|
|
81
|
+
if (isLoading) {
|
|
82
|
+
return <Loading withOverlay={false} small />;
|
|
83
|
+
} else if (error) {
|
|
84
|
+
return (
|
|
85
|
+
<InlineNotification
|
|
86
|
+
kind="error"
|
|
87
|
+
lowContrast={true}
|
|
88
|
+
hideCloseButton={true}
|
|
89
|
+
title={t('errorLoadingPatientForm', 'Error loading patient form')}
|
|
90
|
+
/>
|
|
91
|
+
);
|
|
92
|
+
} else {
|
|
93
|
+
// If patient does not have triage form filled out already, we load a new one,
|
|
94
|
+
// else, we load the last filled out form to edit
|
|
95
|
+
const src =
|
|
96
|
+
filledOutTriageForms.length == 0
|
|
97
|
+
? `${window.openmrsBase}/htmlformentryui/htmlform/enterHtmlFormWithStandardUi.page?patientId=${patientUuid}&visitId=${visitUuid}&definitionUiResource=file:configuration/pih/htmlforms/triage.xml&returnUrl=%2Fopenmrs%2Fcoreapps%2Fclinicianfacing%2Fpatient.page%3FpatientId%3D${patientUuid}%26`
|
|
98
|
+
: `${window.openmrsBase}/htmlformentryui/htmlform/editHtmlFormWithStandardUi.page?patientId=${patientUuid}&encounterId=${filledOutTriageForms[filledOutTriageForms.length - 1].uuid}`;
|
|
99
|
+
|
|
100
|
+
return (
|
|
101
|
+
<Workspace2
|
|
102
|
+
title={t('maternalTriageForm', 'Maternal triage form')}
|
|
103
|
+
hasUnsavedChanges={true} // assume there are unsaved changes to prevent accidental closure
|
|
104
|
+
>
|
|
105
|
+
<O2IFrame key={patientUuid} src={src} elementsToHide={elementsToHide} customJavaScript={customJavaScript} />
|
|
106
|
+
</Workspace2>
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export default MaternalTriageFormWorkspace;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { Button, OverflowMenu, OverflowMenuItem } from '@carbon/react';
|
|
2
|
+
import { isDesktop, launchWorkspace2, showModal, useConfig, useLayoutType } from '@openmrs/esm-framework';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
import styles from './maternal-trial-queue-actions.scss';
|
|
6
|
+
import { type QueueEntry } from './types';
|
|
7
|
+
import { transitionQueueEntry, useMutateQueueEntries } from './maternal-triage-queue-actions.resource';
|
|
8
|
+
|
|
9
|
+
// types taken from esm-service-queues-app
|
|
10
|
+
interface QueueTableCellComponentProps {
|
|
11
|
+
queueEntry: QueueEntry;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface ConfigObject {
|
|
15
|
+
concepts: {
|
|
16
|
+
defaultTransitionStatus: string;
|
|
17
|
+
};
|
|
18
|
+
// other fields not shown
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* This extension provides an extra "Triage" action, along with the other standard actions for
|
|
23
|
+
* queue entries in the queues table. The Triage action opens the maternal triage form, and also
|
|
24
|
+
* has the side effect of putting the patient's queue entry into the "in service" status, if they have
|
|
25
|
+
* not yet already
|
|
26
|
+
* @param param0
|
|
27
|
+
* @returns
|
|
28
|
+
*/
|
|
29
|
+
const MaternalTriageQueueActions: React.FC<QueueTableCellComponentProps> = ({ queueEntry }) => {
|
|
30
|
+
const { t } = useTranslation();
|
|
31
|
+
const layout = useLayoutType();
|
|
32
|
+
|
|
33
|
+
const { mutateQueueEntries } = useMutateQueueEntries();
|
|
34
|
+
const { concepts } = useConfig<ConfigObject>({ externalModuleName: '@openmrs/esm-service-queues-app' });
|
|
35
|
+
const inServiceStatus = concepts.defaultTransitionStatus;
|
|
36
|
+
|
|
37
|
+
const { patient } = queueEntry;
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<div className={styles.actionsCell}>
|
|
41
|
+
<Button
|
|
42
|
+
kind="ghost"
|
|
43
|
+
aria-label={t('triage', 'Triage')}
|
|
44
|
+
onClick={async () => {
|
|
45
|
+
if (queueEntry.status.uuid !== inServiceStatus) {
|
|
46
|
+
const res = await transitionQueueEntry({
|
|
47
|
+
queueEntryToTransition: queueEntry.uuid,
|
|
48
|
+
newStatus: inServiceStatus,
|
|
49
|
+
});
|
|
50
|
+
mutateQueueEntries();
|
|
51
|
+
const updatedQueueEntry = { ...res.data, visit: queueEntry.visit };
|
|
52
|
+
launchWorkspace2('maternal-triage-form-workspace', { queueEntry: updatedQueueEntry, patient });
|
|
53
|
+
} else {
|
|
54
|
+
launchWorkspace2('maternal-triage-form-workspace', { queueEntry, patient });
|
|
55
|
+
}
|
|
56
|
+
}}
|
|
57
|
+
size={isDesktop(layout) ? 'sm' : 'lg'}>
|
|
58
|
+
{t('triage', 'Triage')}
|
|
59
|
+
</Button>
|
|
60
|
+
|
|
61
|
+
<OverflowMenu aria-label="Actions menu" size={isDesktop(layout) ? 'sm' : 'lg'} align="left" flipped>
|
|
62
|
+
<OverflowMenuItem
|
|
63
|
+
className={styles.menuItem}
|
|
64
|
+
aria-label={t('transition', 'Transition')}
|
|
65
|
+
hasDivider
|
|
66
|
+
onClick={() => {
|
|
67
|
+
const dispose = showModal('transition-queue-entry-modal', {
|
|
68
|
+
closeModal: () => dispose(),
|
|
69
|
+
queueEntry,
|
|
70
|
+
});
|
|
71
|
+
}}
|
|
72
|
+
itemText={t('transition', 'Transition')}
|
|
73
|
+
/>
|
|
74
|
+
<OverflowMenuItem
|
|
75
|
+
className={styles.menuItem}
|
|
76
|
+
aria-label={t('edit', 'Edit')}
|
|
77
|
+
hasDivider
|
|
78
|
+
onClick={() => {
|
|
79
|
+
const dispose = showModal('edit-queue-entry-modal', {
|
|
80
|
+
closeModal: () => dispose(),
|
|
81
|
+
queueEntry,
|
|
82
|
+
});
|
|
83
|
+
}}
|
|
84
|
+
itemText={t('edit', 'Edit')}
|
|
85
|
+
/>
|
|
86
|
+
<OverflowMenuItem
|
|
87
|
+
className={styles.menuItem}
|
|
88
|
+
aria-label={t('removePatient', 'Remove patient')}
|
|
89
|
+
hasDivider
|
|
90
|
+
onClick={() => {
|
|
91
|
+
const dispose = showModal('remove-queue-entry-modal', {
|
|
92
|
+
closeModal: () => dispose(),
|
|
93
|
+
queueEntry,
|
|
94
|
+
});
|
|
95
|
+
}}
|
|
96
|
+
itemText={t('removePatient', 'Remove patient')}
|
|
97
|
+
/>
|
|
98
|
+
{queueEntry.previousQueueEntry == null ? (
|
|
99
|
+
<OverflowMenuItem
|
|
100
|
+
className={styles.menuItem}
|
|
101
|
+
aria-label={t('delete', 'Delete')}
|
|
102
|
+
hasDivider
|
|
103
|
+
isDelete
|
|
104
|
+
onClick={() => {
|
|
105
|
+
const dispose = showModal('delete-queue-entry-modal', {
|
|
106
|
+
closeModal: () => dispose(),
|
|
107
|
+
queueEntry,
|
|
108
|
+
});
|
|
109
|
+
}}
|
|
110
|
+
itemText={t('delete', 'Delete')}
|
|
111
|
+
/>
|
|
112
|
+
) : (
|
|
113
|
+
<OverflowMenuItem
|
|
114
|
+
className={styles.menuItem}
|
|
115
|
+
aria-label={t('undoTransition', 'Undo transition')}
|
|
116
|
+
hasDivider
|
|
117
|
+
isDelete
|
|
118
|
+
onClick={() => {
|
|
119
|
+
const dispose = showModal('undo-transition-queue-entry-modal', {
|
|
120
|
+
closeModal: () => dispose(),
|
|
121
|
+
queueEntry,
|
|
122
|
+
});
|
|
123
|
+
}}
|
|
124
|
+
itemText={t('undoTransition', 'Undo transition')}
|
|
125
|
+
/>
|
|
126
|
+
)}
|
|
127
|
+
</OverflowMenu>
|
|
128
|
+
</div>
|
|
129
|
+
);
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
export default MaternalTriageQueueActions;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { type Concept, type FetchResponse, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
|
|
2
|
+
import { useSWRConfig } from 'swr';
|
|
3
|
+
import { type QueueEntry } from './types';
|
|
4
|
+
import { useCallback, useMemo } from 'react';
|
|
5
|
+
|
|
6
|
+
// Hooks here copied from esm-service-queues-app
|
|
7
|
+
|
|
8
|
+
export function useMutateQueueEntries() {
|
|
9
|
+
const { mutate, cache } = useSWRConfig();
|
|
10
|
+
const mutateQueueEntries = useCallback(() => {
|
|
11
|
+
const promises: Promise<unknown>[] = [];
|
|
12
|
+
for (const key of cache.keys()) {
|
|
13
|
+
if (key.includes(`${restBaseUrl}/queue-entry`) || key.includes(`${restBaseUrl}/visit-queue-entry`)) {
|
|
14
|
+
promises.push(mutate(key));
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
return Promise.all(promises);
|
|
18
|
+
}, [mutate, cache]);
|
|
19
|
+
|
|
20
|
+
return useMemo(
|
|
21
|
+
() => ({
|
|
22
|
+
mutateQueueEntries,
|
|
23
|
+
}),
|
|
24
|
+
[mutateQueueEntries],
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface TransitionQueueEntryParams {
|
|
29
|
+
queueEntryToTransition: string;
|
|
30
|
+
transitionDate?: string;
|
|
31
|
+
newQueue?: string;
|
|
32
|
+
newStatus?: string;
|
|
33
|
+
newPriority?: string;
|
|
34
|
+
newPriorityComment?: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* A transition is defined as an action that ends a current queue entry and immediately starts a new one
|
|
39
|
+
* with (slightly) different values. For now, this could be used to transition the queue entry's status,
|
|
40
|
+
* priority or queue. This allows us to keep a history of queue entries through a patient's visit.
|
|
41
|
+
* Note that there are some use cases (like RDE or data correction) where a transition is NOT appropriate.
|
|
42
|
+
* @param params
|
|
43
|
+
* @param abortController
|
|
44
|
+
* @returns
|
|
45
|
+
*/
|
|
46
|
+
export function transitionQueueEntry(
|
|
47
|
+
params: TransitionQueueEntryParams,
|
|
48
|
+
abortController?: AbortController,
|
|
49
|
+
): Promise<FetchResponse<QueueEntry>> {
|
|
50
|
+
return openmrsFetch(`${restBaseUrl}/queue-entry/transition`, {
|
|
51
|
+
method: 'POST',
|
|
52
|
+
headers: {
|
|
53
|
+
'Content-Type': 'application/json',
|
|
54
|
+
},
|
|
55
|
+
signal: abortController?.signal,
|
|
56
|
+
body: params,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
interface UpdateQueueEntryParams {
|
|
61
|
+
status?: Concept;
|
|
62
|
+
priority?: Concept;
|
|
63
|
+
priorityComment?: string;
|
|
64
|
+
sortWeight?: number;
|
|
65
|
+
startedAt?: string;
|
|
66
|
+
endedAt?: string;
|
|
67
|
+
loationWaitingFor?: Location;
|
|
68
|
+
providerWaitingFor?: Location;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export function updateQueueEntry(
|
|
72
|
+
queueEntryUuid: string,
|
|
73
|
+
params: UpdateQueueEntryParams,
|
|
74
|
+
abortController?: AbortController,
|
|
75
|
+
) {
|
|
76
|
+
return openmrsFetch(`${restBaseUrl}/queue-entry/${queueEntryUuid}`, {
|
|
77
|
+
method: 'POST',
|
|
78
|
+
headers: {
|
|
79
|
+
'Content-Type': 'application/json',
|
|
80
|
+
},
|
|
81
|
+
signal: abortController?.signal,
|
|
82
|
+
body: params,
|
|
83
|
+
});
|
|
84
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { type Concept, type OpenmrsResource, type Patient, type Visit } from '@openmrs/esm-framework';
|
|
2
|
+
|
|
3
|
+
export interface Queue {
|
|
4
|
+
uuid: string;
|
|
5
|
+
display: string;
|
|
6
|
+
name: string;
|
|
7
|
+
description: string;
|
|
8
|
+
location: Location;
|
|
9
|
+
service: Concept;
|
|
10
|
+
allowedPriorities: Array<Concept>;
|
|
11
|
+
allowedStatuses: Array<Concept>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface QueueEntry {
|
|
15
|
+
uuid: string;
|
|
16
|
+
display: string;
|
|
17
|
+
endedAt: string;
|
|
18
|
+
locationWaitingFor: Location;
|
|
19
|
+
patient: Patient;
|
|
20
|
+
priority: Concept;
|
|
21
|
+
priorityComment: string | null;
|
|
22
|
+
providerWaitingFor: Provider;
|
|
23
|
+
queue: Queue;
|
|
24
|
+
startedAt: string;
|
|
25
|
+
status: Concept;
|
|
26
|
+
visit: Visit;
|
|
27
|
+
sortWeight: number;
|
|
28
|
+
queueComingFrom: Queue;
|
|
29
|
+
previousQueueEntry: QueueEntry;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface Provider extends OpenmrsResource {}
|
|
33
|
+
|
|
34
|
+
export interface Encounter {
|
|
35
|
+
uuid: string;
|
|
36
|
+
encounterDateTime: string;
|
|
37
|
+
encounterProviders: Array<{
|
|
38
|
+
uuid: string;
|
|
39
|
+
display: string;
|
|
40
|
+
encounterRole: {
|
|
41
|
+
uuid: string;
|
|
42
|
+
display: string;
|
|
43
|
+
};
|
|
44
|
+
provider: {
|
|
45
|
+
uuid: string;
|
|
46
|
+
person: {
|
|
47
|
+
uuid: string;
|
|
48
|
+
display: string;
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
}>;
|
|
52
|
+
encounterType: {
|
|
53
|
+
uuid: string;
|
|
54
|
+
display: string;
|
|
55
|
+
};
|
|
56
|
+
obs: Array<OpenmrsResource>;
|
|
57
|
+
orders: Array<OpenmrsResource>;
|
|
58
|
+
}
|