@openmrs/esm-fast-data-entry-app 1.0.1-pre.17 → 1.0.1-pre.176
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/143.js +1 -0
- package/dist/143.js.map +1 -0
- 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/31.js +2 -0
- package/dist/{569.js.LICENSE.txt → 31.js.LICENSE.txt} +9 -6
- package/dist/31.js.map +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/491.js +1 -0
- package/dist/491.js.map +1 -0
- package/dist/540.js +2 -0
- 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/626.js.LICENSE.txt +9 -0
- 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/{68.js.LICENSE.txt → 773.js.LICENSE.txt} +13 -2
- package/dist/773.js.map +1 -0
- package/dist/91.js +1 -0
- package/dist/91.js.map +1 -0
- package/dist/961.js +2 -0
- package/dist/961.js.map +1 -0
- package/dist/99.js +1 -0
- package/dist/99.js.map +1 -0
- package/dist/main.js +1 -1
- 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 +403 -136
- package/dist/openmrs-esm-fast-data-entry-app.js.map +1 -0
- package/dist/routes.json +1 -0
- package/jest.config.json +2 -1
- package/package.json +41 -38
- package/prettier.config.js +8 -0
- package/src/CancelModal.tsx +42 -0
- package/src/CompleteModal.tsx +35 -0
- package/src/FormBootstrap.tsx +39 -11
- package/src/Root.tsx +7 -12
- package/src/add-group-modal/AddGroupModal.tsx +107 -120
- package/src/add-group-modal/styles.scss +7 -3
- package/src/config-schema.ts +77 -16
- package/src/constant.ts +1 -1
- package/src/context/FormWorkflowContext.tsx +31 -32
- package/src/context/FormWorkflowReducer.ts +53 -67
- package/src/context/GroupFormWorkflowContext.tsx +56 -44
- package/src/context/GroupFormWorkflowReducer.ts +177 -68
- package/src/declarations.d.ts +4 -0
- package/src/empty-state/EmptyDataIllustration.tsx +4 -16
- package/src/empty-state/EmptyState.tsx +8 -13
- package/src/empty-state/styles.scss +14 -14
- package/src/form-entry-workflow/FormEntryWorkflow.tsx +78 -124
- package/src/form-entry-workflow/form-review-card/FormReviewCard.tsx +7 -7
- package/src/form-entry-workflow/form-review-card/index.ts +1 -1
- package/src/form-entry-workflow/form-review-card/styles.scss +9 -11
- package/src/form-entry-workflow/index.ts +1 -1
- package/src/form-entry-workflow/patient-banner/PatientBanner.test.tsx +5 -5
- package/src/form-entry-workflow/patient-banner/PatientBanner.tsx +14 -27
- package/src/form-entry-workflow/patient-banner/index.ts +1 -1
- package/src/form-entry-workflow/patient-banner/styles.scss +11 -12
- package/src/form-entry-workflow/patient-search-header/PatientSearchHeader.tsx +19 -28
- package/src/form-entry-workflow/patient-search-header/index.ts +1 -1
- package/src/form-entry-workflow/patient-search-header/styles.scss +13 -10
- package/src/form-entry-workflow/styles.scss +13 -14
- package/src/form-entry-workflow/workflow-review/WorkflowReview.tsx +13 -11
- package/src/form-entry-workflow/workflow-review/index.ts +1 -1
- package/src/form-entry-workflow/workflow-review/styles.scss +0 -4
- package/src/forms-app-menu-link.tsx +4 -6
- package/src/forms-page/FormsPage.tsx +24 -47
- package/src/forms-page/forms-table/FormsTable.tsx +33 -47
- package/src/forms-page/forms-table/index.ts +1 -1
- package/src/forms-page/forms-table/styles.scss +4 -5
- package/src/forms-page/index.ts +1 -1
- package/src/forms-page/styles.scss +3 -5
- package/src/group-form-entry-workflow/GroupFormEntryWorkflow.tsx +15 -402
- 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 +5 -5
- package/src/group-form-entry-workflow/group-display-header/GroupDisplayHeader.tsx +14 -30
- package/src/group-form-entry-workflow/group-display-header/index.ts +1 -1
- package/src/group-form-entry-workflow/group-display-header/styles.scss +20 -20
- package/src/group-form-entry-workflow/group-search/CompactGroupResults.tsx +24 -35
- package/src/group-form-entry-workflow/group-search/CompactGroupSearch.tsx +13 -15
- package/src/group-form-entry-workflow/group-search/GroupSearch.tsx +22 -38
- package/src/group-form-entry-workflow/group-search/compact-group-result.scss +16 -17
- package/src/group-form-entry-workflow/group-search/compact-group-search.scss +7 -8
- package/src/group-form-entry-workflow/group-search/group-search.scss +20 -23
- package/src/group-form-entry-workflow/group-search-header/GroupSearchHeader.tsx +41 -18
- package/src/group-form-entry-workflow/group-search-header/index.ts +1 -1
- package/src/group-form-entry-workflow/group-search-header/styles.scss +8 -8
- package/src/group-form-entry-workflow/index.ts +1 -1
- package/src/group-form-entry-workflow/styles.scss +15 -17
- package/src/hooks/index.ts +7 -6
- 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 +5 -5
- package/src/hooks/usePostEndpoint.ts +16 -10
- package/src/hooks/useSearchEndpoint.ts +23 -40
- package/src/hooks/useStartVisit.ts +82 -0
- package/src/index.ts +12 -76
- package/src/patient-card/PatientCard.tsx +8 -20
- package/src/patient-card/index.ts +1 -1
- package/src/patient-card/styles.scss +3 -4
- 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 +32 -11
- 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/dist/247.js +0 -1
- package/dist/255.js +0 -1
- package/dist/294.js +0 -2
- package/dist/32.js +0 -1
- package/dist/327.js +0 -1
- package/dist/403.js +0 -2
- package/dist/403.js.LICENSE.txt +0 -14
- package/dist/553.js +0 -2
- package/dist/553.js.LICENSE.txt +0 -14
- package/dist/569.js +0 -2
- package/dist/574.js +0 -1
- package/dist/595.js +0 -2
- package/dist/595.js.LICENSE.txt +0 -1
- package/dist/617.js +0 -1
- package/dist/68.js +0 -2
- package/dist/776.js +0 -1
- package/dist/804.js +0 -1
- package/dist/820.js +0 -1
- package/dist/906.js +0 -1
- package/dist/935.js +0 -2
- package/dist/openmrs-esm-fast-data-entry-app.old +0 -1
- package/src/declarations.d.tsx +0 -2
- /package/dist/{294.js.LICENSE.txt → 540.js.LICENSE.txt} +0 -0
- /package/dist/{935.js.LICENSE.txt → 961.js.LICENSE.txt} +0 -0
|
@@ -1,105 +1,93 @@
|
|
|
1
|
-
import React, { useCallback, useContext, useEffect, useState } from
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
FormLabel,
|
|
10
|
-
Loading,
|
|
11
|
-
} from "@carbon/react";
|
|
12
|
-
import { Add, TrashCan } from "@carbon/react/icons";
|
|
13
|
-
import { useTranslation } from "react-i18next";
|
|
14
|
-
import { ExtensionSlot, showToast } from "@openmrs/esm-framework";
|
|
15
|
-
import styles from "./styles.scss";
|
|
16
|
-
import GroupFormWorkflowContext from "../context/GroupFormWorkflowContext";
|
|
17
|
-
import { usePostCohort } from "../hooks";
|
|
1
|
+
import React, { useCallback, useContext, useEffect, useMemo, useState } from 'react';
|
|
2
|
+
import { ComposedModal, Button, ModalHeader, ModalFooter, ModalBody, TextInput, FormLabel } from '@carbon/react';
|
|
3
|
+
import { TrashCan } from '@carbon/react/icons';
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
import { ExtensionSlot, fetchCurrentPatient, showToast, useConfig, usePatient } from '@openmrs/esm-framework';
|
|
6
|
+
import styles from './styles.scss';
|
|
7
|
+
import GroupFormWorkflowContext from '../context/GroupFormWorkflowContext';
|
|
8
|
+
import { usePostCohort } from '../hooks';
|
|
18
9
|
|
|
19
10
|
const MemExtension = React.memo(ExtensionSlot);
|
|
20
11
|
|
|
21
12
|
const PatientRow = ({ patient, removePatient }) => {
|
|
22
13
|
const { t } = useTranslation();
|
|
14
|
+
const { patient: patientInfo, error, isLoading } = usePatient(patient?.uuid);
|
|
15
|
+
const onClickHandler = useCallback(() => removePatient(patient?.uuid), [patient, removePatient]);
|
|
16
|
+
|
|
17
|
+
const patientDisplay = useMemo(() => {
|
|
18
|
+
if (isLoading || error || !patientInfo) return '';
|
|
19
|
+
|
|
20
|
+
const { identifier, name } = patientInfo;
|
|
21
|
+
const displayIdentifier = identifier?.[0]?.value || '';
|
|
22
|
+
const givenNames = `${(name?.[0]?.given || []).join(' ')} ${name?.[0]?.family || ''}`;
|
|
23
|
+
|
|
24
|
+
return `${displayIdentifier ? `${displayIdentifier} -` : ''}${givenNames ? ` ${givenNames}` : ''}`.trim();
|
|
25
|
+
}, [isLoading, error, patientInfo]);
|
|
26
|
+
|
|
23
27
|
return (
|
|
24
|
-
<li
|
|
28
|
+
<li className={styles.patientRow}>
|
|
25
29
|
<span>
|
|
26
30
|
<Button
|
|
27
31
|
kind="tertiary"
|
|
28
32
|
size="sm"
|
|
29
33
|
hasIconOnly
|
|
30
|
-
onClick={
|
|
34
|
+
onClick={onClickHandler}
|
|
31
35
|
renderIcon={TrashCan}
|
|
32
36
|
tooltipAlignment="start"
|
|
33
37
|
tooltipPosition="top"
|
|
34
|
-
iconDescription={t(
|
|
38
|
+
iconDescription={t('remove', 'Remove')}
|
|
35
39
|
/>
|
|
36
40
|
</span>
|
|
37
|
-
<span className={styles.patientName}>{
|
|
41
|
+
<span className={styles.patientName}>{patientDisplay}</span>
|
|
38
42
|
</li>
|
|
39
43
|
);
|
|
40
44
|
};
|
|
41
45
|
|
|
42
46
|
const NewGroupForm = (props) => {
|
|
43
|
-
const {
|
|
44
|
-
name,
|
|
45
|
-
setName,
|
|
46
|
-
patientList,
|
|
47
|
-
updatePatientList,
|
|
48
|
-
errors,
|
|
49
|
-
validate,
|
|
50
|
-
removePatient,
|
|
51
|
-
} = props;
|
|
47
|
+
const { name, setName, patientList, updatePatientList, errors, validate, removePatient } = props;
|
|
52
48
|
const { t } = useTranslation();
|
|
53
49
|
|
|
54
50
|
return (
|
|
55
51
|
<div
|
|
56
52
|
style={{
|
|
57
|
-
display:
|
|
58
|
-
flexDirection:
|
|
59
|
-
rowGap:
|
|
53
|
+
display: 'flex',
|
|
54
|
+
flexDirection: 'column',
|
|
55
|
+
rowGap: '1rem',
|
|
60
56
|
}}
|
|
61
57
|
>
|
|
62
58
|
<TextInput
|
|
63
|
-
labelText={t(
|
|
59
|
+
labelText={t('newGroupName', 'New Group Name')}
|
|
64
60
|
value={name}
|
|
65
61
|
onChange={(e) => setName(e.target.value)}
|
|
66
|
-
onBlur={() => validate(
|
|
62
|
+
onBlur={() => validate('name')}
|
|
67
63
|
/>
|
|
68
64
|
{errors?.name && (
|
|
69
65
|
<p className={styles.formError}>
|
|
70
|
-
{errors.name ===
|
|
71
|
-
? t("groupNameError", "Please enter a group name.")
|
|
72
|
-
: errors.name}
|
|
66
|
+
{errors.name === 'required' ? t('groupNameError', 'Please enter a group name.') : errors.name}
|
|
73
67
|
</p>
|
|
74
68
|
)}
|
|
75
69
|
<FormLabel>
|
|
76
|
-
{patientList.length} {t(
|
|
70
|
+
{patientList.length} {t('patientsInGroup', 'Patients in group')}
|
|
77
71
|
</FormLabel>
|
|
78
72
|
{errors?.patientList && (
|
|
79
|
-
<p className={styles.formError}>
|
|
80
|
-
{t("noPatientError", "Please enter at least one patient.")}
|
|
81
|
-
</p>
|
|
73
|
+
<p className={styles.formError}>{t('noPatientError', 'Please enter at least one patient.')}</p>
|
|
82
74
|
)}
|
|
83
75
|
{!errors?.patientList && (
|
|
84
76
|
<ul className={styles.patientList}>
|
|
85
77
|
{patientList?.map((patient, index) => (
|
|
86
|
-
<PatientRow
|
|
87
|
-
patient={patient}
|
|
88
|
-
removePatient={removePatient}
|
|
89
|
-
key={index}
|
|
90
|
-
/>
|
|
78
|
+
<PatientRow patient={patient} removePatient={removePatient} key={patient.uuid} />
|
|
91
79
|
))}
|
|
92
80
|
</ul>
|
|
93
81
|
)}
|
|
94
82
|
|
|
95
|
-
<FormLabel>Search for patients to add to group</FormLabel>
|
|
83
|
+
<FormLabel>{t('searchForPatientsToAddToGroup', 'Search for patients to add to group')}</FormLabel>
|
|
96
84
|
<div className={styles.searchBar}>
|
|
97
85
|
<MemExtension
|
|
98
86
|
extensionSlotName="patient-search-bar-slot"
|
|
99
87
|
state={{
|
|
100
88
|
selectPatientAction: updatePatientList,
|
|
101
89
|
buttonProps: {
|
|
102
|
-
kind:
|
|
90
|
+
kind: 'secondary',
|
|
103
91
|
},
|
|
104
92
|
}}
|
|
105
93
|
/>
|
|
@@ -108,45 +96,47 @@ const NewGroupForm = (props) => {
|
|
|
108
96
|
);
|
|
109
97
|
};
|
|
110
98
|
|
|
111
|
-
const AddGroupModal = (
|
|
99
|
+
const AddGroupModal = ({
|
|
100
|
+
patients = undefined,
|
|
101
|
+
isCreate = undefined,
|
|
102
|
+
groupName = '',
|
|
103
|
+
cohortUuid = undefined,
|
|
104
|
+
isOpen,
|
|
105
|
+
onPostCancel,
|
|
106
|
+
onPostSubmit,
|
|
107
|
+
}) => {
|
|
112
108
|
const { setGroup } = useContext(GroupFormWorkflowContext);
|
|
113
109
|
const { t } = useTranslation();
|
|
114
|
-
const [open, setOpen] = useState(false);
|
|
115
110
|
const [errors, setErrors] = useState({});
|
|
116
|
-
const [name, setName] = useState(
|
|
117
|
-
const [patientList, setPatientList] = useState([]);
|
|
118
|
-
const { post, result,
|
|
119
|
-
|
|
120
|
-
const handleCancel = () => {
|
|
121
|
-
setOpen(false);
|
|
122
|
-
};
|
|
111
|
+
const [name, setName] = useState(groupName);
|
|
112
|
+
const [patientList, setPatientList] = useState(patients || []);
|
|
113
|
+
const { post, result, error } = usePostCohort();
|
|
114
|
+
const config = useConfig();
|
|
123
115
|
|
|
124
116
|
const removePatient = useCallback(
|
|
125
117
|
(patientUuid: string) =>
|
|
126
|
-
setPatientList((patientList) =>
|
|
127
|
-
|
|
128
|
-
),
|
|
129
|
-
[setPatientList]
|
|
118
|
+
setPatientList((patientList) => patientList.filter((patient) => patient.uuid !== patientUuid)),
|
|
119
|
+
[setPatientList],
|
|
130
120
|
);
|
|
131
121
|
|
|
132
122
|
const validate = useCallback(
|
|
133
123
|
(field?: string | undefined) => {
|
|
134
124
|
let valid = true;
|
|
135
125
|
if (field) {
|
|
136
|
-
valid = field ===
|
|
126
|
+
valid = field === 'name' ? !!name : !!patientList.length;
|
|
137
127
|
setErrors((errors) => ({
|
|
138
128
|
...errors,
|
|
139
|
-
[field]: valid ? null :
|
|
129
|
+
[field]: valid ? null : 'required',
|
|
140
130
|
}));
|
|
141
131
|
} else {
|
|
142
132
|
if (!name) {
|
|
143
|
-
setErrors((errors) => ({ ...errors, name:
|
|
133
|
+
setErrors((errors) => ({ ...errors, name: 'required' }));
|
|
144
134
|
valid = false;
|
|
145
135
|
} else {
|
|
146
136
|
setErrors((errors) => ({ ...errors, name: null }));
|
|
147
137
|
}
|
|
148
138
|
if (!patientList.length) {
|
|
149
|
-
setErrors((errors) => ({ ...errors, patientList:
|
|
139
|
+
setErrors((errors) => ({ ...errors, patientList: 'required' }));
|
|
150
140
|
valid = false;
|
|
151
141
|
} else {
|
|
152
142
|
setErrors((errors) => ({ ...errors, patientList: null }));
|
|
@@ -154,29 +144,49 @@ const AddGroupModal = () => {
|
|
|
154
144
|
}
|
|
155
145
|
return valid;
|
|
156
146
|
},
|
|
157
|
-
[name, patientList.length]
|
|
147
|
+
[name, patientList.length],
|
|
158
148
|
);
|
|
159
149
|
|
|
160
150
|
const updatePatientList = useCallback(
|
|
161
|
-
(
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
151
|
+
(patientUuid) => {
|
|
152
|
+
function getPatientName(patient) {
|
|
153
|
+
return [patient?.name?.[0]?.given, patient?.name?.[0]?.family].join(' ');
|
|
154
|
+
}
|
|
155
|
+
if (!patientList.find((p) => p.uuid === patientUuid)) {
|
|
156
|
+
fetchCurrentPatient(patientUuid).then((result) => {
|
|
157
|
+
const newPatient = { uuid: patientUuid, ...result };
|
|
158
|
+
setPatientList(
|
|
159
|
+
[...patientList, newPatient].sort((a, b) =>
|
|
160
|
+
getPatientName(a).localeCompare(getPatientName(b), undefined, {
|
|
161
|
+
sensitivity: 'base',
|
|
162
|
+
}),
|
|
163
|
+
),
|
|
164
|
+
);
|
|
165
|
+
});
|
|
166
|
+
}
|
|
169
167
|
setErrors((errors) => ({ ...errors, patientList: null }));
|
|
170
168
|
},
|
|
171
|
-
[setPatientList]
|
|
169
|
+
[patientList, setPatientList],
|
|
172
170
|
);
|
|
173
171
|
|
|
174
172
|
const handleSubmit = () => {
|
|
175
173
|
if (validate()) {
|
|
176
174
|
post({
|
|
175
|
+
uuid: cohortUuid,
|
|
177
176
|
name: name,
|
|
177
|
+
cohortType: config?.groupSessionConcepts?.cohortTypeId,
|
|
178
178
|
cohortMembers: patientList.map((p) => ({ patient: p.uuid })),
|
|
179
179
|
});
|
|
180
|
+
if (onPostSubmit) {
|
|
181
|
+
onPostSubmit();
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
|
|
186
|
+
const handleCancel = () => {
|
|
187
|
+
setPatientList(patients || []);
|
|
188
|
+
if (onPostCancel) {
|
|
189
|
+
onPostCancel();
|
|
180
190
|
}
|
|
181
191
|
};
|
|
182
192
|
|
|
@@ -194,20 +204,13 @@ const AddGroupModal = () => {
|
|
|
194
204
|
useEffect(() => {
|
|
195
205
|
if (error) {
|
|
196
206
|
showToast({
|
|
197
|
-
kind:
|
|
198
|
-
title: t(
|
|
199
|
-
description:
|
|
200
|
-
error.message ??
|
|
201
|
-
t("unknownPostError", "An unknown error occured while saving data"),
|
|
207
|
+
kind: 'error',
|
|
208
|
+
title: t('postError', 'POST Error'),
|
|
209
|
+
description: error.message ?? t('unknownPostError', 'An unknown error occurred while saving data'),
|
|
202
210
|
});
|
|
203
211
|
if (error.fieldErrors) {
|
|
204
212
|
setErrors(
|
|
205
|
-
Object.fromEntries(
|
|
206
|
-
Object.entries(error.fieldErrors).map(([key, value]) => [
|
|
207
|
-
key,
|
|
208
|
-
value?.[0]?.message,
|
|
209
|
-
])
|
|
210
|
-
)
|
|
213
|
+
Object.fromEntries(Object.entries(error.fieldErrors).map(([key, value]) => [key, value?.[0]?.message])),
|
|
211
214
|
);
|
|
212
215
|
}
|
|
213
216
|
}
|
|
@@ -215,43 +218,27 @@ const AddGroupModal = () => {
|
|
|
215
218
|
|
|
216
219
|
return (
|
|
217
220
|
<div className={styles.modal}>
|
|
218
|
-
<
|
|
219
|
-
|
|
220
|
-
renderIcon={Add}
|
|
221
|
-
iconDescription="Add"
|
|
222
|
-
>
|
|
223
|
-
{t("createNewGroup", "Create New Group")}
|
|
224
|
-
</Button>
|
|
225
|
-
<ComposedModal open={open} onClose={() => setOpen(false)}>
|
|
226
|
-
<ModalHeader>{t("createNewGroup", "Create New Group")}</ModalHeader>
|
|
221
|
+
<ComposedModal open={isOpen} onClose={handleCancel}>
|
|
222
|
+
<ModalHeader>{isCreate ? t('createNewGroup', 'Create New Group') : t('editGroup', 'Edit Group')}</ModalHeader>
|
|
227
223
|
<ModalBody>
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
setName,
|
|
240
|
-
patientList,
|
|
241
|
-
updatePatientList,
|
|
242
|
-
errors,
|
|
243
|
-
validate,
|
|
244
|
-
removePatient,
|
|
245
|
-
}}
|
|
246
|
-
/>
|
|
247
|
-
)}
|
|
224
|
+
<NewGroupForm
|
|
225
|
+
{...{
|
|
226
|
+
name,
|
|
227
|
+
setName,
|
|
228
|
+
patientList,
|
|
229
|
+
updatePatientList,
|
|
230
|
+
errors,
|
|
231
|
+
validate,
|
|
232
|
+
removePatient,
|
|
233
|
+
}}
|
|
234
|
+
/>
|
|
248
235
|
</ModalBody>
|
|
249
236
|
<ModalFooter>
|
|
250
|
-
<Button kind="secondary" onClick={handleCancel}
|
|
251
|
-
{t(
|
|
237
|
+
<Button kind="secondary" onClick={handleCancel}>
|
|
238
|
+
{t('cancel', 'Cancel')}
|
|
252
239
|
</Button>
|
|
253
|
-
<Button kind="primary" onClick={handleSubmit}
|
|
254
|
-
{t(
|
|
240
|
+
<Button kind="primary" onClick={handleSubmit}>
|
|
241
|
+
{isCreate ? t('createGroup', 'Create Group') : t('save', 'Save')}
|
|
255
242
|
</Button>
|
|
256
243
|
</ModalFooter>
|
|
257
244
|
</ComposedModal>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
@use '@carbon/styles/scss/spacing';
|
|
2
1
|
@use '@carbon/colors';
|
|
2
|
+
@use '@carbon/layout';
|
|
3
3
|
|
|
4
4
|
.modal {
|
|
5
5
|
:global(.cds--modal) {
|
|
@@ -17,6 +17,10 @@
|
|
|
17
17
|
width: 100%
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
+
.searchBar > div button {
|
|
21
|
+
height: auto;
|
|
22
|
+
}
|
|
23
|
+
|
|
20
24
|
.formError {
|
|
21
25
|
color: red;
|
|
22
26
|
}
|
|
@@ -32,7 +36,7 @@
|
|
|
32
36
|
|
|
33
37
|
.patientName {
|
|
34
38
|
flex-grow: 1;
|
|
35
|
-
padding-left:
|
|
39
|
+
padding-left: layout.$spacing-05;
|
|
36
40
|
}
|
|
37
41
|
|
|
38
42
|
.loading {
|
|
@@ -41,5 +45,5 @@
|
|
|
41
45
|
flex-direction: column;
|
|
42
46
|
justify-content: center;
|
|
43
47
|
align-items: center;
|
|
44
|
-
row-gap:
|
|
48
|
+
row-gap: layout.$spacing-05;
|
|
45
49
|
}
|
package/src/config-schema.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Type } from
|
|
1
|
+
import { Type } from '@openmrs/esm-framework';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* This is the config schema.
|
|
@@ -8,44 +8,43 @@ import { Type } from "@openmrs/esm-framework";
|
|
|
8
8
|
export const configSchema = {
|
|
9
9
|
formCategories: {
|
|
10
10
|
_type: Type.Array,
|
|
11
|
-
_description:
|
|
12
|
-
"Organize forms into categories. A form can belong to multiple categories.",
|
|
11
|
+
_description: 'Organize forms into categories. A form can belong to multiple categories.',
|
|
13
12
|
_elements: {
|
|
14
13
|
name: {
|
|
15
14
|
_type: Type.String,
|
|
16
|
-
_description:
|
|
15
|
+
_description: 'Category name',
|
|
17
16
|
},
|
|
18
17
|
forms: {
|
|
19
18
|
_type: Type.Array,
|
|
20
|
-
_description:
|
|
19
|
+
_description: 'List of forms for this category.',
|
|
21
20
|
_elements: {
|
|
22
21
|
formUUID: {
|
|
23
22
|
_type: Type.UUID,
|
|
24
|
-
_description:
|
|
23
|
+
_description: 'UUID of form',
|
|
25
24
|
},
|
|
26
25
|
name: {
|
|
27
26
|
_type: Type.String,
|
|
28
|
-
_description:
|
|
27
|
+
_description: 'Name of form',
|
|
29
28
|
},
|
|
30
29
|
},
|
|
31
30
|
},
|
|
32
31
|
},
|
|
33
32
|
_default: [
|
|
34
33
|
{
|
|
35
|
-
name:
|
|
34
|
+
name: 'ICRC Forms',
|
|
36
35
|
forms: [
|
|
37
36
|
{
|
|
38
|
-
formUUID:
|
|
39
|
-
name:
|
|
37
|
+
formUUID: '0cefb866-110c-4f16-af58-560932a1db1f',
|
|
38
|
+
name: 'Adult Triage',
|
|
40
39
|
},
|
|
41
40
|
],
|
|
42
41
|
},
|
|
43
42
|
{
|
|
44
|
-
name:
|
|
43
|
+
name: 'Distress Scales',
|
|
45
44
|
forms: [
|
|
46
45
|
{
|
|
47
|
-
formUUID:
|
|
48
|
-
name:
|
|
46
|
+
formUUID: '9f26aad4-244a-46ca-be49-1196df1a8c9a',
|
|
47
|
+
name: 'POC Sample Form 1',
|
|
49
48
|
},
|
|
50
49
|
],
|
|
51
50
|
},
|
|
@@ -53,12 +52,74 @@ export const configSchema = {
|
|
|
53
52
|
},
|
|
54
53
|
formCategoriesToShow: {
|
|
55
54
|
_type: Type.Array,
|
|
56
|
-
_description:
|
|
55
|
+
_description: 'Forms to show by default on the forms app home page.',
|
|
57
56
|
_elements: {
|
|
58
57
|
_type: Type.String,
|
|
59
|
-
_description:
|
|
58
|
+
_description: 'Name of category',
|
|
60
59
|
},
|
|
61
|
-
_default: [
|
|
60
|
+
_default: ['ICRC Forms', 'Distress Scales'],
|
|
61
|
+
},
|
|
62
|
+
groupSessionConcepts: {
|
|
63
|
+
sessionName: {
|
|
64
|
+
_type: Type.UUID,
|
|
65
|
+
_description: 'UUID of concept for Session Name',
|
|
66
|
+
_default: 'e2559620-900b-4f66-ae41-0b9c4adfb654',
|
|
67
|
+
},
|
|
68
|
+
sessionDate: {
|
|
69
|
+
_type: Type.UUID,
|
|
70
|
+
_description: 'UUID of concept for Session Date',
|
|
71
|
+
_default: 'ceaca505-6dff-4940-8a43-8c060a0924d7',
|
|
72
|
+
},
|
|
73
|
+
practitionerName: {
|
|
74
|
+
_type: Type.UUID,
|
|
75
|
+
_description: 'UUID of concept for Practitioner Name',
|
|
76
|
+
_default: 'f1a2d58c-1a0e-4148-931a-aac224649fdc',
|
|
77
|
+
},
|
|
78
|
+
sessionNotes: {
|
|
79
|
+
_type: Type.UUID,
|
|
80
|
+
_description: 'UUID of concept for Session Notes',
|
|
81
|
+
_default: 'fa8fedc0-c066-4da3-8dc1-2ad8621fc480',
|
|
82
|
+
},
|
|
83
|
+
cohortTypeId: {
|
|
84
|
+
_type: Type.UUID,
|
|
85
|
+
_description: 'UUID of cohort type',
|
|
86
|
+
_default: 'eee9970e-7ca0-4e8c-a280-c33e9d5f6a04',
|
|
87
|
+
},
|
|
88
|
+
cohortId: {
|
|
89
|
+
_type: Type.UUID,
|
|
90
|
+
_description: 'UUID of concept for cohort identifier',
|
|
91
|
+
_default: '5461f231-7e59-4be8-93a4-6d49fd13c00a',
|
|
92
|
+
},
|
|
93
|
+
cohortName: {
|
|
94
|
+
_type: Type.UUID,
|
|
95
|
+
_description: 'UUID of concept for cohort name',
|
|
96
|
+
_default: '6029f289-92a6-4a68-80f1-3078d0152449',
|
|
97
|
+
},
|
|
98
|
+
sessionUuid: {
|
|
99
|
+
_type: Type.UUID,
|
|
100
|
+
_description: 'UUID of concept for session identifier',
|
|
101
|
+
_default: '6a803908-8a5b-4598-adea-19358c83529a',
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
specificQuestions: {
|
|
105
|
+
_type: Type.Array,
|
|
106
|
+
_description: 'List of specific questions to populate forms.',
|
|
107
|
+
_elements: {
|
|
108
|
+
forms: {
|
|
109
|
+
_type: Type.Array,
|
|
110
|
+
_description: 'List of form UUIDs for which the question applies.',
|
|
111
|
+
_elements: {
|
|
112
|
+
_type: Type.UUID,
|
|
113
|
+
},
|
|
114
|
+
_default: [],
|
|
115
|
+
},
|
|
116
|
+
questionId: {
|
|
117
|
+
_type: Type.String,
|
|
118
|
+
_description: 'ID of the question.',
|
|
119
|
+
_default: '',
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
_default: [],
|
|
62
123
|
},
|
|
63
124
|
};
|
|
64
125
|
|
package/src/constant.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import React, { useEffect, useMemo, useReducer } from
|
|
2
|
-
import reducer from
|
|
3
|
-
import { useParams, useLocation } from
|
|
1
|
+
import React, { useEffect, useMemo, useReducer } from 'react';
|
|
2
|
+
import reducer from './FormWorkflowReducer';
|
|
3
|
+
import { useParams, useLocation } from 'react-router-dom';
|
|
4
|
+
import useGetSystemSetting from '../hooks/useGetSystemSetting';
|
|
5
|
+
import { useSession } from '@openmrs/esm-framework';
|
|
4
6
|
interface ParamTypes {
|
|
5
7
|
formUuid?: string;
|
|
6
8
|
}
|
|
@@ -30,6 +32,8 @@ export const initialWorkflowState = {
|
|
|
30
32
|
activeEncounterUuid: null, // pseudo field from state[activeFormUuid].encounterUuid
|
|
31
33
|
patientUuids: [], // pseudo field from state[activeFormUuid].patientUuids
|
|
32
34
|
encounters: {}, // pseudo field from state[activeFormUuid].encounters
|
|
35
|
+
singleSessionVisitTypeUuid: null,
|
|
36
|
+
userUuid: null, // UUID of the user to which this workflow state belongs to
|
|
33
37
|
};
|
|
34
38
|
|
|
35
39
|
const FormWorkflowContext = React.createContext({
|
|
@@ -38,41 +42,43 @@ const FormWorkflowContext = React.createContext({
|
|
|
38
42
|
});
|
|
39
43
|
|
|
40
44
|
const FormWorkflowProvider = ({ children }) => {
|
|
45
|
+
const { user } = useSession();
|
|
41
46
|
const { formUuid } = useParams() as ParamTypes;
|
|
42
|
-
const activeFormUuid = formUuid.split(
|
|
47
|
+
const activeFormUuid = formUuid.split('&')[0];
|
|
43
48
|
const { search } = useLocation();
|
|
44
|
-
const newPatientUuid = new URLSearchParams(search).get(
|
|
49
|
+
const newPatientUuid = new URLSearchParams(search).get('patientUuid');
|
|
45
50
|
const [state, dispatch] = useReducer(reducer, {
|
|
46
51
|
...initialWorkflowState,
|
|
47
52
|
...initialActions,
|
|
48
53
|
});
|
|
54
|
+
const systemSetting = useGetSystemSetting('@openmrs/esm-fast-data-entry-app.groupSessionVisitTypeUuid');
|
|
55
|
+
const singleSessionVisitTypeUuid = systemSetting?.result?.data?.results?.[0]?.value;
|
|
49
56
|
|
|
50
57
|
const actions = useMemo(
|
|
51
58
|
() => ({
|
|
52
59
|
initializeWorkflowState: ({ activeFormUuid, newPatientUuid }) =>
|
|
53
60
|
dispatch({
|
|
54
|
-
type:
|
|
61
|
+
type: 'INITIALIZE_WORKFLOW_STATE',
|
|
55
62
|
activeFormUuid,
|
|
56
63
|
newPatientUuid,
|
|
64
|
+
userUuid: user.uuid,
|
|
57
65
|
}),
|
|
58
|
-
addPatient: (patientUuid) =>
|
|
59
|
-
|
|
60
|
-
openPatientSearch: () => dispatch({ type: "OPEN_PATIENT_SEARCH" }),
|
|
66
|
+
addPatient: (patientUuid) => dispatch({ type: 'ADD_PATIENT', patientUuid }),
|
|
67
|
+
openPatientSearch: () => dispatch({ type: 'OPEN_PATIENT_SEARCH' }),
|
|
61
68
|
saveEncounter: (encounterUuid) =>
|
|
62
69
|
dispatch({
|
|
63
|
-
type:
|
|
70
|
+
type: 'SAVE_ENCOUNTER',
|
|
64
71
|
encounterUuid,
|
|
65
72
|
}),
|
|
66
|
-
submitForNext: () => dispatch({ type:
|
|
67
|
-
submitForReview: () => dispatch({ type:
|
|
68
|
-
submitForComplete: () => dispatch({ type:
|
|
69
|
-
editEncounter: (patientUuid) =>
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
closeSession: () => dispatch({ type: "CLOSE_SESSION" }),
|
|
73
|
+
submitForNext: () => dispatch({ type: 'SUBMIT_FOR_NEXT' }),
|
|
74
|
+
submitForReview: () => dispatch({ type: 'SUBMIT_FOR_REVIEW' }),
|
|
75
|
+
submitForComplete: () => dispatch({ type: 'SUBMIT_FOR_COMPLETE' }),
|
|
76
|
+
editEncounter: (patientUuid) => dispatch({ type: 'EDIT_ENCOUNTER', patientUuid }),
|
|
77
|
+
goToReview: () => dispatch({ type: 'GO_TO_REVIEW' }),
|
|
78
|
+
destroySession: () => dispatch({ type: 'DESTROY_SESSION' }),
|
|
79
|
+
closeSession: () => dispatch({ type: 'CLOSE_SESSION' }),
|
|
74
80
|
}),
|
|
75
|
-
[]
|
|
81
|
+
[user],
|
|
76
82
|
);
|
|
77
83
|
|
|
78
84
|
// if formUuid isn't a part of state yet, grab it from the url params
|
|
@@ -88,21 +94,14 @@ const FormWorkflowProvider = ({ children }) => {
|
|
|
88
94
|
value={{
|
|
89
95
|
...state,
|
|
90
96
|
...actions,
|
|
91
|
-
workflowState:
|
|
92
|
-
state.forms?.[state.activeFormUuid]?.workflowState ??
|
|
93
|
-
initialWorkflowState.workflowState,
|
|
97
|
+
workflowState: state.forms?.[state.activeFormUuid]?.workflowState ?? initialWorkflowState.workflowState,
|
|
94
98
|
activePatientUuid:
|
|
95
|
-
state.forms?.[state.activeFormUuid]?.activePatientUuid ??
|
|
96
|
-
initialWorkflowState.activePatientUuid,
|
|
99
|
+
state.forms?.[state.activeFormUuid]?.activePatientUuid ?? initialWorkflowState.activePatientUuid,
|
|
97
100
|
activeEncounterUuid:
|
|
98
|
-
state.forms?.[state.activeFormUuid]?.activeEncounterUuid ??
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
initialWorkflowState.patientUuids,
|
|
103
|
-
encounters:
|
|
104
|
-
state.forms?.[state.activeFormUuid]?.encounters ??
|
|
105
|
-
initialWorkflowState.encounters,
|
|
101
|
+
state.forms?.[state.activeFormUuid]?.activeEncounterUuid ?? initialWorkflowState.activeEncounterUuid,
|
|
102
|
+
patientUuids: state.forms?.[state.activeFormUuid]?.patientUuids ?? initialWorkflowState.patientUuids,
|
|
103
|
+
encounters: state.forms?.[state.activeFormUuid]?.encounters ?? initialWorkflowState.encounters,
|
|
104
|
+
singleSessionVisitTypeUuid,
|
|
106
105
|
}}
|
|
107
106
|
>
|
|
108
107
|
{children}
|