@ampath/esm-patient-registration-app 9.2.0-next.18 → 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 (36) hide show
  1. package/dist/2523.js +1 -0
  2. package/dist/2523.js.map +1 -0
  3. package/dist/6583.js +1 -0
  4. package/dist/6583.js.map +1 -0
  5. package/dist/7821.js +1 -1
  6. package/dist/8414.js +1 -1
  7. package/dist/8414.js.map +1 -1
  8. package/dist/9853.js +1 -0
  9. package/dist/9853.js.map +1 -0
  10. package/dist/main.js +1 -1
  11. package/dist/openmrs-esm-patient-registration-app.js.buildmanifest.json +76 -76
  12. package/dist/routes.json +1 -1
  13. package/package.json +1 -1
  14. package/src/index.ts +1 -1
  15. package/src/patient-registration/client-registry/client-registry-search.component.tsx +13 -4
  16. package/src/patient-registration/client-registry/existing-client/client-dependants-comparison/client-dependants-comparison.component.tsx +26 -0
  17. package/src/patient-registration/client-registry/existing-client/client-dependants-comparison/dependant-comparison-rows.component.tsx +153 -0
  18. package/src/patient-registration/client-registry/existing-client/client-details-comparison/client-details-comparison.component.tsx +181 -0
  19. package/src/patient-registration/client-registry/existing-client/client-details-comparison/comparison-table-row.component.tsx +42 -0
  20. package/src/patient-registration/client-registry/existing-client/client-registry-verification-tag.component.tsx +54 -0
  21. package/src/patient-registration/client-registry/existing-client/existing-client-tab.component.tsx +85 -0
  22. package/src/patient-registration/client-registry/existing-client/existing-client.resource.ts +78 -0
  23. package/src/patient-registration/client-registry/existing-client/mapper-utils.ts +419 -0
  24. package/src/patient-registration/client-registry/existing-client/types/index.ts +185 -0
  25. package/src/patient-registration/client-registry/{client-dependants → new-client/client-dependants}/list/client-depandants.component.tsx +1 -1
  26. package/src/patient-registration/client-registry/{client-details → new-client/client-details}/client-details.tsx +2 -2
  27. package/src/patient-registration/client-registry/new-client/new-client-tab.component.tsx +30 -0
  28. package/src/patient-registration/client-registry/types/index.ts +10 -0
  29. package/src/patient-registration/patient-registration.component.tsx +1 -0
  30. package/src/widgets/client-registry-verification.modal.tsx +13 -0
  31. package/dist/4395.js +0 -1
  32. package/dist/4395.js.map +0 -1
  33. package/dist/6741.js +0 -1
  34. package/dist/6741.js.map +0 -1
  35. package/dist/8882.js +0 -1
  36. package/dist/8882.js.map +0 -1
@@ -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;
@@ -0,0 +1,42 @@
1
+ import { TableRow, TableCell, Checkbox } from '@carbon/react';
2
+ import React, { useState, useEffect } from 'react';
3
+ import { type ComparisonTableRowProps } from '../types';
4
+
5
+ const ComparisonTableRow: React.FC<ComparisonTableRowProps> = ({
6
+ field,
7
+ label,
8
+ amrsValue,
9
+ hieValue,
10
+ onChange,
11
+ allChecked,
12
+ }) => {
13
+ const [checked, setChecked] = useState(false);
14
+ const randomString = Math.random().toString(10).substring(2, 6).toUpperCase();
15
+ useEffect(() => {
16
+ onChange?.(allChecked, field, hieValue, true);
17
+ setChecked(allChecked);
18
+ // eslint-disable-next-line react-hooks/exhaustive-deps
19
+ }, [allChecked]);
20
+
21
+ return (
22
+ <TableRow>
23
+ <TableCell>
24
+ <p style={{ color: amrsValue!.trim().toUpperCase() == hieValue!.trim().toUpperCase() ? '' : 'red' }}>{label}</p>
25
+ </TableCell>
26
+ <TableCell>{amrsValue}</TableCell>
27
+ <TableCell>{hieValue}</TableCell>
28
+ <TableCell>
29
+ <Checkbox
30
+ id={`cbox-${randomString}`}
31
+ onChange={(e) => {
32
+ onChange(e.target.checked, field, hieValue, false);
33
+ setChecked(e.target.checked);
34
+ }}
35
+ checked={checked}
36
+ />
37
+ </TableCell>
38
+ </TableRow>
39
+ );
40
+ };
41
+
42
+ export default ComparisonTableRow;
@@ -0,0 +1,54 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import { Button } from '@carbon/react';
3
+ import { age, usePatient } from '@openmrs/esm-framework';
4
+ import { Formik } from 'formik';
5
+ import ClientRegistryLookupSection from '../client-registry-search.component';
6
+
7
+ const ClientRegistryVerificationTag = () => {
8
+ const { patient } = usePatient();
9
+ const [showCrBtn, setShowCrBtn] = useState(false);
10
+ const [showVerifyModal, setShowVerifyModal] = useState(false);
11
+ const [isClientVerified, setIsClientVerified] = useState(false);
12
+ const initialFormValues = {};
13
+
14
+ useEffect(() => {
15
+ if (patient && patient.birthDate) {
16
+ const ageArr = age(patient.birthDate).split(' ');
17
+ if (ageArr.includes('yrs')) {
18
+ const yrs = Number(ageArr[0]);
19
+ setShowCrBtn(yrs > 17);
20
+ }
21
+ }
22
+ }, [patient]);
23
+
24
+ const openVerifyModal = () => {
25
+ setShowVerifyModal(true);
26
+ };
27
+ const closeVerifyModal = () => {
28
+ setShowVerifyModal(false);
29
+ };
30
+
31
+ return showCrBtn ? (
32
+ <>
33
+ <Button kind="ghost" style={{ backgroundColor: 'purple', color: 'white' }} onClick={openVerifyModal}>
34
+ Verify CR
35
+ </Button>
36
+ {showVerifyModal ? (
37
+ <Formik enableReinitialize initialValues={initialFormValues} onSubmit={null}>
38
+ <ClientRegistryLookupSection
39
+ onClientVerified={() => setIsClientVerified(true)}
40
+ onModalClose={closeVerifyModal}
41
+ open={showVerifyModal}
42
+ isNewClient={false}
43
+ />
44
+ </Formik>
45
+ ) : (
46
+ <></>
47
+ )}
48
+ </>
49
+ ) : (
50
+ <></>
51
+ );
52
+ };
53
+
54
+ export default ClientRegistryVerificationTag;
@@ -0,0 +1,85 @@
1
+ import React, { useState, useEffect } from 'react';
2
+ import { Tabs, TabList, Tab, TabPanels, TabPanel, InlineLoading } from '@carbon/react';
3
+ import { type HieClient } from '../types';
4
+ import ClientDetailsComparison from './client-details-comparison/client-details-comparison.component';
5
+ import { type CustomRelationship, type AmrsClient } from './types';
6
+ import ClientDependantsComparison from './client-dependants-comparison/client-dependants-comparison.component';
7
+ import { showSnackbar, usePatient } from '@openmrs/esm-framework';
8
+ import { fetchAmrsPatientData, getRelationships } from './existing-client.resource';
9
+
10
+ interface ExistingClientTabProps {
11
+ hieClient: HieClient;
12
+ }
13
+
14
+ const ExistingClientTab: React.FC<ExistingClientTabProps> = ({ hieClient }) => {
15
+ const [amrsClient, setAmrsClient] = useState<AmrsClient>();
16
+ const [loading, setLoading] = useState<boolean>(false);
17
+ const { patientUuid } = usePatient();
18
+ const [relationships, setRelationships] = useState<Array<CustomRelationship>>([]);
19
+
20
+ useEffect(() => {
21
+ if (patientUuid) {
22
+ handleAmrsPersonDetails();
23
+ handleFetchPatientRelationships();
24
+ }
25
+ // eslint-disable-next-line react-hooks/exhaustive-deps
26
+ }, [patientUuid]);
27
+
28
+ const handleFetchPatientRelationships = async () => {
29
+ const resp = await getRelationships(patientUuid);
30
+ if (resp) {
31
+ setRelationships(resp);
32
+ }
33
+ };
34
+
35
+ const handleAmrsPersonDetails = async () => {
36
+ try {
37
+ setLoading(true);
38
+ const response = await fetchAmrsPatientData(patientUuid);
39
+ if (response) {
40
+ setAmrsClient(response.data);
41
+ }
42
+ showSnackbar({
43
+ kind: 'success',
44
+ title: 'AMRS person data fetched successfully.',
45
+ });
46
+ } catch (er) {
47
+ showSnackbar({
48
+ kind: 'error',
49
+ title: 'Error fetching AMRS person data.',
50
+ subtitle: JSON.stringify(er),
51
+ });
52
+ } finally {
53
+ setLoading(false);
54
+ }
55
+ };
56
+
57
+ return loading ? (
58
+ <InlineLoading description="Fetching existing client details..." />
59
+ ) : (
60
+ <Tabs>
61
+ <TabList contained>
62
+ <Tab>Patient</Tab>
63
+ <Tab>Dependants</Tab>
64
+ </TabList>
65
+ <TabPanels>
66
+ <TabPanel>
67
+ {hieClient && <ClientDetailsComparison hieClient={hieClient} amrsClient={amrsClient} fromDependant={false} />}
68
+ </TabPanel>
69
+ <TabPanel>
70
+ {hieClient && hieClient.dependants ? (
71
+ <ClientDependantsComparison
72
+ hieDependants={hieClient.dependants}
73
+ amrsClient={amrsClient}
74
+ patientRelationships={relationships}
75
+ />
76
+ ) : (
77
+ <div>Dependants not found.</div>
78
+ )}
79
+ </TabPanel>
80
+ </TabPanels>
81
+ </Tabs>
82
+ );
83
+ };
84
+
85
+ export default ExistingClientTab;
@@ -0,0 +1,78 @@
1
+ import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
2
+ import { type AmrsClient } from './types';
3
+ import { mapAmrsPatientRelationship } from './mapper-utils';
4
+
5
+ export async function fetchAmrsPatientData(patientUuid: string) {
6
+ return await openmrsFetch<AmrsClient>(`${restBaseUrl}/patient/${patientUuid}?v=full`, {
7
+ method: 'GET',
8
+ }).catch((err) => {
9
+ console.error(err);
10
+ });
11
+ }
12
+
13
+ export async function updateAmrsPersonIdentifiers(
14
+ patientUuid: string,
15
+ identifierUuid: string,
16
+ payload: unknown,
17
+ fromDependant = false,
18
+ ) {
19
+ const resource = fromDependant ? 'person' : 'patient';
20
+ return await openmrsFetch(`${restBaseUrl}/${resource}/${patientUuid}/identifier/${identifierUuid}`, {
21
+ method: 'POST',
22
+ headers: {
23
+ 'Content-Type': 'application/json',
24
+ },
25
+ body: payload,
26
+ });
27
+ }
28
+
29
+ export async function fetchAmrsPersonData(personUuid: string) {
30
+ return await openmrsFetch(`${restBaseUrl}/person/${personUuid}?v=full`, {
31
+ method: 'GET',
32
+ }).catch((err) => {
33
+ console.error(err);
34
+ });
35
+ }
36
+
37
+ export async function updatePerson(patientUuid: string, payload: unknown) {
38
+ return await openmrsFetch<AmrsClient>(`${restBaseUrl}/person/${patientUuid}`, {
39
+ method: 'POST',
40
+ headers: {
41
+ 'Content-Type': 'application/json',
42
+ },
43
+ body: payload,
44
+ });
45
+ }
46
+
47
+ export async function createPerson(payload: unknown) {
48
+ return await openmrsFetch<AmrsClient>(`${restBaseUrl}/person`, {
49
+ method: 'POST',
50
+ headers: {
51
+ 'Content-Type': 'application/json',
52
+ },
53
+ body: payload,
54
+ });
55
+ }
56
+
57
+ export async function createRelationship(payload: unknown) {
58
+ return await openmrsFetch(`${restBaseUrl}/relationship`, {
59
+ method: 'POST',
60
+ headers: {
61
+ 'Content-Type': 'application/json',
62
+ },
63
+ body: payload,
64
+ });
65
+ }
66
+
67
+ export async function getRelationships(patientUuid: string) {
68
+ const response = await openmrsFetch(`${restBaseUrl}/relationship?person=${patientUuid}&v=full`, {
69
+ method: 'GET',
70
+ headers: {
71
+ 'Content-Type': 'application/json',
72
+ },
73
+ });
74
+ if (response && response.data) {
75
+ return mapAmrsPatientRelationship(patientUuid, response.data.results);
76
+ }
77
+ return [];
78
+ }