@openmrs/esm-fast-data-entry-app 1.0.0-pre.9 → 1.0.1-pre.101

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.
Files changed (186) hide show
  1. package/README.md +58 -12
  2. package/dist/153.js +1 -0
  3. package/dist/153.js.map +1 -0
  4. package/dist/233.js +2 -0
  5. package/dist/{382.js.LICENSE.txt → 233.js.LICENSE.txt} +3 -2
  6. package/dist/233.js.map +1 -0
  7. package/dist/262.js +1 -0
  8. package/dist/262.js.map +1 -0
  9. package/dist/279.js +1 -0
  10. package/dist/279.js.map +1 -0
  11. package/dist/294.js +1 -2
  12. package/dist/294.js.LICENSE.txt +2 -7
  13. package/dist/294.js.map +1 -1
  14. package/dist/327.js +1 -0
  15. package/dist/327.js.map +1 -0
  16. package/dist/409.js +2 -0
  17. package/dist/409.js.LICENSE.txt +27 -0
  18. package/dist/409.js.map +1 -0
  19. package/dist/415.js +1 -2
  20. package/dist/415.js.map +1 -1
  21. package/dist/559.js +1 -0
  22. package/dist/559.js.map +1 -0
  23. package/dist/574.js +1 -1
  24. package/dist/651.js +1 -0
  25. package/dist/651.js.map +1 -0
  26. package/dist/706.js +1 -0
  27. package/dist/706.js.map +1 -0
  28. package/dist/757.js +1 -0
  29. package/dist/800.js +2 -0
  30. package/dist/800.js.LICENSE.txt +5 -0
  31. package/dist/800.js.map +1 -0
  32. package/dist/820.js +1 -0
  33. package/dist/820.js.map +1 -0
  34. package/dist/883.js +1 -0
  35. package/dist/883.js.map +1 -0
  36. package/dist/889.js +1 -0
  37. package/dist/889.js.map +1 -0
  38. package/dist/897.js +2 -0
  39. package/dist/897.js.LICENSE.txt +21 -0
  40. package/dist/897.js.map +1 -0
  41. package/dist/92.js +1 -0
  42. package/dist/92.js.map +1 -0
  43. package/dist/935.js +2 -0
  44. package/dist/{735.js.LICENSE.txt → 935.js.LICENSE.txt} +6 -16
  45. package/dist/935.js.map +1 -0
  46. package/dist/959.js +1 -0
  47. package/dist/959.js.map +1 -0
  48. package/dist/main.js +1 -0
  49. package/dist/main.js.map +1 -0
  50. package/dist/openmrs-esm-fast-data-entry-app.js +1 -1
  51. package/dist/openmrs-esm-fast-data-entry-app.js.buildmanifest.json +374 -89
  52. package/dist/openmrs-esm-fast-data-entry-app.js.map +1 -1
  53. package/dist/routes.json +1 -0
  54. package/docs/config-icrc-forms.png +0 -0
  55. package/docs/config-other-forms.png +0 -0
  56. package/docs/configuring-form-categories.md +77 -0
  57. package/docs/fde-workflow.mov +0 -0
  58. package/docs/form-workflow-state-diagram.png +0 -0
  59. package/jest.config.json +21 -18
  60. package/package.json +100 -106
  61. package/src/CancelModal.tsx +48 -0
  62. package/src/CompleteModal.tsx +46 -0
  63. package/src/FormBootstrap.tsx +166 -0
  64. package/src/Root.tsx +14 -3
  65. package/src/add-group-modal/AddGroupModal.tsx +288 -0
  66. package/src/add-group-modal/styles.scss +45 -0
  67. package/src/config-schema.ts +85 -31
  68. package/src/context/FormWorkflowContext.tsx +126 -0
  69. package/src/context/FormWorkflowReducer.ts +287 -0
  70. package/src/context/GroupFormWorkflowContext.tsx +176 -0
  71. package/src/context/GroupFormWorkflowReducer.ts +430 -0
  72. package/src/empty-state/EmptyDataIllustration.tsx +51 -0
  73. package/src/empty-state/EmptyState.tsx +33 -0
  74. package/src/empty-state/styles.scss +55 -0
  75. package/src/form-entry-workflow/FormEntryWorkflow.tsx +196 -0
  76. package/src/form-entry-workflow/form-review-card/FormReviewCard.tsx +50 -0
  77. package/src/form-entry-workflow/form-review-card/index.ts +3 -0
  78. package/src/form-entry-workflow/form-review-card/styles.scss +39 -0
  79. package/src/form-entry-workflow/index.ts +3 -0
  80. package/src/form-entry-workflow/patient-banner/PatientBanner.test.tsx +9 -0
  81. package/src/form-entry-workflow/patient-banner/PatientBanner.tsx +86 -0
  82. package/src/form-entry-workflow/patient-banner/index.ts +3 -0
  83. package/src/form-entry-workflow/patient-banner/styles.scss +45 -0
  84. package/src/form-entry-workflow/patient-search-header/PatientSearchHeader.tsx +63 -0
  85. package/src/form-entry-workflow/patient-search-header/index.ts +3 -0
  86. package/src/form-entry-workflow/patient-search-header/styles.scss +22 -0
  87. package/src/form-entry-workflow/styles.scss +65 -0
  88. package/src/form-entry-workflow/workflow-review/WorkflowReview.tsx +35 -0
  89. package/src/form-entry-workflow/workflow-review/index.ts +3 -0
  90. package/src/form-entry-workflow/workflow-review/styles.scss +34 -0
  91. package/src/forms-app-menu-link.tsx +3 -2
  92. package/src/forms-page/FormsPage.tsx +134 -0
  93. package/src/forms-page/forms-table/FormsTable.tsx +137 -0
  94. package/src/forms-page/forms-table/index.ts +3 -0
  95. package/src/forms-page/forms-table/styles.scss +20 -0
  96. package/src/forms-page/index.ts +3 -0
  97. package/src/forms-page/styles.scss +11 -0
  98. package/src/group-form-entry-workflow/GroupFormEntryWorkflow.tsx +26 -0
  99. package/src/group-form-entry-workflow/GroupSessionWorkspace.tsx +247 -0
  100. package/src/group-form-entry-workflow/SessionDetailsForm.tsx +131 -0
  101. package/src/group-form-entry-workflow/SessionMetaWorkspace.tsx +107 -0
  102. package/src/group-form-entry-workflow/attendance-table/AttendanceTable.tsx +144 -0
  103. package/src/group-form-entry-workflow/attendance-table/index.ts +1 -0
  104. package/src/group-form-entry-workflow/group-display-header/GroupDisplayHeader.test.tsx +9 -0
  105. package/src/group-form-entry-workflow/group-display-header/GroupDisplayHeader.tsx +63 -0
  106. package/src/group-form-entry-workflow/group-display-header/index.ts +3 -0
  107. package/src/group-form-entry-workflow/group-display-header/styles.scss +60 -0
  108. package/src/group-form-entry-workflow/group-search/CompactGroupResults.tsx +139 -0
  109. package/src/group-form-entry-workflow/group-search/CompactGroupSearch.tsx +68 -0
  110. package/src/group-form-entry-workflow/group-search/GroupSearch.tsx +150 -0
  111. package/src/group-form-entry-workflow/group-search/compact-group-result.scss +64 -0
  112. package/src/group-form-entry-workflow/group-search/compact-group-search.scss +35 -0
  113. package/src/group-form-entry-workflow/group-search/group-search.scss +96 -0
  114. package/src/group-form-entry-workflow/group-search-header/GroupSearchHeader.tsx +73 -0
  115. package/src/group-form-entry-workflow/group-search-header/index.ts +3 -0
  116. package/src/group-form-entry-workflow/group-search-header/styles.scss +20 -0
  117. package/src/group-form-entry-workflow/index.ts +3 -0
  118. package/src/group-form-entry-workflow/styles.scss +97 -0
  119. package/src/hooks/index.ts +7 -0
  120. package/src/hooks/useFormState.ts +23 -0
  121. package/src/hooks/useGetAllForms.ts +45 -0
  122. package/src/hooks/useGetEncounter.ts +21 -0
  123. package/src/hooks/useGetPatient.ts +23 -0
  124. package/src/hooks/useGetPatients.ts +34 -0
  125. package/src/hooks/useGetSystemSetting.ts +38 -0
  126. package/src/hooks/useKeyPress.ts +31 -0
  127. package/src/hooks/usePostEndpoint.ts +76 -0
  128. package/src/hooks/useSearchEndpoint.ts +120 -0
  129. package/src/hooks/useStartVisit.ts +92 -0
  130. package/src/index.ts +26 -62
  131. package/src/patient-card/PatientCard.tsx +67 -0
  132. package/src/patient-card/index.ts +3 -0
  133. package/src/patient-card/styles.scss +46 -0
  134. package/src/routes.json +24 -0
  135. package/tools/i18next-parser.config.js +93 -0
  136. package/translations/en.json +69 -4
  137. package/translations/fr.json +50 -0
  138. package/tsconfig.json +26 -23
  139. package/.editorconfig +0 -12
  140. package/.eslintignore +0 -2
  141. package/.eslintrc +0 -4
  142. package/.github/workflows/node.js.yml +0 -79
  143. package/.husky/pre-commit +0 -6
  144. package/.husky/pre-push +0 -6
  145. package/.prettierignore +0 -14
  146. package/dist/24.js +0 -3
  147. package/dist/24.js.LICENSE.txt +0 -16
  148. package/dist/24.js.map +0 -1
  149. package/dist/296.js +0 -2
  150. package/dist/296.js.map +0 -1
  151. package/dist/299.js +0 -2
  152. package/dist/299.js.map +0 -1
  153. package/dist/382.js +0 -3
  154. package/dist/382.js.map +0 -1
  155. package/dist/595.js +0 -3
  156. package/dist/595.js.LICENSE.txt +0 -1
  157. package/dist/595.js.map +0 -1
  158. package/dist/69.js +0 -2
  159. package/dist/69.js.map +0 -1
  160. package/dist/735.js +0 -3
  161. package/dist/735.js.map +0 -1
  162. package/dist/777.js +0 -2
  163. package/dist/777.js.map +0 -1
  164. package/dist/860.js +0 -2
  165. package/dist/860.js.map +0 -1
  166. package/dist/906.js +0 -2
  167. package/dist/906.js.map +0 -1
  168. package/dist/openmrs-esm-fast-data-entry-app.old +0 -2
  169. package/src/boxes/extensions/blue-box.tsx +0 -15
  170. package/src/boxes/extensions/box.scss +0 -23
  171. package/src/boxes/extensions/brand-box.tsx +0 -15
  172. package/src/boxes/extensions/red-box.tsx +0 -15
  173. package/src/boxes/slot/boxes.css +0 -23
  174. package/src/boxes/slot/boxes.tsx +0 -19
  175. package/src/forms/FormsRoot.tsx +0 -32
  176. package/src/forms/FormsTable.tsx +0 -64
  177. package/src/forms/mockData.ts +0 -43
  178. package/src/greeter/greeter.css +0 -4
  179. package/src/greeter/greeter.test.tsx +0 -29
  180. package/src/greeter/greeter.tsx +0 -25
  181. package/src/hello.css +0 -3
  182. package/src/hello.test.tsx +0 -45
  183. package/src/hello.tsx +0 -30
  184. package/src/patient-getter/patient-getter.resource.ts +0 -31
  185. package/src/patient-getter/patient-getter.test.tsx +0 -28
  186. package/src/patient-getter/patient-getter.tsx +0 -28
@@ -0,0 +1,196 @@
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 FormWorkflowContext, {
10
+ FormWorkflowProvider,
11
+ } from "../context/FormWorkflowContext";
12
+ import WorkflowReview from "./workflow-review";
13
+ import PatientBanner from "./patient-banner";
14
+ import CompleteModal from "../CompleteModal";
15
+ import CancelModal from "../CancelModal";
16
+ import useStartVisit from "../hooks/useStartVisit";
17
+
18
+ const WorkflowNavigationButtons = () => {
19
+ const context = useContext(FormWorkflowContext);
20
+ const { workflowState, destroySession } = context;
21
+ const [cancelModalOpen, setCancelModalOpen] = useState(false);
22
+ const [completeModalOpen, setCompleteModalOpen] = useState(false);
23
+ const { t } = useTranslation();
24
+
25
+ if (!workflowState) return null;
26
+
27
+ return (
28
+ <>
29
+ <div className={styles.rightPanelActionButtons}>
30
+ <Button
31
+ kind="secondary"
32
+ onClick={
33
+ workflowState === "NEW_PATIENT"
34
+ ? () => destroySession()
35
+ : () => setCompleteModalOpen(true)
36
+ }
37
+ >
38
+ {t("saveAndComplete", "Save & Complete")}
39
+ </Button>
40
+ <Button kind="tertiary" onClick={() => setCancelModalOpen(true)}>
41
+ {t("cancel", "Cancel")}
42
+ </Button>
43
+ </div>
44
+ <CancelModal
45
+ open={cancelModalOpen}
46
+ setOpen={setCancelModalOpen}
47
+ context={context}
48
+ />
49
+ <CompleteModal
50
+ open={completeModalOpen}
51
+ setOpen={setCompleteModalOpen}
52
+ context={context}
53
+ />
54
+ </>
55
+ );
56
+ };
57
+
58
+ const FormWorkspace = () => {
59
+ const {
60
+ patientUuids,
61
+ activePatientUuid,
62
+ activeEncounterUuid,
63
+ saveEncounter,
64
+ activeFormUuid,
65
+ editEncounter,
66
+ encounters,
67
+ singleSessionVisitTypeUuid,
68
+ } = useContext(FormWorkflowContext);
69
+ const { t } = useTranslation();
70
+
71
+ const [encounter, setEncounter] = useState(null);
72
+ const [visit, setVisit] = useState(null);
73
+ const { sessionLocation } = useSession();
74
+
75
+ const {
76
+ saveVisit,
77
+ updateEncounter,
78
+ success: visitSaveSuccess,
79
+ } = useStartVisit({
80
+ showSuccessNotification: false,
81
+ showErrorNotification: true,
82
+ });
83
+
84
+ const handlePostResponse = (encounter) => {
85
+ if (encounter && encounter.uuid) {
86
+ saveEncounter(encounter.uuid);
87
+ setEncounter(encounter);
88
+ }
89
+ };
90
+
91
+ useEffect(() => {
92
+ if (encounter && visit) {
93
+ // Update encounter so that it belongs to the created visit
94
+ updateEncounter({ uuid: encounter.uuid, visit: visit.uuid });
95
+ }
96
+ }, [encounter, visit, updateEncounter]);
97
+
98
+ useEffect(() => {
99
+ if (visitSaveSuccess) {
100
+ setVisit(visitSaveSuccess.data);
101
+ }
102
+ }, [visitSaveSuccess]);
103
+
104
+ const handleEncounterCreate = useCallback(
105
+ (payload) => {
106
+ payload.location = sessionLocation?.uuid;
107
+ payload.encounterDatetime = payload.encounterDatetime
108
+ ? payload.encounterDatetime
109
+ : new Date().toISOString();
110
+ // Create a visit with the same date as the encounter being saved
111
+ const visitStartDatetime = new Date(payload.encounterDatetime);
112
+ const visitStopDatetime = new Date(payload.encounterDatetime);
113
+ saveVisit({
114
+ patientUuid: activePatientUuid,
115
+ startDatetime: visitStartDatetime.toISOString(),
116
+ stopDatetime: visitStopDatetime.toISOString(),
117
+ visitType: singleSessionVisitTypeUuid,
118
+ location: sessionLocation?.uuid,
119
+ });
120
+ },
121
+ [activePatientUuid, singleSessionVisitTypeUuid, saveVisit, sessionLocation]
122
+ );
123
+
124
+ return (
125
+ <div className={styles.workspace}>
126
+ {!patientUuids.length && (
127
+ <div className={styles.selectPatientMessage}>
128
+ {t("selectPatientFirst", "Please select a patient first")}
129
+ </div>
130
+ )}
131
+ {!!patientUuids.length && (
132
+ <div className={styles.formMainContent}>
133
+ <div className={styles.formContainer}>
134
+ <FormBootstrap
135
+ patientUuid={activePatientUuid}
136
+ encounterUuid={activeEncounterUuid}
137
+ {...{
138
+ formUuid: activeFormUuid,
139
+ handlePostResponse,
140
+ handleEncounterCreate,
141
+ }}
142
+ />
143
+ </div>
144
+ <div className={styles.rightPanel}>
145
+ <h4>Forms filled</h4>
146
+ <div className={styles.patientCardsSection}>
147
+ {patientUuids.map((patientUuid) => (
148
+ <PatientCard
149
+ key={patientUuid}
150
+ {...{
151
+ patientUuid,
152
+ activePatientUuid,
153
+ editEncounter,
154
+ encounters,
155
+ }}
156
+ />
157
+ ))}
158
+ </div>
159
+ <WorkflowNavigationButtons />
160
+ </div>
161
+ </div>
162
+ )}
163
+ </div>
164
+ );
165
+ };
166
+
167
+ const FormEntryWorkflow = () => {
168
+ const { workflowState } = useContext(FormWorkflowContext);
169
+ return (
170
+ <>
171
+ <div className={styles.breadcrumbsContainer}>
172
+ <ExtensionSlot extensionSlotName="breadcrumbs-slot" />
173
+ </div>
174
+ {workflowState === "REVIEW" && <WorkflowReview />}
175
+ {workflowState !== "REVIEW" && (
176
+ <>
177
+ <PatientSearchHeader />
178
+ <PatientBanner />
179
+ <div className={styles.workspaceWrapper}>
180
+ <FormWorkspace />
181
+ </div>
182
+ </>
183
+ )}
184
+ </>
185
+ );
186
+ };
187
+
188
+ const FormEntryWorkflowWrapper = () => {
189
+ return (
190
+ <FormWorkflowProvider>
191
+ <FormEntryWorkflow />
192
+ </FormWorkflowProvider>
193
+ );
194
+ };
195
+
196
+ export default FormEntryWorkflowWrapper;
@@ -0,0 +1,50 @@
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
+
8
+ const FormReviewCard = ({ patientUuid }) => {
9
+ const { encounters, editEncounter } = useContext(FormWorkflowContext);
10
+ const patient = useGetPatient(patientUuid);
11
+ const givenName = patient?.name?.[0]?.given?.[0];
12
+ const familyName = patient?.name?.[0]?.family;
13
+ const identifier = patient?.identifier?.[0]?.value;
14
+ const encounterUuid = encounters?.[patientUuid];
15
+ const { encounter } = useGetEncounter(encounterUuid);
16
+ const { t } = useTranslation();
17
+
18
+ return (
19
+ <div className={styles.formReviewCard}>
20
+ <Accordion align="start">
21
+ <AccordionItem
22
+ title={
23
+ <>
24
+ <span className={styles.identifier}>{identifier}</span>
25
+ <span className={styles.displayName}>
26
+ {givenName} {familyName}
27
+ </span>
28
+ </>
29
+ }
30
+ className={styles.accordionItem}
31
+ >
32
+ {encounter && encounter?.obs && encounter.obs?.length && (
33
+ <div className={styles.dataField}>
34
+ <ul>
35
+ {encounter.obs.map((obs, index) => (
36
+ <li key={index}>{obs.display}</li>
37
+ ))}
38
+ </ul>
39
+ </div>
40
+ )}
41
+ <Button kind="primary" onClick={() => editEncounter(patientUuid)}>
42
+ {t("goToForm", "Go To Form")}
43
+ </Button>
44
+ </AccordionItem>
45
+ </Accordion>
46
+ </div>
47
+ );
48
+ };
49
+
50
+ export default FormReviewCard;
@@ -0,0 +1,3 @@
1
+ import FormReviewCard from "./FormReviewCard";
2
+
3
+ export default FormReviewCard;
@@ -0,0 +1,39 @@
1
+ @use '@carbon/styles/scss/spacing';
2
+ // @use '@carbon/colors';
3
+ @use '@carbon/styles/scss/type';
4
+ @import '~@openmrs/esm-styleguide/src/vars';
5
+
6
+
7
+ .formReviewCard {
8
+ background-color: $ui-02;
9
+ padding: spacing.$spacing-02;
10
+ }
11
+
12
+ .formReviewCard :global(.cds--accordion) :global(.cds--accordion__item) {
13
+ border: none;
14
+ }
15
+
16
+ .formReviewCard :global(.cds--accordion__title) {
17
+ display: flex;
18
+ align-items: baseline;
19
+ column-gap: spacing.$spacing-05;
20
+ }
21
+
22
+ .formReviewCard :global(.cds--accordion__content) {
23
+ padding: spacing.$spacing-03;
24
+ }
25
+
26
+ .dataField {
27
+ @include type.type-style('code-02');
28
+ background-color: $ui-01;
29
+ padding: spacing.$spacing-03;
30
+ }
31
+
32
+ .displayName {
33
+ @include type.type-style('heading-02');
34
+ font-weight: bold;
35
+ }
36
+
37
+ .identifier {
38
+ @include type.type-style('body-compact-01')
39
+ }
@@ -0,0 +1,3 @@
1
+ import FormEntryWorkflow from "./FormEntryWorkflow";
2
+
3
+ export default FormEntryWorkflow;
@@ -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
+ });
@@ -0,0 +1,86 @@
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
+
9
+ const SkeletonPatientInfo = () => {
10
+ return (
11
+ <div className={styles.container}>
12
+ <SkeletonPlaceholder className={styles.photoPlaceholder} />
13
+ <div className={styles.patientInfoContent}>
14
+ <div className={styles.patientInfoRow}>
15
+ <SkeletonText width="7rem" lineCount={1} />
16
+ </div>
17
+ <div className={styles.patientInfoRow}>
18
+ <span>
19
+ <SkeletonText width="1rem" lineCount={1} />
20
+ </span>
21
+ <span>&middot;</span>
22
+ <span>
23
+ <SkeletonText width="1rem" lineCount={1} />
24
+ </span>
25
+ <span>&middot;</span>
26
+ <span>
27
+ <SkeletonText width="1rem" lineCount={1} />
28
+ </span>
29
+ </div>
30
+ </div>
31
+ </div>
32
+ );
33
+ };
34
+
35
+ const PatientBanner = () => {
36
+ const { activePatientUuid, workflowState } = useContext(FormWorkflowContext);
37
+ const patient = useGetPatient(activePatientUuid);
38
+ const { t } = useTranslation();
39
+ const patientName = `${patient?.name?.[0].given?.join(" ")} ${
40
+ patient?.name?.[0]?.family
41
+ }`;
42
+
43
+ const patientPhotoSlotState = React.useMemo(
44
+ () => ({ patientUuid: patient?.id, patientName, size: "small" }),
45
+ [patient?.id, patientName]
46
+ );
47
+
48
+ if (workflowState === "NEW_PATIENT") return null;
49
+
50
+ if (!patient) {
51
+ return <SkeletonPatientInfo />;
52
+ }
53
+
54
+ return (
55
+ <div className={styles.container}>
56
+ <ExtensionSlot
57
+ extensionSlotName="patient-photo-slot"
58
+ state={patientPhotoSlotState}
59
+ />
60
+ <div className={styles.patientInfoContent}>
61
+ <div className={styles.patientInfoRow}>
62
+ <span className={styles.patientName}>{patientName}</span>
63
+ </div>
64
+ <div className={styles.patientInfoRow}>
65
+ <span>
66
+ {(patient.gender ?? t("unknown", "Unknown")).replace(/^\w/, (c) =>
67
+ c.toUpperCase()
68
+ )}
69
+ </span>
70
+ <span>&middot;</span>
71
+ <span>{age(patient.birthDate)}</span>
72
+ <span>&middot;</span>
73
+ <span>
74
+ {patient.identifier.length
75
+ ? patient.identifier
76
+ .map((identifier) => identifier.value)
77
+ .join(", ")
78
+ : "--"}
79
+ </span>
80
+ </div>
81
+ </div>
82
+ </div>
83
+ );
84
+ };
85
+
86
+ export default PatientBanner;
@@ -0,0 +1,3 @@
1
+ import PatientBanner from "./PatientBanner";
2
+
3
+ export default PatientBanner;
@@ -0,0 +1,45 @@
1
+ @use '@carbon/styles/scss/spacing';
2
+ // @use '@carbon/colors';
3
+ @use '@carbon/styles/scss/type';
4
+ @import '~@openmrs/esm-styleguide/src/vars';
5
+
6
+ .container {
7
+ height: spacing.$spacing-11;
8
+ display: flex;
9
+ align-items: center;
10
+ background-color: $ui-02;
11
+ border-top: 0.0125rem solid $ui-03;
12
+ border-bottom: 0.0125rem solid $ui-03;
13
+ padding: 0 spacing.$spacing-05;
14
+ }
15
+
16
+ .photoPlaceholder {
17
+ height: 48px;
18
+ width: 48px;
19
+ }
20
+
21
+ .patientName {
22
+ @include type.type-style('heading-03');
23
+ font-weight: 600;
24
+ }
25
+
26
+ .patientInfoContent {
27
+ width: 100%;
28
+ margin-left: 1rem;
29
+ }
30
+
31
+ .patientInfoRow {
32
+ display: flex;
33
+ align-items: center;
34
+ & > button {
35
+ min-height: 2rem;
36
+ }
37
+ @include type.type-style('body-compact-02');
38
+ color: $text-02;
39
+ column-gap: 0.8rem;
40
+ }
41
+
42
+ .patientEditBtn {
43
+ color: $ui-05;
44
+ margin: spacing.$spacing-03;
45
+ }
@@ -0,0 +1,63 @@
1
+ import { Add, Close } from "@carbon/react/icons";
2
+ import {
3
+ ExtensionSlot,
4
+ interpolateUrl,
5
+ navigate,
6
+ } from "@openmrs/esm-framework";
7
+ import { Button } from "@carbon/react";
8
+ import React, { useContext } from "react";
9
+ import { Link } from "react-router-dom";
10
+ import FormWorkflowContext from "../../context/FormWorkflowContext";
11
+ import styles from "./styles.scss";
12
+ import { useTranslation } from "react-i18next";
13
+
14
+ const PatientSearchHeader = () => {
15
+ const { addPatient, workflowState, activeFormUuid } =
16
+ useContext(FormWorkflowContext);
17
+ const handleSelectPatient = (patient) => {
18
+ addPatient(patient.uuid);
19
+ };
20
+ const { t } = useTranslation();
21
+
22
+ if (workflowState !== "NEW_PATIENT") return null;
23
+
24
+ const afterUrl = encodeURIComponent(
25
+ `\${openmrsSpaBase}/forms/form/${activeFormUuid}?patientUuid=\${patientUuid}`
26
+ );
27
+ const patientRegistrationUrl = interpolateUrl(
28
+ `\${openmrsSpaBase}/patient-registration?afterUrl=${afterUrl}`
29
+ );
30
+
31
+ return (
32
+ <div className={styles.searchHeaderContainer}>
33
+ <span className={styles.padded}>{t("nextPatient", "Next patient")}:</span>
34
+ <span className={styles.searchBarWrapper}>
35
+ <ExtensionSlot
36
+ extensionSlotName="patient-search-bar-slot"
37
+ state={{
38
+ selectPatientAction: handleSelectPatient,
39
+ buttonProps: {
40
+ kind: "primary",
41
+ },
42
+ }}
43
+ />
44
+ </span>
45
+ <span className={styles.padded}>{t("or", "or")}</span>
46
+ <span>
47
+ <Button onClick={() => navigate({ to: patientRegistrationUrl })}>
48
+ {t("createNewPatient", "Create new patient")} <Add size={20} />
49
+ </Button>
50
+ </span>
51
+ <span style={{ flexGrow: 1 }} />
52
+ <span>
53
+ <Link to="../">
54
+ <Button kind="ghost">
55
+ {t("cancel", "Cancel")} <Close size={20} />
56
+ </Button>
57
+ </Link>
58
+ </span>
59
+ </div>
60
+ );
61
+ };
62
+
63
+ export default PatientSearchHeader;
@@ -0,0 +1,3 @@
1
+ import PatientSearchHeader from "./PatientSearchHeader";
2
+
3
+ export default PatientSearchHeader;
@@ -0,0 +1,22 @@
1
+ @use '@carbon/styles/scss/spacing';
2
+ // @use '@carbon/colors';
3
+ // @use '@carbon/styles/scss/type';
4
+ @import '~@openmrs/esm-styleguide/src/vars';
5
+
6
+ .searchHeaderContainer {
7
+ height: spacing.$spacing-11;
8
+ display: flex;
9
+ align-items: center;
10
+ background-color: $ui-02;
11
+ border-top: 0.0125rem solid $ui-03;
12
+ border-bottom: 0.0125rem solid $ui-03;
13
+ padding: 0 spacing.$spacing-05;
14
+ }
15
+
16
+ .searchBarWrapper {
17
+ min-width: 35rem;
18
+ }
19
+
20
+ .padded {
21
+ padding: spacing.$spacing-05;
22
+ }
@@ -0,0 +1,65 @@
1
+ @use '@carbon/styles/scss/spacing';
2
+ @use '@carbon/colors';
3
+ @use '@carbon/styles/scss/type';
4
+ @import '~@openmrs/esm-styleguide/src/vars';
5
+
6
+
7
+ .breadcrumbsContainer > div > div > nav {
8
+ background-color: $ui-02;
9
+ padding: spacing.$spacing-04 spacing.$spacing-05;
10
+ height: spacing.$spacing-08;
11
+ }
12
+
13
+ .workspaceWrapper {
14
+ display: flex;
15
+ justify-content: center;
16
+ }
17
+
18
+ .workspace {
19
+ width: 1100px;
20
+ }
21
+
22
+ .selectPatientMessage {
23
+ @include type.type-style('productive-heading-03');
24
+ margin: spacing.$spacing-07;
25
+ text-align: center;
26
+ }
27
+
28
+ .formMainContent {
29
+ display: flex;
30
+ text-align: center;
31
+ margin-top: spacing.$spacing-05;
32
+ column-gap: spacing.$spacing-05;
33
+ }
34
+
35
+ .formContainer {
36
+ flex-grow: 1;
37
+ max-height: calc(100vh - 14rem);
38
+ overflow-y: scroll;
39
+ text-align: left;
40
+ }
41
+
42
+ .formContainer :global(.cds--form-item) :global(.question-area) {
43
+ max-width: 100%;
44
+ }
45
+
46
+ .rightPanel {
47
+ min-width: 13rem;
48
+ text-align: left;
49
+ overflow-y: scroll;
50
+ }
51
+
52
+ .patientCardsSection {
53
+ margin: spacing.$spacing-05 0;
54
+ border-bottom: 1px solid colors.$gray-10;
55
+ }
56
+
57
+ .rightPanelActionButtons {
58
+ display: flex;
59
+ flex-direction: column;
60
+ row-gap: spacing.$spacing-03;
61
+ & button {
62
+ width: 100%;
63
+ text-decoration: "none";
64
+ }
65
+ }
@@ -0,0 +1,35 @@
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
+
8
+ const WorkflowReview = () => {
9
+ const { patientUuids } = useContext(FormWorkflowContext);
10
+ const navigate = useNavigate();
11
+ return (
12
+ <div className={styles.workspaceWrapper}>
13
+ <div className={styles.workspace}>
14
+ <div className={styles.leftPanel}>
15
+ <h4>Review</h4>
16
+ <div className={styles.navButtons}>
17
+ <Button kind="primary" onClick={() => navigate("/")}>
18
+ Save & Close
19
+ </Button>
20
+ <Button kind="tertiary" onClick={() => navigate("/")}>
21
+ Cancel
22
+ </Button>
23
+ </div>
24
+ </div>
25
+ <div className={styles.formContainer}>
26
+ {patientUuids.map((patientUuid) => (
27
+ <FormReviewCard patientUuid={patientUuid} key={patientUuid} />
28
+ ))}
29
+ </div>
30
+ </div>
31
+ </div>
32
+ );
33
+ };
34
+
35
+ export default WorkflowReview;
@@ -0,0 +1,3 @@
1
+ import WorkflowReview from "./WorkflowReview";
2
+
3
+ export default WorkflowReview;
@@ -0,0 +1,34 @@
1
+ @import "~@openmrs/esm-styleguide/src/vars";
2
+ @import "~carbon-components/src/globals/scss/vars";
3
+ @import "~carbon-components/src/globals/scss/mixins";
4
+
5
+ .workspaceWrapper {
6
+ display: flex;
7
+ justify-content: center;
8
+ }
9
+
10
+ .workspace {
11
+ display: flex;
12
+ width: 800px;
13
+ margin-top: 1rem;
14
+ column-gap: 1rem;
15
+ }
16
+
17
+ .leftPanel {
18
+ width: 13rem;
19
+ overflow-y: scroll;
20
+ }
21
+
22
+ .navButtons {
23
+ margin-top: 1rem;
24
+ display: flex;
25
+ flex-direction: column;
26
+ row-gap: 0.5rem;
27
+ }
28
+
29
+ .formContainer {
30
+ flex-grow: 1;
31
+ max-height: calc(100vh - 14rem);
32
+ overflow-y: scroll;
33
+ row-gap: 1rem;
34
+ }
@@ -2,11 +2,12 @@ import React from "react";
2
2
  import { useTranslation } from "react-i18next";
3
3
  import { ConfigurableLink } from "@openmrs/esm-framework";
4
4
 
5
- export default function OfflineToolsAppMenuLink() {
5
+ export default function FormsAppMenuLink() {
6
6
  const { t } = useTranslation();
7
7
  return (
8
+ // eslint-disable-next-line
8
9
  <ConfigurableLink to="${openmrsSpaBase}/forms">
9
- {t("formsAppMenuLink", "Forms")}
10
+ {t("formsAppMenuLink", "Fast Data Entry")}
10
11
  </ConfigurableLink>
11
12
  );
12
13
  }