@thrustdevs/esm-procedure-orders-app 1.0.2-pre.6

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 (207) hide show
  1. package/.turbo/turbo-build.log +41 -0
  2. package/README.md +7 -0
  3. package/dist/122.js +1 -0
  4. package/dist/122.js.map +1 -0
  5. package/dist/144.js +2 -0
  6. package/dist/144.js.LICENSE.txt +19 -0
  7. package/dist/144.js.map +1 -0
  8. package/dist/182.js +1 -0
  9. package/dist/182.js.map +1 -0
  10. package/dist/205.js +1 -0
  11. package/dist/205.js.map +1 -0
  12. package/dist/216.js +2 -0
  13. package/dist/216.js.LICENSE.txt +9 -0
  14. package/dist/216.js.map +1 -0
  15. package/dist/290.js +2 -0
  16. package/dist/290.js.LICENSE.txt +5 -0
  17. package/dist/290.js.map +1 -0
  18. package/dist/300.js +1 -0
  19. package/dist/341.js +2 -0
  20. package/dist/341.js.LICENSE.txt +29 -0
  21. package/dist/341.js.map +1 -0
  22. package/dist/41.js +2 -0
  23. package/dist/41.js.LICENSE.txt +9 -0
  24. package/dist/41.js.map +1 -0
  25. package/dist/470.js +1 -0
  26. package/dist/470.js.map +1 -0
  27. package/dist/495.js +1 -0
  28. package/dist/495.js.map +1 -0
  29. package/dist/506.js +2 -0
  30. package/dist/506.js.LICENSE.txt +39 -0
  31. package/dist/506.js.map +1 -0
  32. package/dist/537.js +1 -0
  33. package/dist/537.js.map +1 -0
  34. package/dist/647.js +2 -0
  35. package/dist/647.js.LICENSE.txt +5 -0
  36. package/dist/647.js.map +1 -0
  37. package/dist/7.js +1 -0
  38. package/dist/7.js.map +1 -0
  39. package/dist/719.js +2 -0
  40. package/dist/719.js.LICENSE.txt +5 -0
  41. package/dist/719.js.map +1 -0
  42. package/dist/720.js +1 -0
  43. package/dist/720.js.map +1 -0
  44. package/dist/876.js +1 -0
  45. package/dist/876.js.map +1 -0
  46. package/dist/883.js +1 -0
  47. package/dist/883.js.map +1 -0
  48. package/dist/89.js +1 -0
  49. package/dist/89.js.map +1 -0
  50. package/dist/892.js +1 -0
  51. package/dist/892.js.map +1 -0
  52. package/dist/895.js +1 -0
  53. package/dist/895.js.map +1 -0
  54. package/dist/913.js +2 -0
  55. package/dist/913.js.LICENSE.txt +32 -0
  56. package/dist/913.js.map +1 -0
  57. package/dist/924.js +1 -0
  58. package/dist/924.js.map +1 -0
  59. package/dist/943.js +1 -0
  60. package/dist/943.js.map +1 -0
  61. package/dist/99.js +2 -0
  62. package/dist/99.js.LICENSE.txt +5 -0
  63. package/dist/99.js.map +1 -0
  64. package/dist/kenyaemr-esm-procedure-orders-app.js +1 -0
  65. package/dist/kenyaemr-esm-procedure-orders-app.js.buildmanifest.json +786 -0
  66. package/dist/kenyaemr-esm-procedure-orders-app.js.map +1 -0
  67. package/dist/main.js +2 -0
  68. package/dist/main.js.LICENSE.txt +35 -0
  69. package/dist/main.js.map +1 -0
  70. package/dist/routes.json +1 -0
  71. package/jest.config.js +8 -0
  72. package/package.json +55 -0
  73. package/src/completed-list/completed-list.component.tsx +40 -0
  74. package/src/completed-list/completed-list.resource.ts +0 -0
  75. package/src/completed-list/completed-list.scss +223 -0
  76. package/src/components/create-dashboard-link.component.tsx +35 -0
  77. package/src/components/overlay/hook.ts +47 -0
  78. package/src/components/overlay/overlay.component.tsx +42 -0
  79. package/src/components/overlay/overlay.scss +92 -0
  80. package/src/config-schema.ts +78 -0
  81. package/src/constants.ts +5 -0
  82. package/src/declarations.d.ts +6 -0
  83. package/src/empty-state/empty-state-component.tsx +21 -0
  84. package/src/empty-state/empty-state.scss +23 -0
  85. package/src/form/post-procedures/post-procedure-form.component.tsx +468 -0
  86. package/src/form/post-procedures/post-procedure-form.scss +189 -0
  87. package/src/form/post-procedures/post-procedure.resource.tsx +71 -0
  88. package/src/form/procedures-orders/add-procedures-order/add-procedures-order.scss +44 -0
  89. package/src/form/procedures-orders/add-procedures-order/add-procedures-order.workspace.tsx +93 -0
  90. package/src/form/procedures-orders/add-procedures-order/procedures-order-form.component.tsx +476 -0
  91. package/src/form/procedures-orders/add-procedures-order/procedures-order-form.scss +80 -0
  92. package/src/form/procedures-orders/add-procedures-order/procedures-order.ts +17 -0
  93. package/src/form/procedures-orders/add-procedures-order/procedures-type-search.scss +115 -0
  94. package/src/form/procedures-orders/add-procedures-order/procedures-type-search.tsx +236 -0
  95. package/src/form/procedures-orders/add-procedures-order/useProceduresTypes.ts +93 -0
  96. package/src/form/procedures-orders/api.ts +282 -0
  97. package/src/form/procedures-orders/order-config.ts +48 -0
  98. package/src/form/procedures-orders/procedures-order-basket-panel/procedures-icon.component.tsx +39 -0
  99. package/src/form/procedures-orders/procedures-order-basket-panel/procedures-order-basket-item-tile.component.tsx +100 -0
  100. package/src/form/procedures-orders/procedures-order-basket-panel/procedures-order-basket-item-tile.scss +72 -0
  101. package/src/form/procedures-orders/procedures-order-basket-panel/procedures-order-basket-panel.extension.tsx +190 -0
  102. package/src/form/procedures-orders/procedures-order-basket-panel/procedures-order-basket-panel.scss +74 -0
  103. package/src/form/procedures-orders/procedures-order-basket-panel/procedures-order-basket.scss +55 -0
  104. package/src/header/procedure-header.component.tsx +32 -0
  105. package/src/header/procedure-header.scss +70 -0
  106. package/src/header/procedure-illustration.component.tsx +52 -0
  107. package/src/hooks/useOrdersWorklist.ts +70 -0
  108. package/src/hooks/useSearchGroupedResults.ts +22 -0
  109. package/src/hooks/useSearchResults.ts +39 -0
  110. package/src/index.ts +59 -0
  111. package/src/left-panel-link.tsx +40 -0
  112. package/src/not-done-list/not-done-list.component.tsx +44 -0
  113. package/src/not-done-list/not-done.scss +207 -0
  114. package/src/patient-chart/patient-procedure-order-results-table.resource.ts +43 -0
  115. package/src/patient-chart/patient-procedure-order-results.component.tsx +12 -0
  116. package/src/patient-chart/patient-procedure-order-results.resource.ts +485 -0
  117. package/src/patient-chart/patient-procedure-results.component.tsx +30 -0
  118. package/src/patient-chart/procedure-active-order/procedure-active-order-results.component.tsx +390 -0
  119. package/src/patient-chart/procedure-active-order/procedure-active-order-results.scss +78 -0
  120. package/src/patient-chart/procedure-order-referals/procedure-order-referals.component.tsx +394 -0
  121. package/src/patient-chart/procedure-order-referals/procedure-order-referals.resource.tsx +0 -0
  122. package/src/patient-chart/procedure-order-referals/procedure-order-referals.scss +78 -0
  123. package/src/patient-chart/procedure-past-test/laboratory-past-test-order-results.component.tsx +366 -0
  124. package/src/patient-chart/procedure-past-test/laboratory-past-test-order-results.scss +74 -0
  125. package/src/patient-chart/procedure-tabs/laboratory-order-tabs.component.tsx +44 -0
  126. package/src/patient-chart/procedure-tabs/laboratory-order-tabs.scss +7 -0
  127. package/src/patient-chart/procedure-workspaces/laboratory-referral.workspace.component.tsx +11 -0
  128. package/src/patient-chart/procedure-workspaces/laboratory-referral.workspace.scss +0 -0
  129. package/src/patient-chart/results-summary/print-results-summary.component.tsx +152 -0
  130. package/src/patient-chart/results-summary/print-results-summary.scss +80 -0
  131. package/src/patient-chart/results-summary/print-results-table.component.tsx +134 -0
  132. package/src/patient-chart/results-summary/results-summary.resource.tsx +174 -0
  133. package/src/patient-chart/results-summary/results-summary.scss +158 -0
  134. package/src/patient-chart/results-summary/send-email-dialog.component.tsx +59 -0
  135. package/src/patient-chart/results-summary/test-children-results.component.tsx +177 -0
  136. package/src/patient-chart/results-summary/test-print-results-table.component.tsx +105 -0
  137. package/src/patient-chart/results-summary/test-results-table.component.tsx +103 -0
  138. package/src/print/print-procedure-results.component.tsx +49 -0
  139. package/src/print/print-procedure.component.tsx +105 -0
  140. package/src/print/print-procedure.scss +98 -0
  141. package/src/procedure-tabs/completed-tab.component.tsx +12 -0
  142. package/src/procedure-tabs/not-done-tab.component.tsx +12 -0
  143. package/src/procedure-tabs/referred-tab.component.tsx +12 -0
  144. package/src/procedure-tabs/work-list-tab.component.tsx +13 -0
  145. package/src/procedure.component.tsx +24 -0
  146. package/src/procedures-ordered/_pick-procedure-request-menu.component.tsx +33 -0
  147. package/src/procedures-ordered/pick-procedure-order/add-to-worklist-dialog.component.tsx +105 -0
  148. package/src/procedures-ordered/pick-procedure-order/add-to-worklist-dialog.resource.ts +106 -0
  149. package/src/procedures-ordered/pick-procedure-order/add-to-worklist-dialog.scss +38 -0
  150. package/src/procedures-ordered/pick-procedure-request-menu.component.tsx +32 -0
  151. package/src/procedures-ordered/procedure-dialogs/add-to-worklist-dialog.component.tsx +300 -0
  152. package/src/procedures-ordered/procedure-dialogs/add-to-worklist-dialog.resource.ts +153 -0
  153. package/src/procedures-ordered/procedure-dialogs/add-to-worklist-dialog.scss +38 -0
  154. package/src/procedures-ordered/procedure-instructions/instructions.scss +24 -0
  155. package/src/procedures-ordered/procedure-instructions/procedure-instructions-menu.component.tsx +32 -0
  156. package/src/procedures-ordered/procedure-instructions/procedure-instructions.component.tsx +78 -0
  157. package/src/procedures-ordered/procedure-instructions/procedure-instructions.scss +24 -0
  158. package/src/procedures-ordered/procedure-queue.scss +211 -0
  159. package/src/procedures-ordered/procedure-tabs.component.tsx +104 -0
  160. package/src/procedures-ordered/procedure-tests/procedure-tests.component.tsx +83 -0
  161. package/src/procedures-ordered/procedure-tests/procedure-tests.resource.ts +14 -0
  162. package/src/procedures-ordered/procedure-tests/procedure-tests.scss +12 -0
  163. package/src/procedures-ordered/procedures-ordered-list.component.tsx +38 -0
  164. package/src/procedures-ordered/reject-order-dialog/reject-order-dialog.scss +14 -0
  165. package/src/procedures-ordered/reject-order-dialog/reject-procedure-order-dialog.component.tsx +98 -0
  166. package/src/procedures-ordered/reject-reason/procedure-reject-reason-menu.component.tsx +32 -0
  167. package/src/procedures-ordered/reject-reason/procedure-reject-reason.component.tsx +40 -0
  168. package/src/procedures-ordered/transition-patient-new-queue/transition-latest-queue-entry-button.component.tsx +42 -0
  169. package/src/procedures-ordered/transition-patient-new-queue/transition-latest-queue-entry-button.scss +14 -0
  170. package/src/procedures-ordered/transition-patient-new-queue/transition-latest-queue-entry-button.test.tsx +67 -0
  171. package/src/referred-procedures/referred-procedures.component.tsx +37 -0
  172. package/src/results/result-form-field.component.tsx +141 -0
  173. package/src/results/result-form.component.tsx +120 -0
  174. package/src/results/result-form.resource.ts +361 -0
  175. package/src/results/result-form.scss +22 -0
  176. package/src/root.component.tsx +16 -0
  177. package/src/routes.json +152 -0
  178. package/src/setup-tests.ts +7 -0
  179. package/src/shared/ui/common/action-button/action-button.component.tsx +68 -0
  180. package/src/shared/ui/common/action-button/action-button.scss +12 -0
  181. package/src/shared/ui/common/action-button/order-action-extension.component.tsx +21 -0
  182. package/src/shared/ui/common/grouped-orders-table.component.tsx +176 -0
  183. package/src/shared/ui/common/grouped-orders-table.scss +30 -0
  184. package/src/shared/ui/common/grouped-procedure-types.ts +47 -0
  185. package/src/shared/ui/common/list-order-details.component.tsx +171 -0
  186. package/src/shared/ui/common/list-order-details.resource.ts +41 -0
  187. package/src/shared/ui/common/list-order-details.scss +118 -0
  188. package/src/shared/ui/common/orders-date-range-picker.scss +15 -0
  189. package/src/shared/ui/common/orders-date-range-picker.tsx +38 -0
  190. package/src/summary-tiles/procedure-summary-tiles.component.tsx +36 -0
  191. package/src/summary-tiles/procedure-summary-tiles.scss +11 -0
  192. package/src/summary-tiles/procedure-summary.resource.tsx +79 -0
  193. package/src/summary-tiles/summary-tile.component.tsx +41 -0
  194. package/src/summary-tiles/summary-tile.scss +53 -0
  195. package/src/types/index.ts +661 -0
  196. package/src/types/patient-queue.ts +77 -0
  197. package/src/ui-components/overflow-menu.component.tsx +74 -0
  198. package/src/ui-components/overflow-menu.scss +39 -0
  199. package/src/utils/functions.ts +236 -0
  200. package/src/utils/orders-table/orders-data-table.component.tsx +129 -0
  201. package/src/utils/orders-table/orders-data-table.scss +50 -0
  202. package/src/work-list/work-list.component.tsx +38 -0
  203. package/src/work-list/work-list.resource.ts +26 -0
  204. package/src/work-list/work-list.scss +207 -0
  205. package/translations/en.json +141 -0
  206. package/tsconfig.json +5 -0
  207. package/webpack.config.js +1 -0
@@ -0,0 +1,468 @@
1
+ import React, { useCallback, useEffect, useState } from 'react';
2
+ import {
3
+ type DefaultWorkspaceProps,
4
+ OpenmrsDatePicker,
5
+ showSnackbar,
6
+ useConfig,
7
+ useDebounce,
8
+ useSession,
9
+ } from '@openmrs/esm-framework';
10
+ import {
11
+ Form,
12
+ Stack,
13
+ ComboBox,
14
+ TextArea,
15
+ Layer,
16
+ FormLabel,
17
+ ButtonSet,
18
+ Button,
19
+ Search,
20
+ InlineLoading,
21
+ Tile,
22
+ Tag,
23
+ DatePicker,
24
+ DatePickerInput,
25
+ } from '@carbon/react';
26
+ import { useTranslation } from 'react-i18next';
27
+ import styles from './post-procedure-form.scss';
28
+ import { Controller, useForm } from 'react-hook-form';
29
+ import { z } from 'zod';
30
+ import { zodResolver } from '@hookform/resolvers/zod';
31
+ import { savePostProcedure, useConditionsSearch, useProvidersSearch } from './post-procedure.resource';
32
+ import { type CodedProvider, type CodedCondition, ProcedurePayload, type Result } from '../../types';
33
+ import dayjs from 'dayjs';
34
+ import { closeOverlay } from '../../components/overlay/hook';
35
+ import { type ConfigObject, StringPath } from '../../config-schema';
36
+ import { updateOrder } from '../../procedures-ordered/pick-procedure-order/add-to-worklist-dialog.resource';
37
+ import { mutate } from 'swr';
38
+
39
+ const validationSchema = z.object({
40
+ startDatetime: z.date({
41
+ required_error: 'Start datetime is required',
42
+ }),
43
+ endDatetime: z.date({ required_error: 'End datetime is required', invalid_type_error: 'Please select a valid date' }),
44
+ outcome: z.string({ required_error: 'Outcome is required' }),
45
+ procedureReport: z.string({ required_error: 'Procedure report is required' }),
46
+ participants: z.string().optional(),
47
+ complications: z.string().optional(),
48
+ });
49
+
50
+ type PostProcedureFormSchema = z.infer<typeof validationSchema>;
51
+
52
+ type PostProcedureFormProps = DefaultWorkspaceProps & {
53
+ patientUuid: string;
54
+ order: Result;
55
+ };
56
+
57
+ const PostProcedureForm: React.FC<PostProcedureFormProps> = ({
58
+ patientUuid,
59
+ order,
60
+ closeWorkspace,
61
+ closeWorkspaceWithSavedChanges,
62
+ promptBeforeClosing,
63
+ }) => {
64
+ const { sessionLocation } = useSession();
65
+ const { t } = useTranslation();
66
+
67
+ const [providerSearchTerm, setProviderSearchTerm] = useState('');
68
+ const debouncedProviderSearchTerm = useDebounce(providerSearchTerm);
69
+ const { providerSearchResults, isProviderSearching } = useProvidersSearch(debouncedProviderSearchTerm);
70
+ const [selectedProvider, setSelectedProvider] = useState<CodedProvider>(null);
71
+ const handleProviderSearchTermChange = (event: React.ChangeEvent<HTMLInputElement>) =>
72
+ setProviderSearchTerm(event.target.value);
73
+
74
+ const [searchTerm, setSearchTerm] = useState('');
75
+ const debouncedSearchTerm = useDebounce(searchTerm);
76
+ const { searchResults, isSearching } = useConditionsSearch(debouncedSearchTerm);
77
+ const [selectedCondition, setSelectedCondition] = useState<CodedCondition>(null);
78
+
79
+ const handleParticipantSearchInputChange = (event) => {
80
+ const value = event.target.value;
81
+ setProviderSearchTerm(value);
82
+ if (!value) {
83
+ setSelectedProvider(null);
84
+ setShowParticipants(false);
85
+ } else {
86
+ setShowParticipants(true);
87
+ }
88
+ };
89
+
90
+ const handleSearchInputChange = (event) => {
91
+ const value = event.target.value;
92
+ setSearchTerm(value);
93
+ if (!value) {
94
+ setSelectedCondition(null);
95
+ setShowItems(false);
96
+ } else {
97
+ setShowItems(true);
98
+ }
99
+ };
100
+
101
+ const [showParticipants, setShowParticipants] = useState(false);
102
+ const [selectedParticipants, setSelectedParticipants] = useState([]);
103
+
104
+ const [showItems, setShowItems] = useState(false);
105
+ const [selectedItems, setSelectedItems] = useState([]);
106
+
107
+ const handleParticipantSelect = useCallback((item: CodedProvider) => {
108
+ setSelectedProvider(item);
109
+ }, []);
110
+
111
+ const handleSelect = useCallback((item: CodedCondition) => {
112
+ setSelectedCondition(item);
113
+ }, []);
114
+
115
+ const {
116
+ procedureComplicationGroupingConceptUuid,
117
+ procedureComplicationConceptUuid,
118
+ procedureResultEncounterType,
119
+ procedureResultEncounterRole,
120
+ } = useConfig<ConfigObject>();
121
+
122
+ const {
123
+ control,
124
+ formState: { errors, isSubmitting, isDirty },
125
+ handleSubmit,
126
+ } = useForm<PostProcedureFormSchema>({
127
+ defaultValues: {},
128
+ resolver: zodResolver(validationSchema),
129
+ });
130
+
131
+ const handleProviderChange = useCallback((selectedProvider: CodedProvider) => {
132
+ setSelectedProvider(selectedProvider);
133
+ }, []);
134
+
135
+ useEffect(() => {
136
+ if (promptBeforeClosing && isDirty) {
137
+ promptBeforeClosing(() => isDirty);
138
+ }
139
+ }, [promptBeforeClosing, isDirty, closeWorkspace]);
140
+
141
+ const onSubmit = async (data: PostProcedureFormSchema) => {
142
+ if (!data.startDatetime || !data.endDatetime) {
143
+ // Handle the error case when dates are invalid or missing
144
+ showSnackbar({
145
+ title: t('error', 'Error'),
146
+ subtitle: t('invalidDates', 'Invalid or missing dates'),
147
+ timeoutInMs: 5000,
148
+ isLowContrast: true,
149
+ kind: 'error',
150
+ });
151
+ return;
152
+ }
153
+ const participants = [];
154
+ selectedParticipants.forEach((p) => {
155
+ const provider = {
156
+ provider: p.uuid,
157
+ encounterRole: procedureResultEncounterRole,
158
+ };
159
+ participants.push(provider);
160
+ });
161
+ const complications = [];
162
+ selectedItems.forEach((p) => {
163
+ const complication = {
164
+ groupMembers: [
165
+ {
166
+ concept: procedureComplicationConceptUuid,
167
+ valueCoded: p.concept.uuid,
168
+ },
169
+ ],
170
+ concept: procedureComplicationGroupingConceptUuid,
171
+ };
172
+ complications.push(complication);
173
+ });
174
+
175
+ const reportPayload = {
176
+ patient: patientUuid,
177
+ procedureOrder: order?.uuid,
178
+ concept: order?.concept?.uuid,
179
+ procedureReason: order?.orderReason?.uuid,
180
+ category: order?.orderType?.uuid,
181
+ status: 'COMPLETED',
182
+ outcome: data.outcome,
183
+ location: sessionLocation?.uuid,
184
+ startDatetime: dayjs(data.startDatetime).format('YYYY-MM-DDTHH:mm:ss'),
185
+ endDatetime: dayjs(data.endDatetime).format('YYYY-MM-DDTHH:mm:ss'),
186
+ procedureReport: data.procedureReport,
187
+ encounters: [
188
+ {
189
+ encounterDatetime: new Date(),
190
+ patient: patientUuid,
191
+ encounterType: procedureResultEncounterType,
192
+ encounterProviders: participants,
193
+ obs: complications,
194
+ },
195
+ ],
196
+ };
197
+ try {
198
+ const response = await savePostProcedure(reportPayload);
199
+ if (response.ok) {
200
+ showSnackbar({
201
+ title: t('procedureSaved', 'Procedure saved'),
202
+ subtitle: t('procedureSavedSuccessfully', 'Procedure saved successfully'),
203
+ timeoutInMs: 5000,
204
+ isLowContrast: true,
205
+ kind: 'success',
206
+ });
207
+ mutate((key) => typeof key === 'string' && key.startsWith('/ws/rest/v1/order'), undefined, {
208
+ revalidate: true,
209
+ });
210
+ closeWorkspaceWithSavedChanges();
211
+ }
212
+ } catch (error) {
213
+ console.error(error);
214
+ showSnackbar({
215
+ title: t('error', 'Error'),
216
+ subtitle: t('errorSavingProcedure', 'Error saving procedure'),
217
+ timeoutInMs: 5000,
218
+ isLowContrast: true,
219
+ kind: 'error',
220
+ });
221
+ closeOverlay();
222
+ }
223
+ };
224
+
225
+ const onError = (error: any) => {
226
+ console.error(error);
227
+ };
228
+
229
+ return (
230
+ <Form onSubmit={handleSubmit(onSubmit, onError)} className={styles.formContainer}>
231
+ <Stack gap={4}>
232
+ <Layer>
233
+ <FormLabel className={styles.formLabel}>{t('date', 'Date')}</FormLabel>
234
+ <Controller
235
+ control={control}
236
+ name="startDatetime"
237
+ render={({ field, fieldState }) => (
238
+ <DatePicker
239
+ datePickerType="single"
240
+ className={styles.formDatePicker}
241
+ onChange={(event) => {
242
+ field.onChange(event[0]);
243
+ }}
244
+ value={field.value}>
245
+ <DatePickerInput
246
+ placeholder="mm/dd/yyyy"
247
+ labelText={t('startDatetime', 'Start Datetime')}
248
+ id="startDatetime"
249
+ size="md"
250
+ invalid={!!errors.startDatetime}
251
+ invalidText={errors.startDatetime?.message}
252
+ />
253
+ </DatePicker>
254
+ )}
255
+ />
256
+ </Layer>
257
+ <Layer>
258
+ <Controller
259
+ control={control}
260
+ name="endDatetime"
261
+ render={({ field, fieldState }) => (
262
+ <DatePicker
263
+ datePickerType="single"
264
+ className={styles.formDatePicker}
265
+ onChange={(event) => {
266
+ field.onChange(event[0]);
267
+ }}
268
+ value={field.value}>
269
+ <DatePickerInput
270
+ placeholder="mm/dd/yyyy"
271
+ labelText={t('endDatetime', 'End Datetime')}
272
+ id="endDatetime"
273
+ size="md"
274
+ invalid={!!errors.endDatetime}
275
+ invalidText={errors.endDatetime?.message}
276
+ />
277
+ </DatePicker>
278
+ )}
279
+ />
280
+ </Layer>
281
+
282
+ <Layer>
283
+ <FormLabel className={styles.formLabel}>{t('procedureOutcome', 'Procedure outcome')}</FormLabel>
284
+ <Controller
285
+ control={control}
286
+ name="outcome"
287
+ render={({ field: { onChange } }) => (
288
+ <ComboBox
289
+ onChange={({ selectedItem }) => onChange(selectedItem.id)}
290
+ id="outcome"
291
+ items={[
292
+ { id: 'SUCCESSFUL', text: t('successful', 'Successful') },
293
+ {
294
+ id: 'PARTIALLY_SUCCESSFUL',
295
+ text: t('partiallySuccessful', 'Partially success'),
296
+ },
297
+ {
298
+ id: 'NOT_SUCCESSFUL',
299
+ text: t('notSuccessfully', 'Not successful'),
300
+ },
301
+ ]}
302
+ itemToString={(item) => (item ? item.text : '')}
303
+ placeholder={t('selectOutcome', 'Select outcome')}
304
+ invalid={!!errors.outcome}
305
+ invalidText={errors.outcome?.message}
306
+ />
307
+ )}
308
+ />
309
+ </Layer>
310
+ <Layer>
311
+ <FormLabel className={styles.formLabel}>{t('procedureReport', 'Procedure report')}</FormLabel>
312
+ <Controller
313
+ control={control}
314
+ name="procedureReport"
315
+ render={({ field: { onChange } }) => (
316
+ <TextArea
317
+ id="procedureReport"
318
+ rows={4}
319
+ onChange={onChange}
320
+ placeholder={t('procedureReportPlaceholder', 'Enter procedure report')}
321
+ invalid={!!errors.procedureReport}
322
+ invalidText={errors.procedureReport?.message}
323
+ />
324
+ )}
325
+ />
326
+ </Layer>
327
+ <Layer>
328
+ <FormLabel className={styles.formLabel}>{t('participants', 'Participants')}</FormLabel>
329
+ <div>
330
+ {selectedParticipants?.map((item) => (
331
+ <>
332
+ <Tag style={{ display: 'inline-flex', alignItems: 'center' }}>
333
+ <span style={{ marginRight: '8px' }}>{item.display}</span>
334
+ <svg
335
+ focusable="false"
336
+ fill="currentColor"
337
+ width="16"
338
+ height="16"
339
+ viewBox="0 0 32 32"
340
+ aria-hidden="true"
341
+ onClick={() => setSelectedParticipants((prevItems) => prevItems.filter((i) => i !== item))}>
342
+ <path d={StringPath}></path>
343
+ </svg>
344
+ </Tag>
345
+ </>
346
+ ))}
347
+ </div>
348
+ <div>
349
+ <Search
350
+ autoFocus
351
+ size="md"
352
+ id="participantsSearch"
353
+ placeholder={t('participants', 'Participants')}
354
+ labelText={t('enterParticipant', 'Enter Participant')}
355
+ onChange={handleParticipantSearchInputChange}
356
+ onClear={() => setProviderSearchTerm('')}
357
+ />
358
+ {providerSearchResults?.length === 0 ? (
359
+ <div className={styles.filterEmptyState}>
360
+ <Layer level={0}>
361
+ <Tile className={styles.filterEmptyStateTile}>
362
+ <span className={styles.filterEmptyStateContent}>
363
+ <strong>{debouncedProviderSearchTerm}</strong>
364
+ </span>
365
+ </Tile>
366
+ </Layer>
367
+ </div>
368
+ ) : (
369
+ showParticipants && (
370
+ <ul className={styles.participantsList}>
371
+ {providerSearchResults.map((item) => (
372
+ <li
373
+ key={item?.uuid}
374
+ role="menuitem"
375
+ tabIndex={0}
376
+ className={styles.participantService}
377
+ onClick={() => {
378
+ handleParticipantSelect(item);
379
+ setSelectedParticipants((prevItems) => [...prevItems, item]);
380
+ setShowParticipants(false);
381
+ }}>
382
+ {item.display}
383
+ </li>
384
+ ))}
385
+ </ul>
386
+ )
387
+ )}
388
+ {isProviderSearching && <InlineLoading description="Loading participants..." />}
389
+ </div>
390
+ </Layer>
391
+ <Layer>
392
+ <FormLabel className={styles.formLabel}>{t('complications', 'Complications')}</FormLabel>
393
+ <div>
394
+ {selectedItems?.map((item) => (
395
+ <>
396
+ <Tag style={{ display: 'inline-flex', alignItems: 'center' }}>
397
+ <span style={{ marginRight: '8px' }}>{item.display}</span>
398
+ <svg
399
+ focusable="false"
400
+ fill="currentColor"
401
+ width="16"
402
+ height="16"
403
+ viewBox="0 0 32 32"
404
+ aria-hidden="true"
405
+ onClick={() => setSelectedItems((prevItems) => prevItems.filter((i) => i !== item))}>
406
+ <path d={StringPath}></path>
407
+ </svg>
408
+ </Tag>
409
+ </>
410
+ ))}
411
+ </div>
412
+ <div>
413
+ <Search
414
+ autoFocus
415
+ size="md"
416
+ id="conditionsSearch"
417
+ placeholder={t('complications', 'complications')}
418
+ labelText={t('enterCondition', 'Enter condition')}
419
+ onChange={handleSearchInputChange}
420
+ onClear={() => setSearchTerm('')}
421
+ />
422
+ {searchResults?.length === 0 ? (
423
+ <div className={styles.filterEmptyState}>
424
+ <Layer level={0}>
425
+ <Tile className={styles.filterEmptyStateTile}>
426
+ <span className={styles.filterEmptyStateContent}>
427
+ <strong>{debouncedSearchTerm}</strong>
428
+ </span>
429
+ </Tile>
430
+ </Layer>
431
+ </div>
432
+ ) : (
433
+ showItems && (
434
+ <ul className={styles.complicationsList}>
435
+ {searchResults.map((item) => (
436
+ <li
437
+ key={item?.concept?.uuid}
438
+ role="menuitem"
439
+ tabIndex={0}
440
+ className={styles.complicationService}
441
+ onClick={() => {
442
+ handleSelect(item);
443
+ setSelectedItems((prevItems) => [...prevItems, item]);
444
+ setShowItems(false);
445
+ }}>
446
+ {item.display}
447
+ </li>
448
+ ))}
449
+ </ul>
450
+ )
451
+ )}
452
+ {isSearching && <InlineLoading description="Loading complications..." />}
453
+ </div>
454
+ </Layer>
455
+ </Stack>
456
+ <ButtonSet className={styles.buttonSetContainer}>
457
+ <Button onClick={closeWorkspace} size="md" kind="secondary">
458
+ {t('discard', 'Discard')}
459
+ </Button>
460
+ <Button type="submit" size="md" kind="primary">
461
+ {t('saveAndClose', 'Save & Close')}
462
+ </Button>
463
+ </ButtonSet>
464
+ </Form>
465
+ );
466
+ };
467
+
468
+ export default PostProcedureForm;
@@ -0,0 +1,189 @@
1
+ @use '@carbon/styles/scss/type';
2
+ @use '@carbon/layout';
3
+ @use '@carbon/colors';
4
+ @use '@openmrs/esm-styleguide/src/vars' as vars;
5
+
6
+ .formLabel {
7
+ color: colors.$cool-gray-90;
8
+ @include type.type-style('heading-compact-01');
9
+ margin-bottom: layout.$spacing-03;
10
+ margin-top: layout.$spacing-01;
11
+ }
12
+
13
+ /* Desktop */
14
+ :global(.omrs-breakpoint-gt-tablet) {
15
+ .formContainer {
16
+ margin: layout.$spacing-03 layout.$spacing-05;
17
+ height: calc(100vh - 7rem);
18
+ display: flex;
19
+ flex-direction: column;
20
+ justify-content: space-between;
21
+ }
22
+ }
23
+
24
+ /* Tablet */
25
+ :global(.omrs-breakpoint-lt-desktop) {
26
+ .formContainer {
27
+ margin: layout.$spacing-05 layout.$spacing-05;
28
+ height: calc(100vh - 4rem);
29
+ display: flex;
30
+ flex-direction: column;
31
+ justify-content: space-between;
32
+ }
33
+ }
34
+
35
+ .buttonSetContainer {
36
+ width: 100%;
37
+ margin-top: auto;
38
+
39
+ & > button {
40
+ max-width: 50% !important;
41
+ height: 4rem;
42
+ }
43
+ }
44
+
45
+ .condition {
46
+ padding: 0.75rem;
47
+ border-top: 0.0625rem solid vars.$grey-2;
48
+
49
+ &:first-of-type {
50
+ border-top: none;
51
+ }
52
+
53
+ &:last-of-type {
54
+ border-bottom: none;
55
+ }
56
+ }
57
+
58
+ .conditionsList {
59
+ background-color: vars.$ui-02;
60
+ max-height: 14rem;
61
+ overflow-y: auto;
62
+ border: 1px solid vars.$ui-03;
63
+
64
+ li:hover {
65
+ background-color: vars.$ui-03;
66
+ }
67
+ }
68
+
69
+ .emptyResults {
70
+ @include type.type-style('body-compact-01');
71
+ color: vars.$text-02;
72
+ min-height: 1rem;
73
+ border: 1px solid vars.$ui-03;
74
+ }
75
+
76
+ .form {
77
+ display: flex;
78
+ flex-direction: column;
79
+ justify-content: space-between;
80
+ height: calc(100vh - 6rem);
81
+ }
82
+
83
+ .formContainer {
84
+ margin: 1rem;
85
+ position: relative;
86
+ z-index: 100;
87
+ }
88
+
89
+ .button {
90
+ height: 4rem;
91
+ display: flex;
92
+ align-content: flex-start;
93
+ align-items: baseline;
94
+ min-width: 50%;
95
+ }
96
+
97
+ .tablet {
98
+ padding: 1.5rem 1rem;
99
+ background-color: vars.$ui-02;
100
+ }
101
+
102
+ .desktop {
103
+ padding: 0rem;
104
+ }
105
+
106
+ .loader {
107
+ padding: 1rem 0.5rem;
108
+ }
109
+
110
+ .conditionLabel {
111
+ @include type.type-style('label-02');
112
+ }
113
+
114
+ .errorContainer {
115
+ margin: 1rem;
116
+ }
117
+
118
+ .conditionsError {
119
+ :global(.cds--search-input):focus {
120
+ outline: 2px solid vars.$danger;
121
+ }
122
+
123
+ :global(.cds--search-magnifier) {
124
+ svg {
125
+ fill: vars.$danger;
126
+ }
127
+ }
128
+ }
129
+
130
+ .errorMessage {
131
+ @include type.type-style('label-02');
132
+ color: vars.$danger;
133
+ margin-top: 0.5rem;
134
+ }
135
+
136
+ .radioGroup {
137
+ :global(.cds--radio-button-wrapper) {
138
+ margin-block-end: 0.375rem !important;
139
+ }
140
+
141
+ :global(.cds--radio-button__validation-msg) {
142
+ display: none;
143
+ }
144
+ }
145
+
146
+ .spinner {
147
+ &:global(.cds--inline-loading) {
148
+ min-height: 1rem;
149
+ }
150
+ }
151
+
152
+ .required {
153
+ color: colors.$red-60;
154
+ margin-left: 0.125rem;
155
+ }
156
+
157
+ .complicationService {
158
+ padding: 1rem 0.75rem;
159
+ }
160
+
161
+ .complicationsList {
162
+ background-color: vars.$ui-02;
163
+ max-height: 14rem;
164
+ overflow-y: auto;
165
+ border: 1px solid vars.$ui-03;
166
+
167
+ li:hover {
168
+ background-color: vars.$ui-03;
169
+ }
170
+ }
171
+
172
+ .participantService {
173
+ padding: 1rem 0.75rem;
174
+ }
175
+
176
+ .participantsList {
177
+ background-color: vars.$ui-02;
178
+ max-height: 14rem;
179
+ overflow-y: auto;
180
+ border: 1px solid vars.$ui-03;
181
+
182
+ li:hover {
183
+ background-color: vars.$ui-03;
184
+ }
185
+ }
186
+
187
+ .formDatePicker input {
188
+ min-width: 23rem;
189
+ }