@ampath/esm-patient-registration-app 9.2.0-next.17 → 9.2.0-next.19

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 (53) hide show
  1. package/dist/2498.js +2 -0
  2. package/dist/2498.js.map +1 -0
  3. package/dist/2523.js +1 -0
  4. package/dist/2523.js.map +1 -0
  5. package/dist/5239.js +1 -1
  6. package/dist/5239.js.map +1 -1
  7. package/dist/6276.js +1 -1
  8. package/dist/6276.js.map +1 -1
  9. package/dist/6583.js +1 -0
  10. package/dist/6583.js.map +1 -0
  11. package/dist/7821.js +1 -1
  12. package/dist/7821.js.map +1 -1
  13. package/dist/8414.js +1 -1
  14. package/dist/8414.js.map +1 -1
  15. package/dist/8434.js +1 -1
  16. package/dist/8434.js.map +1 -1
  17. package/dist/9853.js +1 -0
  18. package/dist/9853.js.map +1 -0
  19. package/dist/main.js +1 -1
  20. package/dist/main.js.map +1 -1
  21. package/dist/openmrs-esm-patient-registration-app.js +1 -1
  22. package/dist/openmrs-esm-patient-registration-app.js.buildmanifest.json +109 -109
  23. package/dist/routes.json +1 -1
  24. package/package.json +1 -1
  25. package/src/index.ts +1 -1
  26. package/src/patient-registration/client-registry/client-registry-search.component.tsx +164 -68
  27. package/src/patient-registration/client-registry/client-registry-search.scss +26 -0
  28. package/src/patient-registration/client-registry/existing-client/client-dependants-comparison/client-dependants-comparison.component.tsx +26 -0
  29. package/src/patient-registration/client-registry/existing-client/client-dependants-comparison/dependant-comparison-rows.component.tsx +153 -0
  30. package/src/patient-registration/client-registry/existing-client/client-details-comparison/client-details-comparison.component.tsx +181 -0
  31. package/src/patient-registration/client-registry/existing-client/client-details-comparison/comparison-table-row.component.tsx +42 -0
  32. package/src/patient-registration/client-registry/existing-client/client-registry-verification-tag.component.tsx +54 -0
  33. package/src/patient-registration/client-registry/existing-client/existing-client-tab.component.tsx +85 -0
  34. package/src/patient-registration/client-registry/existing-client/existing-client.resource.ts +78 -0
  35. package/src/patient-registration/client-registry/existing-client/mapper-utils.ts +419 -0
  36. package/src/patient-registration/client-registry/existing-client/types/index.ts +185 -0
  37. package/src/patient-registration/client-registry/hie-client-adapter.ts +56 -0
  38. package/src/patient-registration/client-registry/new-client/client-dependants/list/client-depandants.component.tsx +55 -0
  39. package/src/patient-registration/client-registry/new-client/client-details/client-details.tsx +42 -0
  40. package/src/patient-registration/client-registry/new-client/new-client-tab.component.tsx +30 -0
  41. package/src/patient-registration/client-registry/types/index.ts +210 -0
  42. package/src/patient-registration/custom-patient-registration.scss +6 -0
  43. package/src/patient-registration/patient-registration.component.tsx +24 -12
  44. package/src/widgets/client-registry-verification.modal.tsx +13 -0
  45. package/dist/4395.js +0 -1
  46. package/dist/4395.js.map +0 -1
  47. package/dist/6996.js +0 -1
  48. package/dist/6996.js.map +0 -1
  49. package/dist/8882.js +0 -1
  50. package/dist/8882.js.map +0 -1
  51. package/dist/9933.js +0 -2
  52. package/dist/9933.js.map +0 -1
  53. /package/dist/{9933.js.LICENSE.txt → 2498.js.LICENSE.txt} +0 -0
@@ -1,13 +1,34 @@
1
1
  import React, { useState } from 'react';
2
- import { Button, TextInput, InlineLoading, InlineNotification, Dropdown } from '@carbon/react';
2
+ import {
3
+ Button,
4
+ TextInput,
5
+ InlineLoading,
6
+ InlineNotification,
7
+ Dropdown,
8
+ Modal,
9
+ ModalBody,
10
+ Tabs,
11
+ TabList,
12
+ Tab,
13
+ TabPanels,
14
+ TabPanel,
15
+ } from '@carbon/react';
3
16
  import { showSnackbar, useSession } from '@openmrs/esm-framework';
4
17
  import { useFormikContext } from 'formik';
5
- import styles from '../patient-registration.scss';
18
+ import styles from './client-registry-search.scss';
6
19
  import { requestCustomOtp, validateCustomOtp, fetchClientRegistryData } from './client-registry.resource';
7
20
  import { applyClientRegistryMapping } from './map-client-registry-to-form-utils';
21
+ import ClientDetails from './new-client/client-details/client-details';
22
+ import { type HieClient } from './types';
23
+ import ClientDependantList from './new-client/client-dependants/list/client-depandants.component';
24
+ import NewClientTab from './new-client/new-client-tab.component';
25
+ import ExistingClientTab from './existing-client/existing-client-tab.component';
8
26
 
9
27
  export interface ClientRegistryLookupSectionProps {
10
28
  onClientVerified?: () => void;
29
+ onModalClose: () => void;
30
+ open: boolean;
31
+ isNewClient: boolean;
11
32
  }
12
33
 
13
34
  export type IdentifierType = 'National ID' | 'Alien ID' | 'Passport' | 'Mandate Number' | 'Refugee ID';
@@ -20,7 +41,12 @@ export const IDENTIFIER_TYPES: IdentifierType[] = [
20
41
  'Refugee ID',
21
42
  ];
22
43
 
23
- const ClientRegistryLookupSection: React.FC<ClientRegistryLookupSectionProps> = ({ onClientVerified }) => {
44
+ const ClientRegistryLookupSection: React.FC<ClientRegistryLookupSectionProps> = ({
45
+ onClientVerified,
46
+ open,
47
+ onModalClose,
48
+ isNewClient = true,
49
+ }) => {
24
50
  const { setFieldValue } = useFormikContext<any>();
25
51
  const [identifierType, setIdentifierType] = useState<IdentifierType>('National ID');
26
52
  const [identifierValue, setIdentifierValue] = useState('');
@@ -31,6 +57,7 @@ const ClientRegistryLookupSection: React.FC<ClientRegistryLookupSectionProps> =
31
57
  const [sessionId, setSessionId] = useState('');
32
58
  const [error, setError] = useState<string>('');
33
59
  const { sessionLocation } = useSession();
60
+ const [client, setClient] = useState<HieClient>();
34
61
  const locationUuid = sessionLocation?.uuid;
35
62
 
36
63
  async function withTimeout<T>(promise: Promise<T>, ms = 10000): Promise<T> {
@@ -64,8 +91,7 @@ const ClientRegistryLookupSection: React.FC<ClientRegistryLookupSectionProps> =
64
91
  if (patients.length === 0) throw new Error('No matching patient found in Client Registry.');
65
92
 
66
93
  const patient = patients[0];
67
- applyClientRegistryMapping(patient, setFieldValue);
68
-
94
+ setClient(patient);
69
95
  showSnackbar({
70
96
  kind: 'success',
71
97
  title: 'Client Data Loaded',
@@ -156,77 +182,147 @@ const ClientRegistryLookupSection: React.FC<ClientRegistryLookupSectionProps> =
156
182
  }
157
183
  };
158
184
 
185
+ const useHieData = () => {
186
+ applyClientRegistryMapping(client, setFieldValue);
187
+ onModalClose();
188
+ };
189
+ const registerOnAfyaYangu = () => {
190
+ window.open('https://afyayangu.go.ke/', '_blank');
191
+ };
192
+
159
193
  return (
160
- <div className={styles.section}>
161
- <h4 className={styles.sectionTitle}>Client Registry Verification</h4>
194
+ <Modal
195
+ open={open}
196
+ size="lg"
197
+ onSecondarySubmit={onModalClose}
198
+ onRequestClose={onModalClose}
199
+ onRequestSubmit={registerOnAfyaYangu}
200
+ primaryButtonText="Register on Afya Yangu"
201
+ secondaryButtonText="Cancel">
202
+ <ModalBody>
203
+ <div className={styles.modalVerificationLayout}>
204
+ <h4 className={styles.sectionTitle}>Client Registry Verification</h4>
162
205
 
163
- {error && (
164
- <div className={styles.notificationSpacing}>
165
- <InlineNotification title="Error" subtitle={error} kind="error" lowContrast />
166
- </div>
167
- )}
168
-
169
- <div className={styles.fieldGroup}>
170
- <Dropdown
171
- id="identifier-type-dropdown"
172
- label="Identifier Type"
173
- titleText="Select Identifier Type"
174
- items={IDENTIFIER_TYPES}
175
- selectedItem={identifierType}
176
- onChange={({ selectedItem }) => setIdentifierType(selectedItem as IdentifierType)}
177
- disabled={otpSent}
178
- />
179
- </div>
180
-
181
- <div className={styles.fieldGroup}>
182
- <TextInput
183
- id="identifier-value"
184
- labelText={`${identifierType} Value`}
185
- value={identifierValue}
186
- onChange={(e) => setIdentifierValue(e.target.value)}
187
- disabled={otpSent}
188
- placeholder={`Enter ${identifierType.toLowerCase()} value`}
189
- />
190
- </div>
191
-
192
- <div style={{ marginTop: '0.75rem' }}>
193
- {!otpSent ? (
194
- <Button kind="secondary" onClick={handleSendOtp} disabled={loading}>
195
- {loading ? <InlineLoading description="Sending..." /> : 'Send OTP'}
196
- </Button>
197
- ) : (
198
- <>
199
- <div style={{ marginTop: '0.75rem' }}>
200
- <TextInput
201
- id="otp-input"
202
- labelText="Enter OTP"
203
- value={otp}
204
- onChange={(e) => setOtp(e.target.value)}
205
- disabled={otpVerified}
206
- placeholder="Enter the code sent to your phone"
207
- />
206
+ {error && (
207
+ <div className={styles.notificationSpacing}>
208
+ <InlineNotification title="Error" subtitle={error} kind="error" lowContrast />
208
209
  </div>
210
+ )}
211
+ {!client ? (
212
+ <div className={styles.formSection}>
213
+ {!otpSent ? (
214
+ <>
215
+ <div className={styles.formRow}>
216
+ <div className={styles.formControl}>
217
+ <Dropdown
218
+ id="identifier-type-dropdown"
219
+ label="Identifier Type"
220
+ titleText="Select Identifier Type"
221
+ items={IDENTIFIER_TYPES}
222
+ selectedItem={identifierType}
223
+ onChange={({ selectedItem }) => setIdentifierType(selectedItem as IdentifierType)}
224
+ disabled={otpSent}
225
+ />
226
+ </div>
209
227
 
210
- <div style={{ marginTop: '0.5rem', display: 'flex', gap: '0.5rem' }}>
211
- {!otpVerified ? (
212
- <Button size="sm" kind="secondary" onClick={handleVerifyOtp} disabled={loading}>
213
- {loading ? <InlineLoading description="Verifying..." /> : 'Verify OTP'}
214
- </Button>
228
+ <div className={styles.formControl}>
229
+ <TextInput
230
+ id="identifier-value"
231
+ labelText={`${identifierType} Value`}
232
+ value={identifierValue}
233
+ onChange={(e) => setIdentifierValue(e.target.value)}
234
+ disabled={otpSent}
235
+ placeholder={`Enter ${identifierType.toLowerCase()} value`}
236
+ />
237
+ </div>
238
+ </div>
239
+ <div className={styles.formRow}>
240
+ <div className={styles.formControl}>
241
+ <Button kind="primary" onClick={handleSendOtp} disabled={loading}>
242
+ {loading ? <InlineLoading description="Sending..." /> : 'Send OTP'}
243
+ </Button>
244
+ </div>
245
+ </div>
246
+ </>
215
247
  ) : (
216
- <Button kind="primary" onClick={handleFetchCR} disabled={loading}>
217
- {loading ? <InlineLoading description="Fetching..." /> : 'Fetch Client Registry Data'}
218
- </Button>
248
+ <>
249
+ {!otpVerified ? (
250
+ <>
251
+ <div className={styles.formRow}>
252
+ <div className={styles.formControl}>
253
+ <TextInput
254
+ id="otp-input"
255
+ labelText="Enter OTP"
256
+ value={otp}
257
+ onChange={(e) => setOtp(e.target.value)}
258
+ disabled={otpVerified}
259
+ placeholder="Enter the code sent to your phone"
260
+ />
261
+ </div>
262
+ </div>
263
+ <div className={styles.formRow}>
264
+ <div className={styles.actionBtn}>
265
+ <Button kind="primary" onClick={handleVerifyOtp} disabled={loading}>
266
+ {loading ? <InlineLoading description="Verifying..." /> : 'Verify OTP'}
267
+ </Button>
268
+ </div>
269
+ <div className={styles.actionBtn}>
270
+ <Button kind="secondary" onClick={handleSendOtp} disabled={loading}>
271
+ {loading ? <InlineLoading description="Resending OTP..." /> : 'Resend OTP'}
272
+ </Button>
273
+ </div>
274
+ <div className={styles.actionBtn}>
275
+ <Button kind="tertiary" onClick={() => setOtpSent(false)}>
276
+ Back
277
+ </Button>
278
+ </div>
279
+ </div>
280
+ </>
281
+ ) : (
282
+ <>
283
+ <div className={styles.formRow}>
284
+ <div className={styles.formControl}>
285
+ <Button kind="primary" onClick={handleFetchCR} disabled={loading}>
286
+ {loading ? <InlineLoading description="Fetching..." /> : 'Fetch Client Registry Data'}
287
+ </Button>
288
+ </div>
289
+ </div>
290
+ </>
291
+ )}
292
+ </>
219
293
  )}
220
- {!otpVerified && (
221
- <Button size="sm" kind="tertiary" onClick={() => setOtpSent(false)}>
222
- Change ID
223
- </Button>
294
+ </div>
295
+ ) : (
296
+ <></>
297
+ )}
298
+
299
+ {otpVerified && client ? (
300
+ <div className="clientDataSection">
301
+ {isNewClient ? (
302
+ <NewClientTab client={client} useHieData={useHieData} />
303
+ ) : (
304
+ <ExistingClientTab hieClient={client} />
224
305
  )}
306
+ {/* <Tabs>
307
+ <TabList contained>
308
+ <Tab>Patient</Tab>
309
+ <Tab>Dependants</Tab>
310
+ </TabList>
311
+ <TabPanels>
312
+ <TabPanel>{client ? <ClientDetails client={client} /> : <></>}</TabPanel>
313
+ <TabPanel>
314
+ {client.dependants ? <ClientDependantList hieDependants={client.dependants} /> : <></>}
315
+ </TabPanel>
316
+ </TabPanels>
317
+ </Tabs>
318
+ <Button onClick={useHieData}>Use Data</Button> */}
225
319
  </div>
226
- </>
227
- )}
228
- </div>
229
- </div>
320
+ ) : (
321
+ <></>
322
+ )}
323
+ </div>
324
+ </ModalBody>
325
+ </Modal>
230
326
  );
231
327
  };
232
328
 
@@ -0,0 +1,26 @@
1
+ .modalVerificationLayout {
2
+ display: flex;
3
+ flex-direction: column;
4
+ row-gap: 10px;
5
+ width: 100%;
6
+ }
7
+ .formSection {
8
+ display: flex;
9
+ flex-direction: column;
10
+ width: 100%;
11
+ }
12
+ .formControl {
13
+ width: 45%;
14
+ }
15
+ .formRow {
16
+ display: flex;
17
+ flex-direction: row;
18
+ width: 100%;
19
+ column-gap: 10px;
20
+ padding-top: 5px;
21
+ padding-bottom: 5px;
22
+ }
23
+ .actionBtn {
24
+ display: flex;
25
+ flex-direction: column;
26
+ }
@@ -0,0 +1,26 @@
1
+ import { Accordion } from '@carbon/react';
2
+ import React from 'react';
3
+ import { type ClientDependantsComparisonProps } from '../types';
4
+ import ClientDependantComparisonRows from './dependant-comparison-rows.component';
5
+
6
+ const ClientDependantsComparison: React.FC<ClientDependantsComparisonProps> = ({
7
+ hieDependants,
8
+ amrsClient,
9
+ patientRelationships,
10
+ }) => {
11
+ return (
12
+ <div>
13
+ <Accordion size="lg">
14
+ {hieDependants.map((dependant) => (
15
+ <ClientDependantComparisonRows
16
+ hieDependant={dependant}
17
+ amrsClient={amrsClient}
18
+ patientRelationships={patientRelationships}
19
+ />
20
+ ))}
21
+ </Accordion>
22
+ </div>
23
+ );
24
+ };
25
+
26
+ export default ClientDependantsComparison;
@@ -0,0 +1,153 @@
1
+ import { AccordionItem, Row, Column, Select, SelectItem, Button } from '@carbon/react';
2
+ import { showSnackbar } from '@openmrs/esm-framework';
3
+ import React, { useState } from 'react';
4
+ import { useTranslation } from 'react-i18next';
5
+ import { createPerson, createRelationship, fetchAmrsPersonData } from '../existing-client.resource';
6
+ import {
7
+ patientObjFields,
8
+ mapFieldValue,
9
+ getPatientAttributes,
10
+ personSyncFields,
11
+ nameFields,
12
+ addressFields,
13
+ getPatientRelationshipPayload,
14
+ } from '../mapper-utils';
15
+ import { type AmrsClient, type ClientDependantComparisonRowsProps } from '../types';
16
+ import { type HieDependant } from '../../types';
17
+ import ClientDetailsComparison from '../client-details-comparison/client-details-comparison.component';
18
+
19
+ const ClientDependantComparisonRows: React.FC<ClientDependantComparisonRowsProps> = ({
20
+ hieDependant,
21
+ patientRelationships,
22
+ amrsClient,
23
+ }) => {
24
+ const { t } = useTranslation();
25
+ const [amrsRelationExists, setAmrsRelationExists] = useState(false);
26
+ const [amrsDependantData, setAmrsDependantData] = useState<AmrsClient>();
27
+ const [selectedRelationship, setSelectedRelationship] = useState('');
28
+
29
+ const handleCreateDependant = async (dependantBody: HieDependant) => {
30
+ try {
31
+ const syncFields = {};
32
+ patientObjFields.forEach((field) => {
33
+ let fieldArr = mapFieldValue(field, dependantBody.result[0], amrsClient);
34
+ const hieFieldValue = fieldArr[1];
35
+ syncFields[field] = hieFieldValue;
36
+ });
37
+
38
+ // Person
39
+ const patientPayload = {};
40
+ const names = {};
41
+ const addresses = {};
42
+ const otherFields = {};
43
+ const attributes = getPatientAttributes(dependantBody.result[0]);
44
+ Object.entries(syncFields).forEach(([k, v]) => {
45
+ if (personSyncFields.includes(k)) {
46
+ // names
47
+ if (nameFields.includes(k)) {
48
+ names[k] = v;
49
+ }
50
+ // addresses
51
+ else if (addressFields.includes(k)) {
52
+ if (v) {
53
+ addresses[k] = v;
54
+ }
55
+ } else {
56
+ otherFields[k] = v;
57
+ }
58
+ }
59
+ });
60
+ Object.assign(
61
+ patientPayload,
62
+ otherFields,
63
+ { addresses: [addresses] },
64
+ { names: [names] },
65
+ { attributes: attributes },
66
+ );
67
+ const response = await createPerson(patientPayload);
68
+ if (response && response.data) {
69
+ showSnackbar({
70
+ kind: 'success',
71
+ title: 'Dependant created successfully.',
72
+ });
73
+ const relationshipPayload = getPatientRelationshipPayload(
74
+ amrsClient,
75
+ dependantBody.relationship,
76
+ response.data.uuid,
77
+ );
78
+ await createRelationship(relationshipPayload);
79
+ showSnackbar({
80
+ kind: 'success',
81
+ title: 'Dependant relationship created successfully.',
82
+ });
83
+ }
84
+ } catch (err) {
85
+ showSnackbar({
86
+ kind: 'error',
87
+ title: 'Error syncing patient data.',
88
+ subtitle: JSON.stringify(err),
89
+ });
90
+ }
91
+ };
92
+
93
+ const handleSelectAmrsDependant = async (e: any) => {
94
+ const uuid = e.target.value;
95
+ if (!uuid) {
96
+ setAmrsRelationExists(false);
97
+ setAmrsDependantData(null);
98
+ }
99
+ const response = await fetchAmrsPersonData(uuid);
100
+ if (response && response.data) {
101
+ setAmrsRelationExists(true);
102
+ setAmrsDependantData((prev) => ({ ...prev, person: response.data }));
103
+ } else {
104
+ setAmrsRelationExists(false);
105
+ setAmrsDependantData(null);
106
+ const relationshipUuid = patientRelationships.find((v) => v.relatedPersonUuid === uuid);
107
+ if (relationshipUuid && relationshipUuid.relationshipType) {
108
+ setSelectedRelationship(relationshipUuid.relationshipType);
109
+ }
110
+ }
111
+ };
112
+
113
+ const getDependantName = () => {
114
+ const name = hieDependant.result[0];
115
+ return `${name.first_name} ${name.middle_name} ${name.last_name} (${hieDependant.relationship})`;
116
+ };
117
+
118
+ return (
119
+ <>
120
+ <AccordionItem title={getDependantName()}>
121
+ <Row>
122
+ <Column>
123
+ <Select labelText={t('amrsDependants', 'AMRS dependants')} onChange={handleSelectAmrsDependant}>
124
+ <SelectItem text={t('selectAmrsRelation', 'Select AMRS relation')} value="" />
125
+ {patientRelationships.map((relationship) => (
126
+ <SelectItem
127
+ text={`${relationship.display} (${relationship.relationshipType})`}
128
+ value={relationship.relatedPerson.uuid}
129
+ />
130
+ ))}
131
+ </Select>
132
+ </Column>
133
+ {!amrsRelationExists ? (
134
+ <Column>
135
+ <Button onClick={() => handleCreateDependant(hieDependant)}>
136
+ {t('createDependant', 'Create dependant in AMRS')}
137
+ </Button>
138
+ </Column>
139
+ ) : null}
140
+ </Row>
141
+ <Row>
142
+ <ClientDetailsComparison
143
+ hieClient={hieDependant.result[0]}
144
+ amrsClient={amrsDependantData}
145
+ fromDependant={true}
146
+ />
147
+ </Row>
148
+ </AccordionItem>
149
+ </>
150
+ );
151
+ };
152
+
153
+ export default ClientDependantComparisonRows;
@@ -0,0 +1,181 @@
1
+ import React, { useState } from 'react';
2
+ import { type ClientDetailsComparisonProps } from '../types';
3
+ import {
4
+ Row,
5
+ Button,
6
+ InlineLoading,
7
+ Table,
8
+ TableHead,
9
+ TableRow,
10
+ TableHeader,
11
+ Checkbox,
12
+ TableBody,
13
+ } from '@carbon/react';
14
+ import { showSnackbar } from '@openmrs/esm-framework';
15
+ import { HieIdentificationType } from '../../types';
16
+ import {
17
+ personSyncFields,
18
+ nameFields,
19
+ addressFields,
20
+ identifiersSyncFields,
21
+ getIdentifierUuid,
22
+ patientObjFields,
23
+ mapFieldValue,
24
+ } from '../mapper-utils';
25
+ import ComparisonTableRow from './comparison-table-row.component';
26
+ import { updateAmrsPersonIdentifiers, updatePerson } from '../existing-client.resource';
27
+
28
+ const ClientDetailsComparison: React.FC<ClientDetailsComparisonProps> = ({ hieClient, amrsClient, fromDependant }) => {
29
+ const [syncFields, setSyncFields] = useState<Array<Record<string, string>>>([]);
30
+ const [allChecked, setAllChecked] = useState(false);
31
+ const [loading, setLoading] = useState(false);
32
+ const locationUuid = '18c343eb-b353-462a-9139-b16606e6b6c2';
33
+ const randomString = Math.random().toString(10).substring(2, 6).toUpperCase();
34
+
35
+ const handleFieldChange = (checked: boolean, field: string, value: string, multiple: boolean) => {
36
+ if (multiple) {
37
+ if (checked) {
38
+ setSyncFields((prev) => [...prev, { [field]: value }]);
39
+ } else {
40
+ setSyncFields([]);
41
+ }
42
+ } else {
43
+ if (checked) {
44
+ setSyncFields([...syncFields, { [field]: value }]);
45
+ } else {
46
+ setSyncFields((prev) => prev.filter((p) => !Object.keys(p).includes(field)));
47
+ }
48
+ }
49
+ };
50
+
51
+ const handleCheckAll = (e) => {
52
+ setAllChecked(e.target.checked);
53
+ };
54
+
55
+ const handleSync = async () => {
56
+ try {
57
+ const payload = {};
58
+ syncFields.forEach((field) => {
59
+ let key = Object.keys(field)[0];
60
+ payload[key] = field[key];
61
+ });
62
+
63
+ // Person
64
+ const patientPayload = {};
65
+ const names = {};
66
+ const addresses = {};
67
+ const otherFields = {};
68
+ Object.entries(payload).forEach(([k, v]) => {
69
+ if (personSyncFields.includes(k)) {
70
+ // names
71
+ if (nameFields.includes(k)) {
72
+ names[k] = v;
73
+ }
74
+ // addresses
75
+ else if (addressFields.includes(k)) {
76
+ if (v) {
77
+ addresses[k] = v;
78
+ }
79
+ } else {
80
+ otherFields[k] = v;
81
+ }
82
+ }
83
+ });
84
+ Object.assign(patientPayload, otherFields, { addresses: [addresses] }, { names: [names] });
85
+ await updatePerson(amrsClient.person.uuid, patientPayload);
86
+ showSnackbar({
87
+ kind: 'success',
88
+ title: 'Patient successfully synced.',
89
+ });
90
+
91
+ // Identifiers
92
+ Object.entries(payload).forEach(async ([k, v]) => {
93
+ if (identifiersSyncFields().includes(k)) {
94
+ if (v) {
95
+ const identifierUuid = getIdentifierUuid(HieIdentificationType[k]);
96
+ const identifierPayload = {
97
+ identifier: v,
98
+ location: locationUuid,
99
+ identifierType: identifierUuid,
100
+ };
101
+ try {
102
+ // Check if the identifier exists
103
+ if (amrsClient?.person?.identifiers?.find((i) => i.identifierType.uuid === identifierUuid)) {
104
+ // update to have the selected identifier
105
+ await updateAmrsPersonIdentifiers(
106
+ amrsClient.person.uuid,
107
+ identifierUuid + '',
108
+ identifierPayload,
109
+ fromDependant,
110
+ );
111
+ } else {
112
+ // create to have the blank identifier
113
+ await updateAmrsPersonIdentifiers(amrsClient.person.uuid, '', identifierPayload, fromDependant);
114
+ }
115
+ } catch (err) {
116
+ showSnackbar({
117
+ kind: 'error',
118
+ title: 'Error syncing patient identifiers.',
119
+ });
120
+ }
121
+ }
122
+ }
123
+ });
124
+ showSnackbar({
125
+ kind: 'success',
126
+ title: 'Patient identifiers successfully synced.',
127
+ });
128
+
129
+ setSyncFields([]);
130
+ } catch (err) {
131
+ showSnackbar({
132
+ kind: 'error',
133
+ title: 'Error syncing patient data.',
134
+ subtitle: JSON.stringify(err?.error?.message),
135
+ });
136
+ } finally {
137
+ setLoading(false);
138
+ }
139
+ };
140
+
141
+ return (
142
+ <>
143
+ <Table>
144
+ <TableHead>
145
+ <TableRow>
146
+ <TableHeader>Field</TableHeader>
147
+ <TableHeader>AMRS Person</TableHeader>
148
+ <TableHeader>HIE Patient</TableHeader>
149
+ <TableHeader>
150
+ <Checkbox id={`cbox-multiple-${randomString}`} onChange={(e) => handleCheckAll(e)} />
151
+ </TableHeader>
152
+ </TableRow>
153
+ </TableHead>
154
+ <TableBody>
155
+ {patientObjFields.map((field) => {
156
+ const fieldValue = mapFieldValue(field, hieClient, amrsClient);
157
+ const amrsField = fieldValue[0];
158
+ const hieField = fieldValue[1];
159
+
160
+ return (
161
+ <ComparisonTableRow
162
+ label={field}
163
+ field={field}
164
+ amrsValue={amrsField}
165
+ hieValue={hieField}
166
+ onChange={handleFieldChange}
167
+ allChecked={allChecked}
168
+ />
169
+ );
170
+ })}
171
+ </TableBody>
172
+ </Table>
173
+ <Row>
174
+ {syncFields.length ? <Button onClick={handleSync}>Sync data</Button> : null}
175
+ {loading ? <InlineLoading description="Syncing patient details..." /> : null}
176
+ </Row>
177
+ </>
178
+ );
179
+ };
180
+
181
+ export default ClientDetailsComparison;