@openmrs/esm-fast-data-entry-app 1.0.0-pre.59 → 1.0.0
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/README.md +21 -2
- package/__mocks__/react-i18next.js +9 -14
- package/dist/101.js +1 -0
- package/dist/101.js.map +1 -0
- package/dist/132.js +1 -1
- package/dist/188.js +1 -0
- package/dist/188.js.map +1 -0
- package/dist/197.js +1 -0
- package/dist/219.js +1 -0
- package/dist/219.js.map +1 -0
- package/dist/221.js +1 -0
- package/dist/221.js.map +1 -0
- package/dist/259.js +1 -0
- package/dist/259.js.map +1 -0
- package/dist/29.js +2 -0
- package/dist/29.js.LICENSE.txt +3 -0
- package/dist/29.js.map +1 -0
- package/dist/300.js +1 -0
- package/dist/326.js +1 -0
- package/dist/326.js.map +1 -0
- package/dist/335.js +1 -0
- package/dist/367.js +1 -0
- package/dist/367.js.map +1 -0
- package/dist/480.js +1 -0
- package/dist/540.js +2 -0
- package/dist/{536.js.LICENSE.txt → 540.js.LICENSE.txt} +3 -2
- package/dist/540.js.map +1 -0
- package/dist/55.js +1 -0
- package/dist/564.js +1 -0
- package/dist/564.js.map +1 -0
- package/dist/602.js +1 -0
- package/dist/602.js.map +1 -0
- package/dist/626.js +2 -0
- package/dist/{294.js.LICENSE.txt → 626.js.LICENSE.txt} +3 -8
- package/dist/626.js.map +1 -0
- package/dist/652.js +1 -0
- package/dist/685.js +1 -0
- package/dist/685.js.map +1 -0
- package/dist/773.js +2 -0
- package/dist/773.js.LICENSE.txt +32 -0
- package/dist/773.js.map +1 -0
- package/dist/893.js +1 -0
- package/dist/893.js.map +1 -0
- package/dist/91.js +1 -0
- package/dist/91.js.map +1 -0
- package/dist/941.js +2 -0
- package/dist/941.js.LICENSE.txt +30 -0
- package/dist/941.js.map +1 -0
- package/dist/961.js +2 -0
- package/dist/{935.js.LICENSE.txt → 961.js.LICENSE.txt} +6 -10
- package/dist/961.js.map +1 -0
- package/dist/99.js +1 -0
- package/dist/99.js.map +1 -0
- package/dist/991.js +1 -0
- package/dist/991.js.map +1 -0
- package/dist/main.js +1 -0
- package/dist/main.js.map +1 -0
- package/dist/openmrs-esm-fast-data-entry-app.js +1 -1
- package/dist/openmrs-esm-fast-data-entry-app.js.buildmanifest.json +500 -122
- package/dist/openmrs-esm-fast-data-entry-app.js.map +1 -0
- package/dist/routes.json +1 -0
- package/jest.config.json +21 -18
- package/package.json +59 -62
- package/prettier.config.js +8 -0
- package/src/CancelModal.tsx +42 -0
- package/src/CompleteModal.tsx +35 -0
- package/src/FormBootstrap.tsx +45 -10
- package/src/Root.tsx +11 -9
- package/src/add-group-modal/AddGroupModal.tsx +249 -0
- package/src/add-group-modal/styles.scss +49 -0
- package/src/config-schema.ts +77 -16
- package/src/constant.ts +1 -1
- package/src/context/FormWorkflowContext.tsx +32 -33
- package/src/context/FormWorkflowReducer.ts +53 -67
- package/src/context/GroupFormWorkflowContext.tsx +155 -0
- package/src/context/GroupFormWorkflowReducer.ts +405 -0
- package/src/declarations.d.ts +4 -0
- package/src/empty-state/EmptyDataIllustration.tsx +4 -16
- package/src/empty-state/EmptyState.tsx +16 -17
- package/src/empty-state/styles.scss +14 -14
- package/src/form-entry-workflow/FormEntryWorkflow.tsx +89 -125
- package/src/{form-review-card → form-entry-workflow/form-review-card}/FormReviewCard.tsx +7 -7
- package/src/form-entry-workflow/form-review-card/index.ts +3 -0
- package/src/form-entry-workflow/form-review-card/styles.scss +37 -0
- package/src/form-entry-workflow/index.ts +1 -1
- package/src/form-entry-workflow/patient-banner/PatientBanner.test.tsx +9 -0
- package/src/{patient-banner → form-entry-workflow/patient-banner}/PatientBanner.tsx +14 -27
- package/src/form-entry-workflow/patient-banner/index.ts +3 -0
- package/src/form-entry-workflow/patient-banner/styles.scss +44 -0
- package/src/form-entry-workflow/patient-search-header/PatientSearchHeader.tsx +54 -0
- package/src/form-entry-workflow/patient-search-header/index.ts +3 -0
- package/src/form-entry-workflow/patient-search-header/styles.scss +25 -0
- package/src/form-entry-workflow/styles.scss +16 -16
- package/src/form-entry-workflow/workflow-review/WorkflowReview.tsx +37 -0
- package/src/form-entry-workflow/workflow-review/index.ts +3 -0
- package/src/{workflow-review → form-entry-workflow/workflow-review}/styles.scss +0 -4
- package/src/forms-app-menu-link.tsx +5 -7
- package/src/forms-page/FormsPage.tsx +48 -37
- package/src/forms-page/forms-table/FormsTable.tsx +117 -0
- package/src/forms-page/forms-table/index.ts +3 -0
- package/src/forms-page/forms-table/styles.scss +19 -0
- package/src/forms-page/index.ts +1 -1
- package/src/forms-page/styles.scss +3 -5
- package/src/group-form-entry-workflow/GroupFormEntryWorkflow.tsx +26 -0
- package/src/group-form-entry-workflow/GroupSessionWorkspace.tsx +207 -0
- package/src/group-form-entry-workflow/SessionDetailsForm.tsx +154 -0
- package/src/group-form-entry-workflow/SessionMetaWorkspace.tsx +99 -0
- package/src/group-form-entry-workflow/attendance-table/AttendanceTable.tsx +130 -0
- package/src/group-form-entry-workflow/attendance-table/index.ts +1 -0
- package/src/group-form-entry-workflow/configurable-questions/ConfigurableQuestionsSection.tsx +41 -0
- package/src/group-form-entry-workflow/group-display-header/GroupDisplayHeader.test.tsx +9 -0
- package/src/group-form-entry-workflow/group-display-header/GroupDisplayHeader.tsx +55 -0
- package/src/group-form-entry-workflow/group-display-header/index.ts +3 -0
- package/src/group-form-entry-workflow/group-display-header/styles.scss +60 -0
- package/src/group-form-entry-workflow/group-search/CompactGroupResults.tsx +128 -0
- package/src/group-form-entry-workflow/group-search/CompactGroupSearch.tsx +66 -0
- package/src/group-form-entry-workflow/group-search/GroupSearch.tsx +134 -0
- package/src/group-form-entry-workflow/group-search/compact-group-result.scss +63 -0
- package/src/group-form-entry-workflow/group-search/compact-group-search.scss +34 -0
- package/src/group-form-entry-workflow/group-search/group-search.scss +93 -0
- package/src/group-form-entry-workflow/group-search-header/GroupSearchHeader.tsx +72 -0
- package/src/group-form-entry-workflow/group-search-header/index.ts +3 -0
- package/src/group-form-entry-workflow/group-search-header/styles.scss +20 -0
- package/src/group-form-entry-workflow/index.ts +3 -0
- package/src/group-form-entry-workflow/styles.scss +94 -0
- package/src/hooks/index.ts +7 -5
- package/src/hooks/useForm.ts +56 -0
- package/src/hooks/useFormState.ts +3 -3
- package/src/hooks/useGetAllForms.ts +7 -15
- package/src/hooks/useGetEncounter.ts +3 -3
- package/src/hooks/useGetPatient.ts +3 -3
- package/src/hooks/useGetPatients.ts +32 -0
- package/src/hooks/useGetSystemSetting.ts +36 -0
- package/src/hooks/useKeyPress.ts +31 -0
- package/src/hooks/usePostEndpoint.ts +76 -0
- package/src/hooks/useSearchEndpoint.ts +103 -0
- package/src/hooks/useStartVisit.ts +82 -0
- package/src/index.ts +12 -72
- package/src/patient-card/PatientCard.tsx +10 -20
- package/src/patient-card/index.ts +1 -1
- package/src/patient-card/styles.scss +8 -8
- package/src/routes.json +24 -0
- package/src/setup-tests.ts +1 -1
- package/src/types.ts +20 -0
- package/tools/i18next-parser.config.js +93 -0
- package/translations/am.json +75 -0
- package/translations/ar.json +75 -0
- package/translations/en.json +57 -2
- package/translations/es.json +75 -0
- package/translations/fr.json +75 -0
- package/translations/he.json +75 -0
- package/translations/km.json +75 -0
- package/turbo.json +18 -0
- package/webpack.config.js +1 -1
- package/.editorconfig +0 -12
- package/.eslintignore +0 -2
- package/.eslintrc.js +0 -10
- package/.github/pull_request_template.md +0 -18
- package/.github/workflows/node.js.yml +0 -121
- package/.husky/pre-push +0 -1
- package/.prettierignore +0 -14
- package/dist/187.js +0 -1
- package/dist/247.js +0 -1
- package/dist/294.js +0 -2
- package/dist/312.js +0 -1
- package/dist/412.js +0 -1
- package/dist/536.js +0 -2
- package/dist/574.js +0 -1
- package/dist/592.js +0 -1
- package/dist/595.js +0 -2
- package/dist/595.js.LICENSE.txt +0 -1
- package/dist/776.js +0 -1
- package/dist/804.js +0 -1
- package/dist/880.js +0 -2
- package/dist/880.js.LICENSE.txt +0 -20
- package/dist/906.js +0 -1
- package/dist/935.js +0 -2
- package/dist/990.js +0 -1
- package/dist/openmrs-esm-fast-data-entry-app.old +0 -1
- package/src/declarations.d.tsx +0 -2
- package/src/form-review-card/index.ts +0 -3
- package/src/form-review-card/styles.scss +0 -38
- package/src/forms-table/FormsTable.tsx +0 -123
- package/src/forms-table/index.ts +0 -3
- package/src/forms-table/styles.scss +0 -20
- package/src/patient-banner/PatientBanner.test.tsx +0 -9
- package/src/patient-banner/index.ts +0 -3
- package/src/patient-banner/styles.scss +0 -44
- package/src/patient-search-header/PatientSearchHeader.tsx +0 -61
- package/src/patient-search-header/index.ts +0 -3
- package/src/patient-search-header/styles.scss +0 -21
- package/src/workflow-review/WorkflowReview.tsx +0 -35
- package/src/workflow-review/index.ts +0 -3
|
@@ -1,107 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
import
|
|
14
|
-
import
|
|
15
|
-
import
|
|
16
|
-
import PatientCard from "../patient-card/PatientCard";
|
|
17
|
-
import PatientBanner from "../patient-banner";
|
|
18
|
-
import styles from "./styles.scss";
|
|
19
|
-
import PatientSearchHeader from "../patient-search-header";
|
|
20
|
-
import { useTranslation } from "react-i18next";
|
|
21
|
-
import FormWorkflowContext, {
|
|
22
|
-
FormWorkflowProvider,
|
|
23
|
-
} from "../context/FormWorkflowContext";
|
|
24
|
-
import WorkflowReview from "../workflow-review";
|
|
25
|
-
|
|
26
|
-
const formStore = getGlobalStore("ampath-form-state");
|
|
27
|
-
|
|
28
|
-
const CancelModal = ({ open, setOpen }) => {
|
|
29
|
-
const { destroySession, closeSession } = useContext(FormWorkflowContext);
|
|
30
|
-
const { t } = useTranslation();
|
|
31
|
-
const history = useHistory();
|
|
32
|
-
|
|
33
|
-
const discard = () => {
|
|
34
|
-
destroySession();
|
|
35
|
-
setOpen(false);
|
|
36
|
-
history.push("/");
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const saveAndClose = () => {
|
|
40
|
-
closeSession();
|
|
41
|
-
setOpen(false);
|
|
42
|
-
history.push("/");
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
return (
|
|
46
|
-
<ComposedModal open={open}>
|
|
47
|
-
<ModalHeader>{t("areYouSure", "Are you sure?")}</ModalHeader>
|
|
48
|
-
<ModalBody>
|
|
49
|
-
{t(
|
|
50
|
-
"cancelExplanation",
|
|
51
|
-
"You will lose any unsaved changes on the current form. Do you want to discard the current session?"
|
|
52
|
-
)}
|
|
53
|
-
</ModalBody>
|
|
54
|
-
<ModalFooter>
|
|
55
|
-
<Button kind="secondary" onClick={() => setOpen(false)}>
|
|
56
|
-
{t("cancel", "Cancel")}
|
|
57
|
-
</Button>
|
|
58
|
-
<Button kind="danger" onClick={discard}>
|
|
59
|
-
{t("discard", "Discard")}
|
|
60
|
-
</Button>
|
|
61
|
-
<Button kind="primary" onClick={saveAndClose}>
|
|
62
|
-
{t("saveSession", "Save Session")}
|
|
63
|
-
</Button>
|
|
64
|
-
</ModalFooter>
|
|
65
|
-
</ComposedModal>
|
|
66
|
-
);
|
|
67
|
-
};
|
|
68
|
-
|
|
69
|
-
const CompleteModal = ({ open, setOpen }) => {
|
|
70
|
-
const { submitForComplete } = useContext(FormWorkflowContext);
|
|
71
|
-
const { t } = useTranslation();
|
|
72
|
-
|
|
73
|
-
const completeSession = () => {
|
|
74
|
-
submitForComplete();
|
|
75
|
-
setOpen(false);
|
|
76
|
-
};
|
|
77
|
-
|
|
78
|
-
return (
|
|
79
|
-
<ComposedModal open={open}>
|
|
80
|
-
<ModalHeader>{t("areYouSure", "Are you sure?")}</ModalHeader>
|
|
81
|
-
<ModalBody>
|
|
82
|
-
{t(
|
|
83
|
-
"saveExplanation",
|
|
84
|
-
"Do you want to save the current form and exit the workflow?"
|
|
85
|
-
)}
|
|
86
|
-
</ModalBody>
|
|
87
|
-
<ModalFooter>
|
|
88
|
-
<Button kind="secondary" onClick={() => setOpen(false)}>
|
|
89
|
-
{t("cancel", "Cancel")}
|
|
90
|
-
</Button>
|
|
91
|
-
<Button kind="primary" onClick={completeSession}>
|
|
92
|
-
{t("complete", "Complete")}
|
|
93
|
-
</Button>
|
|
94
|
-
</ModalFooter>
|
|
95
|
-
</ComposedModal>
|
|
96
|
-
);
|
|
97
|
-
};
|
|
1
|
+
import { ExtensionSlot, useSession } from '@openmrs/esm-framework';
|
|
2
|
+
import { Button } from '@carbon/react';
|
|
3
|
+
import React, { useCallback, useContext, useEffect, useState } from 'react';
|
|
4
|
+
import FormBootstrap from '../FormBootstrap';
|
|
5
|
+
import PatientCard from '../patient-card/PatientCard';
|
|
6
|
+
import styles from './styles.scss';
|
|
7
|
+
import PatientSearchHeader from './patient-search-header';
|
|
8
|
+
import { useTranslation } from 'react-i18next';
|
|
9
|
+
import { v4 as uuid } from 'uuid';
|
|
10
|
+
import FormWorkflowContext, { FormWorkflowProvider } from '../context/FormWorkflowContext';
|
|
11
|
+
import WorkflowReview from './workflow-review';
|
|
12
|
+
import PatientBanner from './patient-banner';
|
|
13
|
+
import CompleteModal from '../CompleteModal';
|
|
14
|
+
import CancelModal from '../CancelModal';
|
|
15
|
+
import useStartVisit from '../hooks/useStartVisit';
|
|
98
16
|
|
|
99
17
|
const WorkflowNavigationButtons = () => {
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
const store = useStore(formStore);
|
|
103
|
-
const formState = store[activeFormUuid];
|
|
104
|
-
const navigationDisabled = formState !== "ready";
|
|
18
|
+
const context = useContext(FormWorkflowContext);
|
|
19
|
+
const { workflowState, destroySession } = context;
|
|
105
20
|
const [cancelModalOpen, setCancelModalOpen] = useState(false);
|
|
106
21
|
const [completeModalOpen, setCompleteModalOpen] = useState(false);
|
|
107
22
|
const { t } = useTranslation();
|
|
@@ -111,29 +26,18 @@ const WorkflowNavigationButtons = () => {
|
|
|
111
26
|
return (
|
|
112
27
|
<>
|
|
113
28
|
<div className={styles.rightPanelActionButtons}>
|
|
114
|
-
<Button
|
|
115
|
-
kind="primary"
|
|
116
|
-
onClick={() => submitForNext()}
|
|
117
|
-
disabled={navigationDisabled || workflowState === "NEW_PATIENT"}
|
|
118
|
-
>
|
|
119
|
-
{t("nextPatient", "Next Patient")}
|
|
120
|
-
</Button>
|
|
121
29
|
<Button
|
|
122
30
|
kind="secondary"
|
|
123
|
-
onClick={
|
|
124
|
-
workflowState === "NEW_PATIENT"
|
|
125
|
-
? () => destroySession()
|
|
126
|
-
: () => setCompleteModalOpen(true)
|
|
127
|
-
}
|
|
31
|
+
onClick={workflowState === 'NEW_PATIENT' ? () => destroySession() : () => setCompleteModalOpen(true)}
|
|
128
32
|
>
|
|
129
|
-
{t(
|
|
33
|
+
{t('saveAndComplete', 'Save & Complete')}
|
|
130
34
|
</Button>
|
|
131
35
|
<Button kind="tertiary" onClick={() => setCancelModalOpen(true)}>
|
|
132
|
-
{t(
|
|
36
|
+
{t('cancel', 'Cancel')}
|
|
133
37
|
</Button>
|
|
134
38
|
</div>
|
|
135
|
-
<CancelModal open={cancelModalOpen} setOpen={setCancelModalOpen} />
|
|
136
|
-
<CompleteModal open={completeModalOpen} setOpen={setCompleteModalOpen} />
|
|
39
|
+
<CancelModal open={cancelModalOpen} setOpen={setCancelModalOpen} context={context} />
|
|
40
|
+
<CompleteModal open={completeModalOpen} setOpen={setCompleteModalOpen} context={context} />
|
|
137
41
|
</>
|
|
138
42
|
);
|
|
139
43
|
};
|
|
@@ -145,21 +49,72 @@ const FormWorkspace = () => {
|
|
|
145
49
|
activeEncounterUuid,
|
|
146
50
|
saveEncounter,
|
|
147
51
|
activeFormUuid,
|
|
52
|
+
editEncounter,
|
|
53
|
+
encounters,
|
|
54
|
+
singleSessionVisitTypeUuid,
|
|
148
55
|
} = useContext(FormWorkflowContext);
|
|
149
56
|
const { t } = useTranslation();
|
|
150
57
|
|
|
58
|
+
const [encounter, setEncounter] = useState(null);
|
|
59
|
+
const [visit, setVisit] = useState(null);
|
|
60
|
+
const { sessionLocation } = useSession();
|
|
61
|
+
|
|
62
|
+
const { updateEncounter, success: visitSaveSuccess } = useStartVisit({
|
|
63
|
+
showSuccessNotification: false,
|
|
64
|
+
showErrorNotification: true,
|
|
65
|
+
});
|
|
66
|
+
|
|
151
67
|
const handlePostResponse = (encounter) => {
|
|
152
68
|
if (encounter && encounter.uuid) {
|
|
153
69
|
saveEncounter(encounter.uuid);
|
|
70
|
+
setEncounter(encounter);
|
|
154
71
|
}
|
|
155
72
|
};
|
|
156
73
|
|
|
74
|
+
useEffect(() => {
|
|
75
|
+
if (encounter && visit) {
|
|
76
|
+
// Update encounter so that it belongs to the created visit
|
|
77
|
+
updateEncounter({ uuid: encounter.uuid, visit: visit.uuid });
|
|
78
|
+
}
|
|
79
|
+
}, [encounter, visit, updateEncounter]);
|
|
80
|
+
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
if (visitSaveSuccess) {
|
|
83
|
+
setVisit(visitSaveSuccess.data);
|
|
84
|
+
}
|
|
85
|
+
}, [visitSaveSuccess]);
|
|
86
|
+
|
|
87
|
+
const handleEncounterCreate = useCallback(
|
|
88
|
+
(payload) => {
|
|
89
|
+
payload.location = sessionLocation?.uuid;
|
|
90
|
+
payload.encounterDatetime = payload.encounterDatetime ? payload.encounterDatetime : new Date().toISOString();
|
|
91
|
+
// Create a visit with the same date as the encounter being saved
|
|
92
|
+
const visitStartDatetime = new Date(payload.encounterDatetime);
|
|
93
|
+
const visitStopDatetime = new Date(payload.encounterDatetime);
|
|
94
|
+
const visitInfo = {
|
|
95
|
+
startDatetime: visitStartDatetime,
|
|
96
|
+
stopDatetime: visitStopDatetime,
|
|
97
|
+
uuid: uuid(),
|
|
98
|
+
patient: {
|
|
99
|
+
uuid: activePatientUuid,
|
|
100
|
+
},
|
|
101
|
+
location: {
|
|
102
|
+
uuid: sessionLocation?.uuid,
|
|
103
|
+
},
|
|
104
|
+
visitType: {
|
|
105
|
+
uuid: singleSessionVisitTypeUuid,
|
|
106
|
+
},
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
payload.visit = visitInfo;
|
|
110
|
+
},
|
|
111
|
+
[activePatientUuid, singleSessionVisitTypeUuid, sessionLocation],
|
|
112
|
+
);
|
|
113
|
+
|
|
157
114
|
return (
|
|
158
115
|
<div className={styles.workspace}>
|
|
159
116
|
{!patientUuids.length && (
|
|
160
|
-
<div className={styles.selectPatientMessage}>
|
|
161
|
-
{t("selectPatientFirst", "Please select a patient first")}
|
|
162
|
-
</div>
|
|
117
|
+
<div className={styles.selectPatientMessage}>{t('selectPatientFirst', 'Please select a patient first')}</div>
|
|
163
118
|
)}
|
|
164
119
|
{!!patientUuids.length && (
|
|
165
120
|
<div className={styles.formMainContent}>
|
|
@@ -170,14 +125,23 @@ const FormWorkspace = () => {
|
|
|
170
125
|
{...{
|
|
171
126
|
formUuid: activeFormUuid,
|
|
172
127
|
handlePostResponse,
|
|
128
|
+
handleEncounterCreate,
|
|
173
129
|
}}
|
|
174
130
|
/>
|
|
175
131
|
</div>
|
|
176
132
|
<div className={styles.rightPanel}>
|
|
177
|
-
<h4>Forms filled</h4>
|
|
133
|
+
<h4>{t('formsFilled', 'Forms filled')}</h4>
|
|
178
134
|
<div className={styles.patientCardsSection}>
|
|
179
135
|
{patientUuids.map((patientUuid) => (
|
|
180
|
-
<PatientCard
|
|
136
|
+
<PatientCard
|
|
137
|
+
key={patientUuid}
|
|
138
|
+
{...{
|
|
139
|
+
patientUuid,
|
|
140
|
+
activePatientUuid,
|
|
141
|
+
editEncounter,
|
|
142
|
+
encounters,
|
|
143
|
+
}}
|
|
144
|
+
/>
|
|
181
145
|
))}
|
|
182
146
|
</div>
|
|
183
147
|
<WorkflowNavigationButtons />
|
|
@@ -195,8 +159,8 @@ const FormEntryWorkflow = () => {
|
|
|
195
159
|
<div className={styles.breadcrumbsContainer}>
|
|
196
160
|
<ExtensionSlot extensionSlotName="breadcrumbs-slot" />
|
|
197
161
|
</div>
|
|
198
|
-
{workflowState ===
|
|
199
|
-
{workflowState !==
|
|
162
|
+
{workflowState === 'REVIEW' && <WorkflowReview />}
|
|
163
|
+
{workflowState !== 'REVIEW' && (
|
|
200
164
|
<>
|
|
201
165
|
<PatientSearchHeader />
|
|
202
166
|
<PatientBanner />
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { Accordion, AccordionItem, Button } from
|
|
2
|
-
import React, { useContext } from
|
|
3
|
-
import { useTranslation } from
|
|
4
|
-
import FormWorkflowContext from
|
|
5
|
-
import { useGetPatient, useGetEncounter } from
|
|
6
|
-
import styles from
|
|
1
|
+
import { Accordion, AccordionItem, Button } from '@carbon/react';
|
|
2
|
+
import React, { useContext } from 'react';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import FormWorkflowContext from '../../context/FormWorkflowContext';
|
|
5
|
+
import { useGetPatient, useGetEncounter } from '../../hooks';
|
|
6
|
+
import styles from './styles.scss';
|
|
7
7
|
|
|
8
8
|
const FormReviewCard = ({ patientUuid }) => {
|
|
9
9
|
const { encounters, editEncounter } = useContext(FormWorkflowContext);
|
|
@@ -39,7 +39,7 @@ const FormReviewCard = ({ patientUuid }) => {
|
|
|
39
39
|
</div>
|
|
40
40
|
)}
|
|
41
41
|
<Button kind="primary" onClick={() => editEncounter(patientUuid)}>
|
|
42
|
-
{t(
|
|
42
|
+
{t('goToForm', 'Go To Form')}
|
|
43
43
|
</Button>
|
|
44
44
|
</AccordionItem>
|
|
45
45
|
</Accordion>
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
@use '@carbon/colors';
|
|
2
|
+
@use '@carbon/layout';
|
|
3
|
+
@use '@carbon/type';
|
|
4
|
+
|
|
5
|
+
.formReviewCard {
|
|
6
|
+
background-color: colors.$white-0;
|
|
7
|
+
padding: layout.$spacing-02;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
.formReviewCard :global(.cds--accordion) :global(.cds--accordion__item) {
|
|
11
|
+
border: none;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.formReviewCard :global(.cds--accordion__title) {
|
|
15
|
+
display: flex;
|
|
16
|
+
align-items: baseline;
|
|
17
|
+
column-gap: layout.$spacing-05;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.formReviewCard :global(.cds--accordion__content) {
|
|
21
|
+
padding: layout.$spacing-03;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
.dataField {
|
|
25
|
+
@include type.type-style('code-02');
|
|
26
|
+
background-color: colors.$gray-10;
|
|
27
|
+
padding: layout.$spacing-03;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.displayName {
|
|
31
|
+
@include type.type-style('heading-02');
|
|
32
|
+
font-weight: bold;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
.identifier {
|
|
36
|
+
@include type.type-style('body-compact-01')
|
|
37
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render } from '@testing-library/react';
|
|
3
|
+
import PatientBanner from './PatientBanner';
|
|
4
|
+
|
|
5
|
+
describe('PatientBanner', () => {
|
|
6
|
+
it('renders placeholder information when no data is present', () => {
|
|
7
|
+
render(<PatientBanner />);
|
|
8
|
+
});
|
|
9
|
+
});
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { age, ExtensionSlot } from
|
|
2
|
-
import { SkeletonPlaceholder, SkeletonText } from
|
|
3
|
-
import React, { useContext } from
|
|
4
|
-
import styles from
|
|
5
|
-
import { useTranslation } from
|
|
6
|
-
import useGetPatient from
|
|
7
|
-
import FormWorkflowContext from
|
|
1
|
+
import { age, ExtensionSlot } from '@openmrs/esm-framework';
|
|
2
|
+
import { SkeletonPlaceholder, SkeletonText } from '@carbon/react';
|
|
3
|
+
import React, { useContext } from 'react';
|
|
4
|
+
import styles from './styles.scss';
|
|
5
|
+
import { useTranslation } from 'react-i18next';
|
|
6
|
+
import useGetPatient from '../../hooks/useGetPatient';
|
|
7
|
+
import FormWorkflowContext from '../../context/FormWorkflowContext';
|
|
8
8
|
|
|
9
9
|
const SkeletonPatientInfo = () => {
|
|
10
10
|
return (
|
|
@@ -36,16 +36,14 @@ const PatientBanner = () => {
|
|
|
36
36
|
const { activePatientUuid, workflowState } = useContext(FormWorkflowContext);
|
|
37
37
|
const patient = useGetPatient(activePatientUuid);
|
|
38
38
|
const { t } = useTranslation();
|
|
39
|
-
const patientName = `${patient?.name?.[0].given?.join(
|
|
40
|
-
patient?.name?.[0]?.family
|
|
41
|
-
}`;
|
|
39
|
+
const patientName = `${patient?.name?.[0].given?.join(' ')} ${patient?.name?.[0]?.family}`;
|
|
42
40
|
|
|
43
41
|
const patientPhotoSlotState = React.useMemo(
|
|
44
|
-
() => ({ patientUuid: patient?.id, patientName, size:
|
|
45
|
-
[patient?.id, patientName]
|
|
42
|
+
() => ({ patientUuid: patient?.id, patientName, size: 'small' }),
|
|
43
|
+
[patient?.id, patientName],
|
|
46
44
|
);
|
|
47
45
|
|
|
48
|
-
if (workflowState ===
|
|
46
|
+
if (workflowState === 'NEW_PATIENT') return null;
|
|
49
47
|
|
|
50
48
|
if (!patient) {
|
|
51
49
|
return <SkeletonPatientInfo />;
|
|
@@ -53,29 +51,18 @@ const PatientBanner = () => {
|
|
|
53
51
|
|
|
54
52
|
return (
|
|
55
53
|
<div className={styles.container}>
|
|
56
|
-
<ExtensionSlot
|
|
57
|
-
extensionSlotName="patient-photo-slot"
|
|
58
|
-
state={patientPhotoSlotState}
|
|
59
|
-
/>
|
|
54
|
+
<ExtensionSlot extensionSlotName="patient-photo-slot" state={patientPhotoSlotState} />
|
|
60
55
|
<div className={styles.patientInfoContent}>
|
|
61
56
|
<div className={styles.patientInfoRow}>
|
|
62
57
|
<span className={styles.patientName}>{patientName}</span>
|
|
63
58
|
</div>
|
|
64
59
|
<div className={styles.patientInfoRow}>
|
|
65
|
-
<span>
|
|
66
|
-
{(patient.gender ?? t("unknown", "Unknown")).replace(/^\w/, (c) =>
|
|
67
|
-
c.toUpperCase()
|
|
68
|
-
)}
|
|
69
|
-
</span>
|
|
60
|
+
<span>{(patient.gender ?? t('unknown', 'Unknown')).replace(/^\w/, (c) => c.toUpperCase())}</span>
|
|
70
61
|
<span>·</span>
|
|
71
62
|
<span>{age(patient.birthDate)}</span>
|
|
72
63
|
<span>·</span>
|
|
73
64
|
<span>
|
|
74
|
-
{patient.identifier.length
|
|
75
|
-
? patient.identifier
|
|
76
|
-
.map((identifier) => identifier.value)
|
|
77
|
-
.join(", ")
|
|
78
|
-
: "--"}
|
|
65
|
+
{patient.identifier.length ? patient.identifier.map((identifier) => identifier.value).join(', ') : '--'}
|
|
79
66
|
</span>
|
|
80
67
|
</div>
|
|
81
68
|
</div>
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
@use '@carbon/colors';
|
|
2
|
+
@use '@carbon/layout';
|
|
3
|
+
@use '@carbon/type';
|
|
4
|
+
|
|
5
|
+
.container {
|
|
6
|
+
height: layout.$spacing-11;
|
|
7
|
+
display: flex;
|
|
8
|
+
align-items: center;
|
|
9
|
+
background-color: colors.$white-0;
|
|
10
|
+
border-top: 0.0125rem solid colors.$gray-20;
|
|
11
|
+
border-bottom: 0.0125rem solid colors.$gray-20;
|
|
12
|
+
padding: 0 layout.$spacing-05;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.photoPlaceholder {
|
|
16
|
+
height: 48px;
|
|
17
|
+
width: 48px;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
.patientName {
|
|
21
|
+
@include type.type-style('heading-03');
|
|
22
|
+
font-weight: 600;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
.patientInfoContent {
|
|
26
|
+
width: 100%;
|
|
27
|
+
margin-left: 1rem;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
.patientInfoRow {
|
|
31
|
+
display: flex;
|
|
32
|
+
align-items: center;
|
|
33
|
+
& > button {
|
|
34
|
+
min-height: 2rem;
|
|
35
|
+
}
|
|
36
|
+
@include type.type-style('body-compact-02');
|
|
37
|
+
color: colors.$gray-70;
|
|
38
|
+
column-gap: 0.8rem;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
.patientEditBtn {
|
|
42
|
+
color: colors.$gray-100;
|
|
43
|
+
margin: layout.$spacing-03;
|
|
44
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { Add, Close } from '@carbon/react/icons';
|
|
2
|
+
import { ExtensionSlot, interpolateUrl, navigate } from '@openmrs/esm-framework';
|
|
3
|
+
import { Button } from '@carbon/react';
|
|
4
|
+
import React, { useContext } from 'react';
|
|
5
|
+
import { Link } from 'react-router-dom';
|
|
6
|
+
import FormWorkflowContext from '../../context/FormWorkflowContext';
|
|
7
|
+
import styles from './styles.scss';
|
|
8
|
+
import { useTranslation } from 'react-i18next';
|
|
9
|
+
|
|
10
|
+
const PatientSearchHeader = () => {
|
|
11
|
+
const { addPatient, workflowState, activeFormUuid } = useContext(FormWorkflowContext);
|
|
12
|
+
const handleSelectPatient = (patient) => {
|
|
13
|
+
addPatient(patient);
|
|
14
|
+
};
|
|
15
|
+
const { t } = useTranslation();
|
|
16
|
+
|
|
17
|
+
if (workflowState !== 'NEW_PATIENT') return null;
|
|
18
|
+
|
|
19
|
+
const afterUrl = encodeURIComponent(`\${openmrsSpaBase}/forms/form/${activeFormUuid}?patientUuid=\${patientUuid}`);
|
|
20
|
+
const patientRegistrationUrl = interpolateUrl(`\${openmrsSpaBase}/patient-registration?afterUrl=${afterUrl}`);
|
|
21
|
+
|
|
22
|
+
return (
|
|
23
|
+
<div className={styles.searchHeaderContainer}>
|
|
24
|
+
<span className={styles.padded}>{t('nextPatient', 'Next patient')}:</span>
|
|
25
|
+
<span className={styles.searchBarWrapper}>
|
|
26
|
+
<ExtensionSlot
|
|
27
|
+
name="patient-search-bar-slot"
|
|
28
|
+
state={{
|
|
29
|
+
selectPatientAction: handleSelectPatient,
|
|
30
|
+
buttonProps: {
|
|
31
|
+
kind: 'primary',
|
|
32
|
+
},
|
|
33
|
+
}}
|
|
34
|
+
/>
|
|
35
|
+
</span>
|
|
36
|
+
<span className={styles.padded}>{t('or', 'or')}</span>
|
|
37
|
+
<span>
|
|
38
|
+
<Button onClick={() => navigate({ to: patientRegistrationUrl })}>
|
|
39
|
+
{t('createNewPatient', 'Create new patient')} <Add size={20} />
|
|
40
|
+
</Button>
|
|
41
|
+
</span>
|
|
42
|
+
<span style={{ flexGrow: 1 }} />
|
|
43
|
+
<span>
|
|
44
|
+
<Link to="../">
|
|
45
|
+
<Button kind="ghost">
|
|
46
|
+
{t('cancel', 'Cancel')} <Close size={20} />
|
|
47
|
+
</Button>
|
|
48
|
+
</Link>
|
|
49
|
+
</span>
|
|
50
|
+
</div>
|
|
51
|
+
);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export default PatientSearchHeader;
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
@use '@carbon/colors';
|
|
2
|
+
@use '@carbon/layout';
|
|
3
|
+
@use '@carbon/type';
|
|
4
|
+
|
|
5
|
+
.searchHeaderContainer {
|
|
6
|
+
height: layout.$spacing-11;
|
|
7
|
+
display: flex;
|
|
8
|
+
align-items: center;
|
|
9
|
+
background-color: colors.$white-0;
|
|
10
|
+
border-top: 0.0125rem solid colors.$gray-20;
|
|
11
|
+
border-bottom: 0.0125rem solid colors.$gray-20;
|
|
12
|
+
padding: 0 layout.$spacing-05;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
.searchBarWrapper {
|
|
16
|
+
min-width: 35rem;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
.searchBarWrapper > div button {
|
|
20
|
+
height: auto;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
.padded {
|
|
24
|
+
padding: layout.$spacing-05;
|
|
25
|
+
}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
@
|
|
2
|
-
@
|
|
3
|
-
@
|
|
4
|
-
|
|
1
|
+
@use '@carbon/colors';
|
|
2
|
+
@use '@carbon/layout';
|
|
3
|
+
@use '@carbon/type';
|
|
5
4
|
|
|
6
5
|
.breadcrumbsContainer > div > div > nav {
|
|
7
|
-
background-color:
|
|
8
|
-
padding:
|
|
9
|
-
height:
|
|
6
|
+
background-color: colors.$white-0;
|
|
7
|
+
padding: layout.$spacing-04 layout.$spacing-05;
|
|
8
|
+
height: layout.$spacing-08;
|
|
10
9
|
}
|
|
11
10
|
|
|
12
11
|
.workspaceWrapper {
|
|
@@ -19,43 +18,44 @@
|
|
|
19
18
|
}
|
|
20
19
|
|
|
21
20
|
.selectPatientMessage {
|
|
22
|
-
@include
|
|
23
|
-
margin:
|
|
21
|
+
@include type.type-style('heading-03');
|
|
22
|
+
margin: layout.$spacing-07;
|
|
24
23
|
text-align: center;
|
|
25
24
|
}
|
|
26
25
|
|
|
27
26
|
.formMainContent {
|
|
28
27
|
display: flex;
|
|
29
28
|
text-align: center;
|
|
30
|
-
margin-top:
|
|
31
|
-
column-gap:
|
|
29
|
+
margin-top: layout.$spacing-05;
|
|
30
|
+
column-gap: layout.$spacing-05;
|
|
32
31
|
}
|
|
33
32
|
|
|
34
33
|
.formContainer {
|
|
35
34
|
flex-grow: 1;
|
|
36
35
|
max-height: calc(100vh - 14rem);
|
|
37
36
|
overflow-y: scroll;
|
|
37
|
+
text-align: left;
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
-
.formContainer :global(.
|
|
40
|
+
.formContainer :global(.cds--form-item) :global(.question-area) {
|
|
41
41
|
max-width: 100%;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
.rightPanel {
|
|
45
|
-
width: 13rem;
|
|
45
|
+
min-width: 13rem;
|
|
46
46
|
text-align: left;
|
|
47
47
|
overflow-y: scroll;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
.patientCardsSection {
|
|
51
|
-
margin:
|
|
52
|
-
border-bottom: 1px solid
|
|
51
|
+
margin: layout.$spacing-05 0;
|
|
52
|
+
border-bottom: 1px solid colors.$gray-10;
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
.rightPanelActionButtons {
|
|
56
56
|
display: flex;
|
|
57
57
|
flex-direction: column;
|
|
58
|
-
row-gap:
|
|
58
|
+
row-gap: layout.$spacing-03;
|
|
59
59
|
& button {
|
|
60
60
|
width: 100%;
|
|
61
61
|
text-decoration: "none";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Button } from '@carbon/react';
|
|
2
|
+
import React, { useContext } from 'react';
|
|
3
|
+
import { useNavigate } from 'react-router-dom';
|
|
4
|
+
import FormWorkflowContext from '../../context/FormWorkflowContext';
|
|
5
|
+
import FormReviewCard from '../form-review-card';
|
|
6
|
+
import styles from './styles.scss';
|
|
7
|
+
import { useTranslation } from 'react-i18next';
|
|
8
|
+
|
|
9
|
+
const WorkflowReview = () => {
|
|
10
|
+
const { patientUuids } = useContext(FormWorkflowContext);
|
|
11
|
+
const { t } = useTranslation();
|
|
12
|
+
const navigate = useNavigate();
|
|
13
|
+
return (
|
|
14
|
+
<div className={styles.workspaceWrapper}>
|
|
15
|
+
<div className={styles.workspace}>
|
|
16
|
+
<div className={styles.leftPanel}>
|
|
17
|
+
<h4>{t('review', 'Review')}</h4>
|
|
18
|
+
<div className={styles.navButtons}>
|
|
19
|
+
<Button kind="primary" onClick={() => navigate('/')}>
|
|
20
|
+
{t('save&close', 'Save & Close')}
|
|
21
|
+
</Button>
|
|
22
|
+
<Button kind="tertiary" onClick={() => navigate('/')}>
|
|
23
|
+
{t('cancel', 'Cancel')}
|
|
24
|
+
</Button>
|
|
25
|
+
</div>
|
|
26
|
+
</div>
|
|
27
|
+
<div className={styles.formContainer}>
|
|
28
|
+
{patientUuids.map((patientUuid) => (
|
|
29
|
+
<FormReviewCard patientUuid={patientUuid} key={patientUuid} />
|
|
30
|
+
))}
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
33
|
+
</div>
|
|
34
|
+
);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export default WorkflowReview;
|