@ampath/esm-dha-workflow-app 4.0.0-next.13 → 4.0.0-next.15

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 (80) hide show
  1. package/dist/104.js +2 -0
  2. package/dist/104.js.LICENSE.txt +9 -0
  3. package/dist/104.js.map +1 -0
  4. package/dist/306.js +1 -0
  5. package/dist/306.js.map +1 -0
  6. package/dist/388.js +2 -0
  7. package/dist/388.js.map +1 -0
  8. package/dist/460.js +1 -0
  9. package/dist/460.js.map +1 -0
  10. package/dist/560.js +1 -0
  11. package/dist/560.js.map +1 -0
  12. package/dist/635.js +1 -0
  13. package/dist/635.js.map +1 -0
  14. package/dist/695.js +1 -0
  15. package/dist/695.js.map +1 -0
  16. package/dist/709.js +1 -0
  17. package/dist/709.js.map +1 -0
  18. package/dist/710.js +2 -0
  19. package/dist/{198.js.LICENSE.txt → 710.js.LICENSE.txt} +0 -10
  20. package/dist/710.js.map +1 -0
  21. package/dist/91.js +1 -1
  22. package/dist/91.js.map +1 -1
  23. package/dist/93.js +1 -1
  24. package/dist/978.js +1 -0
  25. package/dist/978.js.map +1 -0
  26. package/dist/esm-dha-workflow-app.js +1 -0
  27. package/dist/{openmrs-esm-home-app.js.buildmanifest.json → esm-dha-workflow-app.js.buildmanifest.json} +266 -97
  28. package/dist/esm-dha-workflow-app.js.map +1 -0
  29. package/dist/main.js +1 -1
  30. package/dist/main.js.map +1 -1
  31. package/dist/routes.json +1 -1
  32. package/package.json +2 -2
  33. package/src/config-schema.ts +115 -33
  34. package/src/consultation/action-button.component.tsx +34 -0
  35. package/src/consultation/action-overflow-menu-item.component.tsx +34 -0
  36. package/src/consultation/consultation-room.component.tsx +60 -0
  37. package/src/consultation/consultation.scss +5 -0
  38. package/src/consultation/consultation.tsx +9 -2
  39. package/src/hooks/useActions.ts +148 -0
  40. package/src/hooks/useQueueEntries.ts +35 -0
  41. package/src/index.ts +11 -1
  42. package/src/left-panel/left-panel.component.tsx +0 -2
  43. package/src/metrics/metrics-cards/attended-patients.extension.tsx +18 -0
  44. package/src/metrics/metrics-cards/metrics-card.component.tsx +86 -0
  45. package/src/metrics/metrics-cards/metrics-card.scss +106 -0
  46. package/src/metrics/metrics-cards/waiting-patients.extension.tsx +18 -0
  47. package/src/metrics/metrics-container.component.tsx +16 -0
  48. package/src/metrics/metrics-container.scss +36 -0
  49. package/src/metrics/metrics.resource.ts +101 -0
  50. package/src/modals/sign-off-modal.scss +7 -0
  51. package/src/modals/sign-off-modal.tsx +52 -0
  52. package/src/registry/mock-client.ts +627 -0
  53. package/src/registry/modal/otp-verification-modal/otp-verification-modal.tsx +13 -11
  54. package/src/registry/modal/send-to-triage/send-to-triage.modal.scss +7 -0
  55. package/src/registry/modal/send-to-triage/send-to-triage.modal.tsx +97 -72
  56. package/src/registry/registry.component.scss +12 -1
  57. package/src/registry/registry.component.tsx +168 -104
  58. package/src/registry/types/index.ts +59 -1
  59. package/src/registry/utils/error-handler.ts +33 -0
  60. package/src/registry/utils/format-dependant-display-data.ts +8 -0
  61. package/src/registry/utils/hie-client-adapter.ts +309 -0
  62. package/src/resources/hie-amrs-automatic-registration.service.ts +16 -0
  63. package/src/resources/identifier-types.ts +27 -0
  64. package/src/resources/patient-resource.ts +60 -0
  65. package/src/routes.json +18 -0
  66. package/src/service-queues/service-queues.resource.ts +62 -0
  67. package/src/shared/constants/civil-status.ts +29 -0
  68. package/src/shared/constants/person-attributes.ts +33 -0
  69. package/src/types/types.ts +100 -0
  70. package/dist/16.js +0 -1
  71. package/dist/16.js.map +0 -1
  72. package/dist/198.js +0 -2
  73. package/dist/198.js.map +0 -1
  74. package/dist/200.js +0 -2
  75. package/dist/200.js.map +0 -1
  76. package/dist/860.js +0 -1
  77. package/dist/860.js.map +0 -1
  78. package/dist/openmrs-esm-home-app.js +0 -1
  79. package/dist/openmrs-esm-home-app.js.map +0 -1
  80. /package/dist/{200.js.LICENSE.txt → 388.js.LICENSE.txt} +0 -0
@@ -4,7 +4,6 @@ import {
4
4
  InlineLoading,
5
5
  RadioButton,
6
6
  RadioButtonGroup,
7
- SideNav,
8
7
  Table,
9
8
  TableBody,
10
9
  TableCell,
@@ -24,14 +23,18 @@ import ClientDetailsModal from './modal/client-details-modal/client-details-moda
24
23
  import { searchPatientByCrNumber } from '../resources/patient-search.resource';
25
24
  import SendToTriageModal from './modal/send-to-triage/send-to-triage.modal';
26
25
  import { useNavigate } from 'react-router-dom';
26
+ import { formatDependantDisplayData } from './utils/format-dependant-display-data';
27
+ import { registerHieClientInAmrs } from '../resources/hie-amrs-automatic-registration.service';
28
+ import { getErrorMessages } from './utils/error-handler';
27
29
 
28
30
  interface RegistryComponentProps {}
29
31
  const RegistryComponent: React.FC<RegistryComponentProps> = () => {
30
32
  const [identifierType, setIdentifierType] = useState<IdentifierType>('National ID');
31
33
  const [identifierValue, setIdentifierValue] = useState('');
32
34
  const [loading, setLoading] = useState<boolean>(false);
33
- const [client, setClient] = useState<HieClient>();
34
- const [amrsPatients, setAmrsPatient] = useState<Patient[]>();
35
+ const [principal, setPrincipal] = useState<HieClient>();
36
+ const [selectedDependant, setSelectedDependant] = useState<HieClient>();
37
+ const [amrsPatients, setAmrsPatients] = useState<Patient[]>([]);
35
38
  const [selectedPatient, setSelectedPatient] = useState<string>('principal');
36
39
  const [displayOtpModal, setDisplayOtpModal] = useState<boolean>(false);
37
40
  const [displayClientDetailsModal, setDisplayClientDetailsModal] = useState<boolean>(false);
@@ -60,7 +63,7 @@ const RegistryComponent: React.FC<RegistryComponentProps> = () => {
60
63
  if (patients.length === 0) throw new Error('No matching patient found in Client Registry.');
61
64
 
62
65
  const patient = patients[0];
63
- setClient(patient);
66
+ setPrincipal(patient);
64
67
  showAlert('success', 'Client Data Loaded', 'Patient fetched successfully');
65
68
  } catch (err: any) {
66
69
  const errorMessage = err.message || 'Failed to fetch client data';
@@ -99,7 +102,6 @@ const RegistryComponent: React.FC<RegistryComponentProps> = () => {
99
102
  };
100
103
  const handleModelClose = () => {
101
104
  setDisplayOtpModal(false);
102
- setDisplayClientDetailsModal(true);
103
105
  };
104
106
  const onClientDetailsModalClose = () => {
105
107
  setDisplayClientDetailsModal(false);
@@ -110,11 +112,24 @@ const RegistryComponent: React.FC<RegistryComponentProps> = () => {
110
112
  const handleEmergencyRegistration = () => {
111
113
  window.location.href = `${window.spaBase}/patient-registration`;
112
114
  };
115
+ const handleManualRegistration = () => {
116
+ setDisplaytriageModal(false);
117
+ handleEmergencyRegistration();
118
+ };
113
119
  const handleSendClientToTriage = async (crId: string) => {
114
120
  onClientDetailsModalClose();
115
121
  const resp = await searchPatientByCrNumber(crId);
116
122
  if (resp.totalCount > 0) {
117
- setAmrsPatient(resp.results);
123
+ showAlert(
124
+ 'success',
125
+ `${resp.totalCount} ${resp.totalCount > 0 ? 'Patients' : 'Patient'} found in the system with ${crId}`,
126
+ '',
127
+ );
128
+ setAmrsPatients(resp.results);
129
+ setDisplaytriageModal(true);
130
+ } else {
131
+ showAlert('error', 'Patient not found in the system', '');
132
+ setAmrsPatients([]);
118
133
  setDisplaytriageModal(true);
119
134
  }
120
135
  };
@@ -125,6 +140,59 @@ const RegistryComponent: React.FC<RegistryComponentProps> = () => {
125
140
  }
126
141
  };
127
142
  const handleSendToTriageModalSubmit = () => {};
143
+ const getPatient = (): HieClient => {
144
+ if (selectedPatient === 'principal') {
145
+ return principal;
146
+ } else if (selectedPatient === 'dependants') {
147
+ return selectedDependant;
148
+ } else {
149
+ return principal;
150
+ }
151
+ };
152
+ const handleSelectedDependant = (dependantId: string) => {
153
+ const dependants = principal.dependants;
154
+ if (dependants.length > 0) {
155
+ const dependant = dependants.find((d) => {
156
+ return d.result[0].id === dependantId;
157
+ });
158
+ if (dependant) {
159
+ setSelectedDependant(dependant.result[0] as unknown as HieClient);
160
+ }
161
+ }
162
+ };
163
+ const createAmrsPatient = async (client: HieClient) => {
164
+ try {
165
+ const resp = await registerHieClientInAmrs(selectedDependant, locationUuid);
166
+ if (resp) {
167
+ showAlert('success', 'Patient created succesfully', '');
168
+ setAmrsPatients([resp]);
169
+ }
170
+ } catch (e) {
171
+ const errorResp = e['responseBody'] ?? e.message;
172
+ showAlert('error', 'Error Creating Patient', '');
173
+ const errors = getErrorMessages(errorResp);
174
+ if (errors && errors.length > 0) {
175
+ for (let error of errors) {
176
+ showAlert('error', error, '');
177
+ }
178
+ }
179
+ }
180
+ };
181
+ const handleCancel = () => {
182
+ setPrincipal(null);
183
+ setSelectedDependant(null);
184
+ setAmrsPatients([]);
185
+ setSelectedPatient(null);
186
+ setIdentifierValue('');
187
+ setIdentifierType('National ID');
188
+ setDisplayClientDetailsModal(false);
189
+ setDisplayOtpModal(false);
190
+ setDisplaytriageModal(false);
191
+ };
192
+ const handleOtpSuccessfullVerification = () => {
193
+ setDisplayOtpModal(false);
194
+ setDisplayClientDetailsModal(true);
195
+ };
128
196
  return (
129
197
  <>
130
198
  <div className={styles.registryLayout}>
@@ -167,18 +235,13 @@ const RegistryComponent: React.FC<RegistryComponentProps> = () => {
167
235
  >
168
236
  {loading ? <InlineLoading description="Searching..." /> : 'Search'}
169
237
  </Button>
170
- <Button
171
- className={styles.registrySearchBtn}
172
- kind="secondary"
173
- onClick={handleEmergencyRegistration}
174
- disabled={loading}
175
- >
238
+ <Button className={styles.registrySearchBtn} kind="secondary" onClick={handleEmergencyRegistration}>
176
239
  Emergency Registration
177
240
  </Button>
178
241
  </div>
179
242
  </div>
180
243
  </div>
181
- {client ? (
244
+ {principal ? (
182
245
  <div className={styles.formRow}>
183
246
  <div className={styles.hieData}>
184
247
  <div className={styles.selectionHeader}>
@@ -204,108 +267,109 @@ const RegistryComponent: React.FC<RegistryComponentProps> = () => {
204
267
  </Button>
205
268
  </div>
206
269
  <div className={styles.btnContainer}>
207
- <Button kind="secondary">Cancel</Button>
270
+ <Button kind="secondary" onClick={handleCancel}>
271
+ Cancel
272
+ </Button>
208
273
  </div>
209
274
  </div>
210
275
  </div>
211
- {selectedPatient === 'principal' ? (
212
- <>
213
- <Table>
214
- <TableHead>
215
- <TableRow>
216
- <TableHeader>Name</TableHeader>
217
- <TableHeader>CR</TableHeader>
218
- <TableHeader>Phone No</TableHeader>
219
- <TableHeader>ID No</TableHeader>
220
- </TableRow>
221
- </TableHead>
222
- <TableBody>
223
- <TableRow>
224
- <TableCell>
225
- {client.first_name} {maskExceptFirstAndLast(client.middle_name)}{' '}
226
- {maskExceptFirstAndLast(client.last_name)}
227
- </TableCell>
228
- <TableCell>{maskValue(client.id)}</TableCell>
229
- <TableCell>{maskValue(client.phone)}</TableCell>
230
- <TableCell>{maskValue(client.identification_number)}</TableCell>
231
- </TableRow>
232
- </TableBody>
233
- </Table>
234
- </>
235
- ) : (
236
- <></>
237
- )}
238
- {selectedPatient === 'dependants' ? (
239
- <>
240
- <Table>
241
- <TableHead>
242
- <TableRow>
243
- <TableHeader>Name</TableHeader>
244
- <TableHeader>CR</TableHeader>
245
- <TableHeader>Relationship</TableHeader>
246
- </TableRow>
247
- </TableHead>
248
- <TableBody>
249
- {client.dependants.map((d) => {
276
+ <div className={styles.principalDependantSection}>
277
+ {selectedPatient === 'principal' ? (
278
+ <>
279
+ <Table>
280
+ <TableHead>
281
+ <TableRow>
282
+ <TableHeader>Name</TableHeader>
283
+ <TableHeader>CR</TableHeader>
284
+ <TableHeader>Phone No</TableHeader>
285
+ <TableHeader>ID No</TableHeader>
286
+ </TableRow>
287
+ </TableHead>
288
+ <TableBody>
289
+ <TableRow>
290
+ <TableCell>
291
+ {principal.first_name} {maskExceptFirstAndLast(principal.middle_name)}{' '}
292
+ {maskExceptFirstAndLast(principal.last_name)}
293
+ </TableCell>
294
+ <TableCell>{maskValue(principal.id)}</TableCell>
295
+ <TableCell>{maskValue(principal.phone)}</TableCell>
296
+ <TableCell>{maskValue(principal.identification_number)}</TableCell>
297
+ </TableRow>
298
+ </TableBody>
299
+ </Table>
300
+ </>
301
+ ) : (
302
+ <></>
303
+ )}
304
+ {selectedPatient === 'dependants' ? (
305
+ <>
306
+ <RadioButtonGroup
307
+ defaultSelected=""
308
+ helperText=""
309
+ invalidText=""
310
+ legendText=""
311
+ name="dependant-group"
312
+ onChange={(dependantId) => handleSelectedDependant(dependantId as string)}
313
+ >
314
+ {principal.dependants.map((d) => {
250
315
  const dependant = d.result[0];
251
316
  const relationship = d.relationship;
252
317
  return (
253
- <>
254
- <TableRow>
255
- <TableCell>
256
- {dependant.first_name} {maskExceptFirstAndLast(dependant.middle_name)}{' '}
257
- {maskExceptFirstAndLast(dependant.last_name)}
258
- </TableCell>
259
- <TableCell>{maskValue(dependant.id)}</TableCell>
260
- <TableCell>{relationship}</TableCell>
261
- </TableRow>
262
- </>
318
+ <RadioButton
319
+ id={dependant.id}
320
+ labelText={formatDependantDisplayData(dependant, relationship)}
321
+ value={dependant.id}
322
+ />
263
323
  );
264
324
  })}
265
- </TableBody>
266
- </Table>
267
- </>
268
- ) : (
269
- <></>
270
- )}
325
+ </RadioButtonGroup>
326
+ </>
327
+ ) : (
328
+ <></>
329
+ )}
271
330
 
272
- {displayOtpModal ? (
273
- <OtpVerificationModal
274
- requestCustomOtpDto={requestCustomOtpDto}
275
- phoneNumber={client.phone}
276
- open={displayOtpModal}
277
- onModalClose={handleModelClose}
278
- />
279
- ) : (
280
- <></>
281
- )}
331
+ {displayOtpModal ? (
332
+ <OtpVerificationModal
333
+ requestCustomOtpDto={requestCustomOtpDto}
334
+ phoneNumber={principal.phone}
335
+ open={displayOtpModal}
336
+ onModalClose={handleModelClose}
337
+ onOtpSuccessfullVerification={handleOtpSuccessfullVerification}
338
+ />
339
+ ) : (
340
+ <></>
341
+ )}
282
342
 
283
- {client && displayClientDetailsModal ? (
284
- <>
285
- <ClientDetailsModal
286
- client={client}
287
- open={displayClientDetailsModal}
288
- onModalClose={onClientDetailsModalClose}
289
- onSubmit={handleClientDetailsSubmit}
290
- onSendClientToTriage={handleSendClientToTriage}
291
- />{' '}
292
- </>
293
- ) : (
294
- <></>
295
- )}
343
+ {principal && displayClientDetailsModal ? (
344
+ <>
345
+ <ClientDetailsModal
346
+ client={getPatient()}
347
+ open={displayClientDetailsModal}
348
+ onModalClose={onClientDetailsModalClose}
349
+ onSubmit={handleClientDetailsSubmit}
350
+ onSendClientToTriage={handleSendClientToTriage}
351
+ />{' '}
352
+ </>
353
+ ) : (
354
+ <></>
355
+ )}
296
356
 
297
- {client && displaytriageModal ? (
298
- <>
299
- <SendToTriageModal
300
- patients={amrsPatients}
301
- open={displaytriageModal}
302
- onModalClose={onSendToTriageModalClose}
303
- onSubmit={handleSendToTriageModalSubmit}
304
- />
305
- </>
306
- ) : (
307
- <></>
308
- )}
357
+ {principal && displaytriageModal ? (
358
+ <>
359
+ <SendToTriageModal
360
+ client={getPatient()}
361
+ patients={amrsPatients}
362
+ open={displaytriageModal}
363
+ onModalClose={onSendToTriageModalClose}
364
+ onSubmit={handleSendToTriageModalSubmit}
365
+ onCreateAmrsPatient={createAmrsPatient}
366
+ onManualRegistration={handleManualRegistration}
367
+ />
368
+ </>
369
+ ) : (
370
+ <></>
371
+ )}
372
+ </div>
309
373
  </div>
310
374
  </div>
311
375
  ) : (
@@ -1,4 +1,4 @@
1
- import { Location, type Patient } from '@openmrs/esm-framework';
1
+ import { type Location, type Patient } from '@openmrs/esm-framework';
2
2
 
3
3
  export type IdentifierType = 'National ID' | 'Alien ID' | 'Passport' | 'Mandate Number' | 'Refugee ID';
4
4
 
@@ -10,6 +10,12 @@ export const IDENTIFIER_TYPES: IdentifierType[] = [
10
10
  'Refugee ID',
11
11
  ];
12
12
 
13
+ export enum OtpStatus {
14
+ Draft = 'DRAFT',
15
+ Sent = 'SENT',
16
+ Verified = 'VERIFIED',
17
+ }
18
+
13
19
  export interface ValidateCustomOtpResponse {
14
20
  message: string;
15
21
  isValid?: boolean;
@@ -65,6 +71,7 @@ export enum HieIdentificationType {
65
71
  MandateNumber = 'Mandate Number',
66
72
  Cr = 'id',
67
73
  TemporaryDependantID = 'Temporary Dependant ID',
74
+ BirthCertificate = 'Birth Certificate',
68
75
  }
69
76
 
70
77
  export interface HieIdentifications {
@@ -221,3 +228,54 @@ export type CreateVisitDto = {
221
228
  stopDatetime: null | string;
222
229
  patient: string;
223
230
  };
231
+
232
+ export interface GlobalError {
233
+ code: string;
234
+ message: string;
235
+ }
236
+
237
+ export interface AmrsErrorResponse {
238
+ message: string;
239
+ error: {
240
+ error: {
241
+ message: string;
242
+ code: string;
243
+ globalErrors?: GlobalError[];
244
+ fieldErrors?: any;
245
+ };
246
+ };
247
+ }
248
+
249
+ export interface CreatePersonDto {
250
+ gender?: string;
251
+ birthdate?: string;
252
+ dead?: boolean;
253
+ deathDate?: string;
254
+ names?: {
255
+ givenName?: string;
256
+ middleName?: string;
257
+ familyName?: string;
258
+ }[];
259
+ addresses?: {
260
+ country?: string;
261
+ address1?: string;
262
+ address2?: string;
263
+ address4?: string;
264
+ address7?: string;
265
+ address10?: string;
266
+ countyDistrict?: string;
267
+ stateProvince?: string;
268
+ cityVillage?: string;
269
+ longitude?: string;
270
+ latitude?: string;
271
+ }[];
272
+ attributes?: {
273
+ value: string | number;
274
+ attributeType: string;
275
+ }[];
276
+ }
277
+
278
+ export type CreatePatientDto = {
279
+ person: CreatePersonDto;
280
+ identifiers?: any;
281
+ };
@@ -1,4 +1,37 @@
1
+ import { type AmrsErrorResponse } from '../types';
2
+
1
3
  export function generateErrorMessage(error: any): string[] {
2
4
  const errors: string[] = [];
3
5
  return errors;
4
6
  }
7
+
8
+ export function getErrorMessages(error: AmrsErrorResponse) {
9
+ const errors = [];
10
+ if (error && error.error) {
11
+ if (error.error.error) {
12
+ const globalErrors = error.error.error.globalErrors || null;
13
+ if (globalErrors) {
14
+ for (const err of globalErrors) {
15
+ errors.push(err.message);
16
+ }
17
+ }
18
+ } else if (error.error) {
19
+ if (error.error['globalErrors']) {
20
+ const globalErrors = error.error['globalErrors'] || null;
21
+ if (globalErrors) {
22
+ for (const err of globalErrors) {
23
+ errors.push(err.message);
24
+ }
25
+ }
26
+ } else if (error.error['message']) {
27
+ errors.push(error.error['message']);
28
+ }
29
+ } else {
30
+ errors.push(
31
+ error.error.error.message ||
32
+ 'An error occurred while creating the patient. Please try again or contact support',
33
+ );
34
+ }
35
+ }
36
+ return errors;
37
+ }
@@ -0,0 +1,8 @@
1
+ import { type HieClient } from '../types';
2
+ import { maskExceptFirstAndLast } from './mask-data';
3
+
4
+ export function formatDependantDisplayData(dependant: HieClient, relationship: string): string {
5
+ const stringData = `${dependant.first_name} ${maskExceptFirstAndLast(dependant.middle_name)} (${dependant.gender}) (DOB: ${dependant.date_of_birth}) (${relationship}) (CR: ${dependant.id})`;
6
+
7
+ return stringData;
8
+ }