@openmrs/esm-fast-data-entry-app 1.0.1-pre.17 → 1.0.1-pre.171
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/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 +42 -37
- package/src/CancelModal.tsx +48 -0
- package/src/CompleteModal.tsx +46 -0
- package/src/FormBootstrap.tsx +32 -4
- package/src/add-group-modal/AddGroupModal.tsx +110 -60
- package/src/add-group-modal/styles.scss +7 -3
- package/src/config-schema.ts +62 -0
- package/src/context/FormWorkflowContext.tsx +13 -1
- package/src/context/FormWorkflowReducer.ts +13 -3
- package/src/context/GroupFormWorkflowContext.tsx +43 -6
- package/src/context/GroupFormWorkflowReducer.ts +160 -15
- package/src/declarations.d.ts +4 -0
- package/src/empty-state/styles.scss +14 -14
- package/src/form-entry-workflow/FormEntryWorkflow.tsx +74 -102
- package/src/form-entry-workflow/form-review-card/styles.scss +9 -11
- package/src/form-entry-workflow/patient-banner/styles.scss +11 -12
- package/src/form-entry-workflow/patient-search-header/PatientSearchHeader.tsx +2 -2
- 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 +5 -3
- package/src/form-entry-workflow/workflow-review/styles.scss +0 -4
- package/src/forms-page/FormsPage.tsx +10 -5
- package/src/forms-page/forms-table/FormsTable.tsx +11 -5
- package/src/forms-page/forms-table/styles.scss +4 -5
- package/src/forms-page/styles.scss +3 -5
- package/src/group-form-entry-workflow/GroupFormEntryWorkflow.tsx +12 -399
- package/src/group-form-entry-workflow/GroupSessionWorkspace.tsx +238 -0
- package/src/group-form-entry-workflow/SessionDetailsForm.tsx +177 -0
- package/src/group-form-entry-workflow/SessionMetaWorkspace.tsx +107 -0
- package/src/group-form-entry-workflow/attendance-table/AttendanceTable.tsx +144 -0
- package/src/group-form-entry-workflow/attendance-table/index.ts +1 -0
- package/src/group-form-entry-workflow/configurable-questions/ConfigurableQuestionsSection.tsx +47 -0
- package/src/group-form-entry-workflow/group-display-header/GroupDisplayHeader.tsx +1 -9
- package/src/group-form-entry-workflow/group-display-header/styles.scss +20 -20
- package/src/group-form-entry-workflow/group-search/CompactGroupSearch.tsx +1 -1
- package/src/group-form-entry-workflow/group-search/GroupSearch.tsx +1 -1
- 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 +36 -6
- package/src/group-form-entry-workflow/group-search-header/styles.scss +8 -8
- package/src/group-form-entry-workflow/styles.scss +15 -17
- package/src/hooks/index.ts +8 -1
- package/src/hooks/useForm.ts +73 -0
- package/src/hooks/useGetAllForms.ts +3 -2
- package/src/hooks/useGetEncounter.ts +2 -2
- package/src/hooks/useGetPatient.ts +1 -1
- package/src/hooks/useGetPatients.ts +34 -0
- package/src/hooks/useGetSystemSetting.ts +38 -0
- package/src/hooks/usePostEndpoint.ts +10 -4
- package/src/hooks/useSearchEndpoint.ts +14 -8
- package/src/hooks/useStartVisit.ts +93 -0
- package/src/index.ts +13 -65
- package/src/patient-card/styles.scss +3 -4
- package/src/routes.json +24 -0
- 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/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
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import {
|
|
2
|
+
Layer,
|
|
3
|
+
Tile,
|
|
4
|
+
TextInput,
|
|
5
|
+
TextArea,
|
|
6
|
+
DatePicker,
|
|
7
|
+
DatePickerInput,
|
|
8
|
+
} from "@carbon/react";
|
|
9
|
+
import React, { useContext } from "react";
|
|
10
|
+
import { useConfig } from "@openmrs/esm-framework";
|
|
11
|
+
import { useParams } from "react-router-dom";
|
|
12
|
+
import styles from "./styles.scss";
|
|
13
|
+
import { useTranslation } from "react-i18next";
|
|
14
|
+
import { Controller, useFormContext } from "react-hook-form";
|
|
15
|
+
import { AttendanceTable } from "./attendance-table";
|
|
16
|
+
import GroupFormWorkflowContext from "../context/GroupFormWorkflowContext";
|
|
17
|
+
import useGetPatients from "../hooks/useGetPatients";
|
|
18
|
+
import ConfigurableQuestionsSection from "./configurable-questions/ConfigurableQuestionsSection";
|
|
19
|
+
import useSpecificQuestions from "../hooks/useForm";
|
|
20
|
+
|
|
21
|
+
interface ParamTypes {
|
|
22
|
+
formUuid?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const SessionDetailsForm = () => {
|
|
26
|
+
const { specificQuestions } = useConfig();
|
|
27
|
+
const { formUuid } = useParams() as ParamTypes;
|
|
28
|
+
const { questions } = useSpecificQuestions(formUuid, specificQuestions);
|
|
29
|
+
|
|
30
|
+
const { t } = useTranslation();
|
|
31
|
+
const {
|
|
32
|
+
register,
|
|
33
|
+
formState: { errors },
|
|
34
|
+
control,
|
|
35
|
+
} = useFormContext();
|
|
36
|
+
|
|
37
|
+
const { activeGroupMembers} = useContext(GroupFormWorkflowContext);
|
|
38
|
+
const { patients, isLoading } = useGetPatients(activeGroupMembers);
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
<div>
|
|
42
|
+
{!isLoading && (
|
|
43
|
+
<div className={styles.formSection}>
|
|
44
|
+
<h4>{t("sessionDetails", "1. Session details")}</h4>
|
|
45
|
+
<div>
|
|
46
|
+
<p>
|
|
47
|
+
{t(
|
|
48
|
+
"allFieldsRequired",
|
|
49
|
+
"All fields are required unless marked optional"
|
|
50
|
+
)}
|
|
51
|
+
</p>
|
|
52
|
+
</div>
|
|
53
|
+
<Layer>
|
|
54
|
+
<Tile className={styles.formSectionTile}>
|
|
55
|
+
<Layer>
|
|
56
|
+
<div
|
|
57
|
+
style={{
|
|
58
|
+
display: "flex",
|
|
59
|
+
flexDirection: "column",
|
|
60
|
+
rowGap: "1.5rem",
|
|
61
|
+
}}
|
|
62
|
+
>
|
|
63
|
+
<TextInput
|
|
64
|
+
id="text"
|
|
65
|
+
type="text"
|
|
66
|
+
labelText={t("sessionName", "Session Name")}
|
|
67
|
+
{...register("sessionName", { required: true })}
|
|
68
|
+
invalid={errors.sessionName}
|
|
69
|
+
invalidText={t("requiredField", "This field is required")}
|
|
70
|
+
/>
|
|
71
|
+
<TextInput
|
|
72
|
+
id="text"
|
|
73
|
+
type="text"
|
|
74
|
+
labelText={t("practitionerName", "Practitioner Name")}
|
|
75
|
+
{...register("practitionerName", { required: true })}
|
|
76
|
+
invalid={errors.practitionerName}
|
|
77
|
+
invalidText={t("requiredField", "This field is required")}
|
|
78
|
+
/>
|
|
79
|
+
<Controller
|
|
80
|
+
name="sessionDate"
|
|
81
|
+
control={control}
|
|
82
|
+
rules={{ required: true }}
|
|
83
|
+
render={({ field }) => (
|
|
84
|
+
<DatePicker
|
|
85
|
+
datePickerType="single"
|
|
86
|
+
size="md"
|
|
87
|
+
maxDate={new Date()}
|
|
88
|
+
{...field}
|
|
89
|
+
>
|
|
90
|
+
<DatePickerInput
|
|
91
|
+
id="session-date"
|
|
92
|
+
labelText={t("sessionDate", "Session Date")}
|
|
93
|
+
placeholder="mm/dd/yyyy"
|
|
94
|
+
size="md"
|
|
95
|
+
invalid={errors.sessionDate}
|
|
96
|
+
invalidText={t(
|
|
97
|
+
"requiredField",
|
|
98
|
+
"This field is required"
|
|
99
|
+
)}
|
|
100
|
+
/>
|
|
101
|
+
</DatePicker>
|
|
102
|
+
)}
|
|
103
|
+
/>
|
|
104
|
+
<TextArea
|
|
105
|
+
id="text"
|
|
106
|
+
type="text"
|
|
107
|
+
labelText={t("sessionNotes", "Session Notes")}
|
|
108
|
+
{...register("sessionNotes", { required: true })}
|
|
109
|
+
invalid={errors.sessionNotes}
|
|
110
|
+
invalidText={t("requiredField", "This field is required")}
|
|
111
|
+
/>
|
|
112
|
+
</div>
|
|
113
|
+
</Layer>
|
|
114
|
+
</Tile>
|
|
115
|
+
</Layer>
|
|
116
|
+
<h4>{t("sessionParticipants", "2. Session participants")}</h4>
|
|
117
|
+
<div>
|
|
118
|
+
<p>
|
|
119
|
+
{t(
|
|
120
|
+
"markAbsentPatients",
|
|
121
|
+
"The patients in this group. Patients that are not present in the session should be marked as absent."
|
|
122
|
+
)}
|
|
123
|
+
</p>
|
|
124
|
+
</div>
|
|
125
|
+
<Layer>
|
|
126
|
+
<Tile className={styles.formSectionTile}>
|
|
127
|
+
<Layer>
|
|
128
|
+
<div
|
|
129
|
+
style={{
|
|
130
|
+
display: "flex",
|
|
131
|
+
flexDirection: "column",
|
|
132
|
+
rowGap: "1.5rem",
|
|
133
|
+
}}
|
|
134
|
+
>
|
|
135
|
+
<AttendanceTable patients={patients} />
|
|
136
|
+
</div>
|
|
137
|
+
</Layer>
|
|
138
|
+
</Tile>
|
|
139
|
+
</Layer>
|
|
140
|
+
{questions?.length > 0 ? (
|
|
141
|
+
<>
|
|
142
|
+
<h4>{t("sessionSpecificDetails", "3. Specific details")}</h4>
|
|
143
|
+
<div>
|
|
144
|
+
<p>
|
|
145
|
+
{t(
|
|
146
|
+
"sessionSpecificDetailsDescription",
|
|
147
|
+
"They will be mapped to form responses for all patients as pre-filled data."
|
|
148
|
+
)}
|
|
149
|
+
</p>
|
|
150
|
+
</div>
|
|
151
|
+
<Layer>
|
|
152
|
+
<Tile className={styles.formSectionTile}>
|
|
153
|
+
<Layer>
|
|
154
|
+
<div
|
|
155
|
+
style={{
|
|
156
|
+
display: "flex",
|
|
157
|
+
flexDirection: "column",
|
|
158
|
+
rowGap: "1.5rem",
|
|
159
|
+
}}
|
|
160
|
+
>
|
|
161
|
+
<ConfigurableQuestionsSection
|
|
162
|
+
register={register}
|
|
163
|
+
specificQuestions={questions}
|
|
164
|
+
/>
|
|
165
|
+
</div>
|
|
166
|
+
</Layer>
|
|
167
|
+
</Tile>
|
|
168
|
+
</Layer>
|
|
169
|
+
</>
|
|
170
|
+
) : null}
|
|
171
|
+
</div>
|
|
172
|
+
)}
|
|
173
|
+
</div>
|
|
174
|
+
);
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
export default SessionDetailsForm;
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { Button } from "@carbon/react";
|
|
2
|
+
import React, { useContext, useEffect, useState } from "react";
|
|
3
|
+
import styles from "./styles.scss";
|
|
4
|
+
import { useTranslation } from "react-i18next";
|
|
5
|
+
import GroupFormWorkflowContext from "../context/GroupFormWorkflowContext";
|
|
6
|
+
import { FormProvider, useForm, useFormContext } from "react-hook-form";
|
|
7
|
+
import CancelModal from "../CancelModal";
|
|
8
|
+
import SessionDetailsForm from "./SessionDetailsForm";
|
|
9
|
+
|
|
10
|
+
const NewGroupWorkflowButtons = () => {
|
|
11
|
+
const { t } = useTranslation();
|
|
12
|
+
const context = useContext(GroupFormWorkflowContext);
|
|
13
|
+
const { workflowState, patientUuids } = context;
|
|
14
|
+
const [cancelModalOpen, setCancelModalOpen] = useState(false);
|
|
15
|
+
if (workflowState !== "NEW_GROUP_SESSION") return null;
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<>
|
|
19
|
+
<div className={styles.rightPanelActionButtons}>
|
|
20
|
+
<Button kind="secondary" type="submit" disabled={!patientUuids.length}>
|
|
21
|
+
{t("createNewSession", "Create New Session")}
|
|
22
|
+
</Button>
|
|
23
|
+
<Button
|
|
24
|
+
kind="tertiary"
|
|
25
|
+
onClick={() => {
|
|
26
|
+
setCancelModalOpen(true);
|
|
27
|
+
}}
|
|
28
|
+
>
|
|
29
|
+
{t("cancel", "Cancel")}
|
|
30
|
+
</Button>
|
|
31
|
+
</div>
|
|
32
|
+
<CancelModal
|
|
33
|
+
open={cancelModalOpen}
|
|
34
|
+
setOpen={setCancelModalOpen}
|
|
35
|
+
context={context}
|
|
36
|
+
/>
|
|
37
|
+
</>
|
|
38
|
+
);
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
const GroupIdField = () => {
|
|
42
|
+
const { t } = useTranslation();
|
|
43
|
+
const {
|
|
44
|
+
register,
|
|
45
|
+
formState: { errors },
|
|
46
|
+
setValue,
|
|
47
|
+
} = useFormContext();
|
|
48
|
+
const { activeGroupUuid } = useContext(GroupFormWorkflowContext);
|
|
49
|
+
|
|
50
|
+
useEffect(() => {
|
|
51
|
+
if (activeGroupUuid) setValue("groupUuid", activeGroupUuid);
|
|
52
|
+
}, [activeGroupUuid, setValue]);
|
|
53
|
+
|
|
54
|
+
return (
|
|
55
|
+
<>
|
|
56
|
+
<input
|
|
57
|
+
hidden
|
|
58
|
+
{...register("groupUuid", {
|
|
59
|
+
value: activeGroupUuid,
|
|
60
|
+
required: t("chooseGroupError", "Please choose a group."),
|
|
61
|
+
})}
|
|
62
|
+
/>
|
|
63
|
+
{errors.groupUuid && !activeGroupUuid && (
|
|
64
|
+
<div className={styles.formError}>
|
|
65
|
+
{errors.groupUuid.message as string}
|
|
66
|
+
</div>
|
|
67
|
+
)}
|
|
68
|
+
</>
|
|
69
|
+
);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const SessionMetaWorkspace = () => {
|
|
73
|
+
const { t } = useTranslation();
|
|
74
|
+
const { setSessionMeta, workflowState } = useContext(
|
|
75
|
+
GroupFormWorkflowContext
|
|
76
|
+
);
|
|
77
|
+
const methods = useForm();
|
|
78
|
+
|
|
79
|
+
const onSubmit = (data) => {
|
|
80
|
+
const { sessionDate, ...rest } = data;
|
|
81
|
+
setSessionMeta({ ...rest, sessionDate: sessionDate[0] });
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
if (workflowState !== "NEW_GROUP_SESSION") return null;
|
|
85
|
+
|
|
86
|
+
return (
|
|
87
|
+
<FormProvider {...methods}>
|
|
88
|
+
<form onSubmit={methods.handleSubmit(onSubmit)}>
|
|
89
|
+
<div className={styles.workspace}>
|
|
90
|
+
<div className={styles.formMainContent}>
|
|
91
|
+
<div className={styles.formContainer}>
|
|
92
|
+
<SessionDetailsForm />
|
|
93
|
+
</div>
|
|
94
|
+
<div className={styles.rightPanel}>
|
|
95
|
+
<h4>{t("newGroupSession", "New Group Session")}</h4>
|
|
96
|
+
<GroupIdField />
|
|
97
|
+
<hr style={{ width: "100%" }} />
|
|
98
|
+
<NewGroupWorkflowButtons />
|
|
99
|
+
</div>
|
|
100
|
+
</div>
|
|
101
|
+
</div>
|
|
102
|
+
</form>
|
|
103
|
+
</FormProvider>
|
|
104
|
+
);
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export default SessionMetaWorkspace;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import React, { useCallback, useContext, useMemo, useState } from "react";
|
|
2
|
+
import { Edit } from "@carbon/react/icons";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
Checkbox,
|
|
6
|
+
SkeletonText,
|
|
7
|
+
Table,
|
|
8
|
+
TableHead,
|
|
9
|
+
TableRow,
|
|
10
|
+
TableHeader,
|
|
11
|
+
TableBody,
|
|
12
|
+
TableCell,
|
|
13
|
+
Button,
|
|
14
|
+
} from "@carbon/react";
|
|
15
|
+
import { useTranslation } from "react-i18next";
|
|
16
|
+
import GroupFormWorkflowContext from "../../context/GroupFormWorkflowContext";
|
|
17
|
+
import AddGroupModal from "../../add-group-modal/AddGroupModal";
|
|
18
|
+
|
|
19
|
+
const PatientRow = ({ patient }) => {
|
|
20
|
+
const { patientUuids, addPatientUuid, removePatientUuid } = useContext(
|
|
21
|
+
GroupFormWorkflowContext
|
|
22
|
+
);
|
|
23
|
+
const givenName = patient?.name?.[0]?.given?.[0];
|
|
24
|
+
const familyName = patient?.name?.[0]?.family;
|
|
25
|
+
const identifier = patient?.identifier?.[0]?.value;
|
|
26
|
+
|
|
27
|
+
const handleOnChange = (e, { checked }) => {
|
|
28
|
+
if (checked) {
|
|
29
|
+
addPatientUuid(patient.id);
|
|
30
|
+
} else {
|
|
31
|
+
removePatientUuid(patient.id);
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
if (!patient) {
|
|
36
|
+
return (
|
|
37
|
+
<TableRow>
|
|
38
|
+
<TableCell>
|
|
39
|
+
<SkeletonText />
|
|
40
|
+
</TableCell>
|
|
41
|
+
<TableCell>
|
|
42
|
+
<SkeletonText />
|
|
43
|
+
</TableCell>
|
|
44
|
+
<TableCell>
|
|
45
|
+
<Checkbox diabled />
|
|
46
|
+
</TableCell>
|
|
47
|
+
</TableRow>
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return (
|
|
52
|
+
<TableRow>
|
|
53
|
+
<TableCell>
|
|
54
|
+
{patient.display ||
|
|
55
|
+
patient.displayName ||
|
|
56
|
+
[givenName, familyName].join(" ")}
|
|
57
|
+
</TableCell>
|
|
58
|
+
<TableCell>{identifier}</TableCell>
|
|
59
|
+
<TableCell>
|
|
60
|
+
<Checkbox
|
|
61
|
+
checked={patientUuids.includes(patient.id)}
|
|
62
|
+
labelText={patient.id}
|
|
63
|
+
hideLabel
|
|
64
|
+
id={`${identifier}-attendance-checkbox`}
|
|
65
|
+
onChange={handleOnChange}
|
|
66
|
+
/>
|
|
67
|
+
</TableCell>
|
|
68
|
+
</TableRow>
|
|
69
|
+
);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const AttendanceTable = ({ patients }) => {
|
|
73
|
+
const { t } = useTranslation();
|
|
74
|
+
const { activeGroupUuid, activeGroupName, activeGroupMembers } = useContext(
|
|
75
|
+
GroupFormWorkflowContext
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const [isOpen, setOpen] = useState(false);
|
|
79
|
+
|
|
80
|
+
const headers = [
|
|
81
|
+
t("name", "Name"),
|
|
82
|
+
t("identifier", "Patient ID"),
|
|
83
|
+
t("patientIsPresent", "Patient is present"),
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
const onPostCancel = useCallback(() => {
|
|
87
|
+
setOpen(false);
|
|
88
|
+
}, []);
|
|
89
|
+
|
|
90
|
+
const onPostSubmit = useCallback(() => {
|
|
91
|
+
setOpen(false);
|
|
92
|
+
}, []);
|
|
93
|
+
|
|
94
|
+
const newArr = useMemo(() => {
|
|
95
|
+
return activeGroupMembers.map(function (value) {
|
|
96
|
+
const patient = patients.find((patient) => patient.id === value);
|
|
97
|
+
return { uuid: value, ...patient };
|
|
98
|
+
});
|
|
99
|
+
}, [activeGroupMembers, patients]);
|
|
100
|
+
|
|
101
|
+
if (!activeGroupUuid) {
|
|
102
|
+
return <div>{t("selectGroupFirst", "Please select a group first")}</div>;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<div>
|
|
107
|
+
<span style={{ flexGrow: 1 }} />
|
|
108
|
+
<Button kind="ghost" onClick={() => setOpen(true)}>
|
|
109
|
+
{t("editGroup", "Edit Group")}
|
|
110
|
+
<Edit size={20} />
|
|
111
|
+
</Button>
|
|
112
|
+
<AddGroupModal
|
|
113
|
+
{...{
|
|
114
|
+
cohortUuid: activeGroupUuid,
|
|
115
|
+
patients: newArr,
|
|
116
|
+
isCreate: false,
|
|
117
|
+
groupName: activeGroupName,
|
|
118
|
+
isOpen: isOpen,
|
|
119
|
+
onPostCancel: onPostCancel,
|
|
120
|
+
onPostSubmit: onPostSubmit,
|
|
121
|
+
}}
|
|
122
|
+
/>
|
|
123
|
+
<Table>
|
|
124
|
+
<TableHead>
|
|
125
|
+
<TableRow>
|
|
126
|
+
{headers.map((header, index) => (
|
|
127
|
+
<TableHeader key={index}>{header}</TableHeader>
|
|
128
|
+
))}
|
|
129
|
+
</TableRow>
|
|
130
|
+
</TableHead>
|
|
131
|
+
<TableBody>
|
|
132
|
+
{activeGroupMembers.map((patientUuid, index) => {
|
|
133
|
+
const patient = patients.find(
|
|
134
|
+
(patient) => patient.id === patientUuid
|
|
135
|
+
);
|
|
136
|
+
return <PatientRow patient={patient} key={index} />;
|
|
137
|
+
})}
|
|
138
|
+
</TableBody>
|
|
139
|
+
</Table>
|
|
140
|
+
</div>
|
|
141
|
+
);
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
export default AttendanceTable;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as AttendanceTable } from "./AttendanceTable";
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { TextInput, Select, SelectItem } from "@carbon/react";
|
|
3
|
+
import { type FieldValues, type UseFormRegister } from "react-hook-form";
|
|
4
|
+
import { type SpecificQuestion } from "../../types";
|
|
5
|
+
|
|
6
|
+
interface ConfigurableQuestionsSectionProps {
|
|
7
|
+
specificQuestions: Array<SpecificQuestion>;
|
|
8
|
+
register?: UseFormRegister<FieldValues>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const ConfigurableQuestionsSection: React.FC<
|
|
12
|
+
ConfigurableQuestionsSectionProps
|
|
13
|
+
> = ({ register, specificQuestions }) => {
|
|
14
|
+
return (
|
|
15
|
+
<>
|
|
16
|
+
{specificQuestions?.map((specificQuestion) => (
|
|
17
|
+
<div key={specificQuestion.question.id}>
|
|
18
|
+
{specificQuestion?.answers?.length > 0 ? (
|
|
19
|
+
<Select
|
|
20
|
+
{...register(specificQuestion.question.id, { required: false })}
|
|
21
|
+
id={specificQuestion.question.id}
|
|
22
|
+
labelText={specificQuestion.question.display}
|
|
23
|
+
>
|
|
24
|
+
<SelectItem value="" text="" />
|
|
25
|
+
{specificQuestion.answers.map((answer) => (
|
|
26
|
+
<SelectItem
|
|
27
|
+
key={answer.value}
|
|
28
|
+
value={answer.value}
|
|
29
|
+
text={answer.display}
|
|
30
|
+
/>
|
|
31
|
+
))}
|
|
32
|
+
</Select>
|
|
33
|
+
) : (
|
|
34
|
+
<TextInput
|
|
35
|
+
id={specificQuestion.question.id}
|
|
36
|
+
{...register(specificQuestion.question.id, { required: false })}
|
|
37
|
+
type="text"
|
|
38
|
+
labelText={specificQuestion.question.display}
|
|
39
|
+
/>
|
|
40
|
+
)}
|
|
41
|
+
</div>
|
|
42
|
+
))}
|
|
43
|
+
</>
|
|
44
|
+
);
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export default ConfigurableQuestionsSection;
|
|
@@ -4,7 +4,6 @@ import { Events, Close } from "@carbon/react/icons";
|
|
|
4
4
|
import styles from "./styles.scss";
|
|
5
5
|
import { useTranslation } from "react-i18next";
|
|
6
6
|
import GroupFormWorkflowContext from "../../context/GroupFormWorkflowContext";
|
|
7
|
-
import { navigate } from "@openmrs/esm-framework";
|
|
8
7
|
|
|
9
8
|
const GroupDisplayHeader = () => {
|
|
10
9
|
const {
|
|
@@ -53,14 +52,7 @@ const GroupDisplayHeader = () => {
|
|
|
53
52
|
</Button>
|
|
54
53
|
</span>
|
|
55
54
|
<span>
|
|
56
|
-
<Button
|
|
57
|
-
kind="ghost"
|
|
58
|
-
onClick={() => {
|
|
59
|
-
destroySession();
|
|
60
|
-
// eslint-disable-next-line
|
|
61
|
-
navigate({ to: "${openmrsSpaBase}/forms" });
|
|
62
|
-
}}
|
|
63
|
-
>
|
|
55
|
+
<Button kind="ghost" onClick={() => destroySession()}>
|
|
64
56
|
{t("cancel", "Cancel")} <Close size={20} />
|
|
65
57
|
</Button>
|
|
66
58
|
</span>
|
|
@@ -1,26 +1,26 @@
|
|
|
1
|
-
@use '@carbon/
|
|
2
|
-
@use '@carbon/
|
|
3
|
-
@
|
|
1
|
+
@use '@carbon/colors';
|
|
2
|
+
@use '@carbon/layout';
|
|
3
|
+
@use '@carbon/type';
|
|
4
4
|
|
|
5
5
|
.container {
|
|
6
|
-
height:
|
|
6
|
+
height: layout.$spacing-11;
|
|
7
7
|
display: flex;
|
|
8
8
|
align-items: center;
|
|
9
|
-
background-color:
|
|
10
|
-
border-top: 0.0125rem solid
|
|
11
|
-
border-bottom: 0.0125rem solid
|
|
12
|
-
padding: 0
|
|
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
13
|
}
|
|
14
14
|
|
|
15
15
|
.photoPlaceholder {
|
|
16
|
-
height:
|
|
17
|
-
width:
|
|
16
|
+
height: layout.$spacing-09;
|
|
17
|
+
width: layout.$spacing-09;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
.groupAvatar {
|
|
21
|
-
width:
|
|
22
|
-
height:
|
|
23
|
-
margin:
|
|
21
|
+
width: layout.$spacing-09;
|
|
22
|
+
height: layout.$spacing-09;
|
|
23
|
+
margin: layout.$spacing-03 layout.$spacing-05 layout.$spacing-03 0;
|
|
24
24
|
border-radius: 1px;
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -30,29 +30,29 @@
|
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
.groupInfoContent {
|
|
33
|
-
margin-left:
|
|
34
|
-
margin-right:
|
|
33
|
+
margin-left: layout.$spacing-05;
|
|
34
|
+
margin-right: layout.$spacing-10;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
37
|
.groupInfoRow {
|
|
38
38
|
display: flex;
|
|
39
39
|
align-items: center;
|
|
40
40
|
& > button {
|
|
41
|
-
min-height:
|
|
41
|
+
min-height: layout.$spacing-07;
|
|
42
42
|
}
|
|
43
43
|
@include type.type-style('body-compact-02');
|
|
44
|
-
color:
|
|
44
|
+
color: colors.$gray-70;
|
|
45
45
|
column-gap: 0.8rem;
|
|
46
46
|
|
|
47
47
|
}
|
|
48
48
|
.sessionNotesLabel {
|
|
49
49
|
@include type.type-style('label-01');
|
|
50
|
-
margin-bottom:
|
|
50
|
+
margin-bottom: layout.$spacing-01
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
.groupEditBtn {
|
|
54
|
-
color:
|
|
55
|
-
margin:
|
|
54
|
+
color: colors.$gray-100;
|
|
55
|
+
margin: layout.$spacing-03;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
.groupMetaContent {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import React, { useState } from "react";
|
|
2
|
-
import { GroupType } from "../../context/GroupFormWorkflowContext";
|
|
2
|
+
import { type GroupType } from "../../context/GroupFormWorkflowContext";
|
|
3
3
|
import styles from "./compact-group-search.scss";
|
|
4
4
|
import GroupSearch from "./GroupSearch";
|
|
5
5
|
import { Button, Search } from "@carbon/react";
|
|
@@ -6,7 +6,7 @@ import { EmptyDataIllustration } from "../../empty-state/EmptyDataIllustration";
|
|
|
6
6
|
import CompactGroupResults, {
|
|
7
7
|
SearchResultSkeleton,
|
|
8
8
|
} from "./CompactGroupResults";
|
|
9
|
-
import { GroupType } from "../../context/GroupFormWorkflowContext";
|
|
9
|
+
import { type GroupType } from "../../context/GroupFormWorkflowContext";
|
|
10
10
|
import { useSearchCohortInfinite } from "../../hooks/useSearchEndpoint";
|
|
11
11
|
|
|
12
12
|
interface GroupSearchProps {
|
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
@use '@carbon/
|
|
2
|
-
@use '@carbon/
|
|
3
|
-
@use '@carbon/
|
|
4
|
-
@import '~@openmrs/esm-styleguide/src/vars';
|
|
1
|
+
@use '@carbon/colors';
|
|
2
|
+
@use '@carbon/layout';
|
|
3
|
+
@use '@carbon/type';
|
|
5
4
|
|
|
6
5
|
.patientSearchResult {
|
|
7
6
|
text-decoration: none;
|
|
8
7
|
display: flex;
|
|
9
8
|
align-items: center;
|
|
10
|
-
border-bottom: 1px solid
|
|
11
|
-
padding: 0
|
|
9
|
+
border-bottom: 1px solid colors.$gray-20;
|
|
10
|
+
padding: 0 layout.$spacing-04;
|
|
12
11
|
|
|
13
12
|
&:hover,
|
|
14
13
|
&:focus {
|
|
15
|
-
background-color:
|
|
14
|
+
background-color: colors.$gray-10;
|
|
16
15
|
}
|
|
17
16
|
}
|
|
18
17
|
|
|
@@ -29,9 +28,9 @@
|
|
|
29
28
|
}
|
|
30
29
|
|
|
31
30
|
.patientAvatar {
|
|
32
|
-
width:
|
|
33
|
-
height:
|
|
34
|
-
margin:
|
|
31
|
+
width: layout.$spacing-09;
|
|
32
|
+
height: layout.$spacing-09;
|
|
33
|
+
margin: layout.$spacing-03 layout.$spacing-05 layout.$spacing-03 0;
|
|
35
34
|
border-radius: 1px;
|
|
36
35
|
}
|
|
37
36
|
|
|
@@ -39,26 +38,26 @@
|
|
|
39
38
|
width: 100%;
|
|
40
39
|
display: flex;
|
|
41
40
|
flex-flow: column wrap;
|
|
42
|
-
margin:
|
|
41
|
+
margin: layout.$spacing-05;
|
|
43
42
|
cursor: pointer;
|
|
44
43
|
}
|
|
45
44
|
|
|
46
45
|
.demographics {
|
|
47
|
-
margin-top:
|
|
46
|
+
margin-top: layout.$spacing-03;
|
|
48
47
|
@include type.type-style('body-compact-02');
|
|
49
|
-
color:
|
|
48
|
+
color: colors.$gray-70;
|
|
50
49
|
}
|
|
51
50
|
|
|
52
51
|
.identifiers {
|
|
53
52
|
@include type.type-style('body-compact-02');
|
|
54
|
-
color:
|
|
53
|
+
color: colors.$gray-50;
|
|
55
54
|
}
|
|
56
55
|
|
|
57
56
|
.actionsContainer {
|
|
58
|
-
padding-top:
|
|
59
|
-
margin-top:
|
|
57
|
+
padding-top: layout.$spacing-03;
|
|
58
|
+
margin-top: layout.$spacing-05;
|
|
60
59
|
}
|
|
61
60
|
|
|
62
61
|
.middot {
|
|
63
|
-
margin: 0
|
|
62
|
+
margin: 0 layout.$spacing-03;
|
|
64
63
|
}
|