@ampath/esm-patient-registration-app 9.2.0-next.14 → 9.2.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.
@@ -0,0 +1,190 @@
1
+ interface PatientData {
2
+ [key: string]: any;
3
+ }
4
+
5
+ interface Dependant {
6
+ relationship: string;
7
+ result: Array<{
8
+ first_name: string;
9
+ middle_name: string;
10
+ last_name: string;
11
+ gender: string;
12
+ date_of_birth: string;
13
+ identification_number: string;
14
+ identification_type: string;
15
+ }>;
16
+ }
17
+
18
+ interface AlternativeContact {
19
+ contact_type: string;
20
+ contact_id: string;
21
+ contact_name: string;
22
+ relationship: string;
23
+ remarks: string;
24
+ }
25
+
26
+ const fieldMapping: Record<string, string | { path: string; transform?: (value: any) => any }> = {
27
+ givenName: 'first_name',
28
+ familyName: 'last_name',
29
+ middleName: 'middle_name',
30
+ birthdate: 'date_of_birth',
31
+ phone: 'phone',
32
+ email: 'email',
33
+ identificationNumber: 'identification_number',
34
+ identificationType: 'identification_type',
35
+ county: 'county',
36
+ subCounty: 'sub_county',
37
+ ward: 'ward',
38
+ village: 'village_estate',
39
+ postalAddress: 'postal_address',
40
+ address: 'address',
41
+ gender: {
42
+ path: 'gender',
43
+ transform: (value) => {
44
+ if (!value) return '';
45
+ const genderMap: Record<string, string> = {
46
+ male: 'male',
47
+ m: 'male',
48
+ female: 'female',
49
+ f: 'female',
50
+ other: 'other',
51
+ unknown: 'unknown',
52
+ };
53
+ return genderMap[value.toLowerCase()] || value.toLowerCase();
54
+ },
55
+ },
56
+ 'academicOccupation.highestLevelEducation': {
57
+ path: 'employment_type',
58
+ transform: (value) => value || '',
59
+ },
60
+
61
+ // Occupation field - also using employment_type (you might want to separate these)
62
+ 'academicOccupation.occupation': {
63
+ path: 'employment_type',
64
+ transform: (value) => value || '',
65
+ },
66
+
67
+ // Civil status/marital status
68
+ civilStatus: {
69
+ path: 'civil_status',
70
+ transform: (value) => value || '',
71
+ },
72
+ };
73
+
74
+ function mapNextOfKin(patient: PatientData, setFieldValue: (field: string, value: any) => void) {
75
+ const alternativeContacts: AlternativeContact[] = patient.alternative_contacts || [];
76
+
77
+ // Find spouse as next of kin (highest priority)
78
+ const spouseContact = alternativeContacts.find((contact) => contact.relationship.toLowerCase() === 'spouse');
79
+
80
+ // If no spouse, find any next of kin
81
+ const nextOfKinContact =
82
+ spouseContact ||
83
+ alternativeContacts.find(
84
+ (contact) =>
85
+ contact.remarks.toLowerCase().includes('next of kin') ||
86
+ contact.relationship.toLowerCase().includes('next of kin'),
87
+ );
88
+
89
+ if (nextOfKinContact) {
90
+ setFieldValue('nextOfKin.nextOfKinName', nextOfKinContact.contact_name);
91
+ setFieldValue('nextOfKin.nextOfKinRelationship', nextOfKinContact.relationship);
92
+ setFieldValue('nextOfKin.nextOfKinPhoneNumber', nextOfKinContact.contact_id);
93
+ setFieldValue('nextOfKin.nextOfKinResidence', patient.village_estate || patient.county || '');
94
+ } else {
95
+ setFieldValue('nextOfKin.nextOfKinName', '');
96
+ setFieldValue('nextOfKin.nextOfKinRelationship', '');
97
+ setFieldValue('nextOfKin.nextOfKinPhoneNumber', '');
98
+ setFieldValue('nextOfKin.nextOfKinResidence', '');
99
+ }
100
+ }
101
+
102
+ function mapRelationships(patient: PatientData, setFieldValue: (field: string, value: any) => void) {
103
+ const dependants: Dependant[] = patient.dependants || [];
104
+ const relationships: any[] = [];
105
+
106
+ dependants.forEach((dependant, index) => {
107
+ if (dependant.result && dependant.result.length > 0) {
108
+ const person = dependant.result[0];
109
+ relationships.push({
110
+ relationshipType: dependant.relationship, // "Child"
111
+ relatedPersonName: `${person.first_name} ${person.middle_name || ''} ${person.last_name}`.trim(),
112
+ relatedPersonUuid: '', // Not available in CR
113
+ relationshipTypeUuid: '', // Will need to map to OpenMRS relationship type UUID
114
+ // Additional fields that might be needed
115
+ relativeName: `${person.first_name} ${person.middle_name || ''} ${person.last_name}`.trim(),
116
+ relativePhone: '', // Not available in dependants
117
+ relationship: dependant.relationship,
118
+ birthdate: person.date_of_birth,
119
+ gender: person.gender,
120
+ identifier: person.identification_number,
121
+ });
122
+ }
123
+ });
124
+ setFieldValue('relationships', relationships);
125
+ }
126
+
127
+ function mapAddresses(patient: PatientData, setFieldValue: (field: string, value: any) => void) {
128
+ // county
129
+ if (patient['county']) {
130
+ setFieldValue('address.countyDistrict', patient['county']);
131
+ }
132
+ //subcounty
133
+ if (patient['sub_county']) {
134
+ setFieldValue('address.stateProvince', patient['sub_county']);
135
+ }
136
+ if (patient['ward']) {
137
+ //ward
138
+ setFieldValue('address.address4', patient['ward']);
139
+ }
140
+ }
141
+
142
+ function mapContactDetails(patient: PatientData, setFieldValue: (field: string, value: any) => void) {
143
+ if (patient['email']) {
144
+ setFieldValue('attributes.2f65dbcb-3e58-45a3-8be7-fd1dc9aa0faa', patient['email']);
145
+ }
146
+ if (patient['phone']) {
147
+ setFieldValue('attributes.72a759a8-1359-11df-a1f1-0026b9348838', patient['phone']);
148
+ }
149
+ }
150
+
151
+ function mapIdentifiers(patient: PatientData, setFieldValue: (field: string, value: any) => void) {
152
+ if (patient['id']) {
153
+ const crIdentifier = {
154
+ identifierTypeUuid: 'e88dc246-3614-4ee3-8141-1f2a83054e72',
155
+ initialValue: patient['id'],
156
+ identifierValue: patient['id'],
157
+ identifierName: 'CR',
158
+ selectedSource: null,
159
+ autoGeneration: false,
160
+ preferred: false,
161
+ required: true,
162
+ };
163
+ setFieldValue('identifiers.cr', crIdentifier);
164
+ }
165
+ }
166
+
167
+ export function applyClientRegistryMapping(patient: PatientData, setFieldValue: (field: string, value: any) => void) {
168
+ Object.entries(fieldMapping).forEach(([formField, mapping]) => {
169
+ let crField: string;
170
+ let transformFn: (value: any) => any = (v) => v;
171
+
172
+ if (typeof mapping === 'string') {
173
+ crField = mapping;
174
+ } else {
175
+ crField = mapping.path;
176
+ transformFn = mapping.transform || transformFn;
177
+ }
178
+
179
+ if (crField && patient[crField] !== undefined && patient[crField] !== null && patient[crField] !== '') {
180
+ const value = transformFn(patient[crField]);
181
+ setFieldValue(formField, value);
182
+ }
183
+ });
184
+
185
+ mapNextOfKin(patient, setFieldValue);
186
+ mapRelationships(patient, setFieldValue);
187
+ mapAddresses(patient, setFieldValue);
188
+ mapContactDetails(patient, setFieldValue);
189
+ mapIdentifiers(patient, setFieldValue);
190
+ }
@@ -1,6 +1,6 @@
1
1
  import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
2
  import classNames from 'classnames';
3
- import { Button, InlineLoading, Link } from '@carbon/react';
3
+ import { Button, InlineLoading, Link, InlineNotification } from '@carbon/react';
4
4
  import { XAxis } from '@carbon/react/icons';
5
5
  import { useLocation, useParams } from 'react-router-dom';
6
6
  import { useTranslation } from 'react-i18next';
@@ -25,6 +25,7 @@ import { type SavePatientForm, SavePatientTransactionManager } from './form-mana
25
25
  import { useInitialAddressFieldValues, useInitialFormValues, usePatientUuidMap } from './patient-registration-hooks';
26
26
  import BeforeSavePrompt from './before-save-prompt';
27
27
  import styles from './patient-registration.scss';
28
+ import ClientRegistryLookupSection from './client-registry/client-registry-search.component';
28
29
 
29
30
  let exportedInitialFormValuesForTesting = {} as FormValues;
30
31
 
@@ -56,6 +57,7 @@ export const PatientRegistration: React.FC<PatientRegistrationProps> = ({ savePa
56
57
  const [patientUuidMap] = usePatientUuidMap({}, isLoadingPatientToEdit, patientToEdit, uuidOfPatientToEdit);
57
58
 
58
59
  const [target, setTarget] = useState<undefined | string>();
60
+ const [isClientVerified, setIsClientVerified] = useState(false);
59
61
  const [capturePhotoProps, setCapturePhotoProps] = useState<CapturePhotoProps | null>(null);
60
62
 
61
63
  const location = currentSession?.sessionLocation?.uuid;
@@ -243,13 +245,28 @@ export const PatientRegistration: React.FC<PatientRegistrationProps> = ({ savePa
243
245
  </div>
244
246
  <div className={styles.infoGrid}>
245
247
  <PatientRegistrationContextProvider value={createContextValue(props)}>
246
- {sections.map((section, index) => (
247
- <SectionWrapper
248
- key={`registration-section-${section.id}`}
249
- sectionDefinition={section}
250
- index={index}
251
- />
252
- ))}
248
+ <div>
249
+ {!isClientVerified && !inEditMode && (
250
+ <div className={styles.notificationSpacing} style={{ marginTop: '1rem' }}>
251
+ <InlineNotification
252
+ title="Verification required"
253
+ subtitle="Please complete Client Registry OTP verification before proceeding with registration."
254
+ kind="info"
255
+ lowContrast
256
+ />
257
+ </div>
258
+ )}
259
+
260
+ <ClientRegistryLookupSection onClientVerified={() => setIsClientVerified(true)} />
261
+
262
+ {sections.map((section, index) => (
263
+ <SectionWrapper
264
+ key={`registration-section-${section.id}`}
265
+ sectionDefinition={section}
266
+ index={index}
267
+ />
268
+ ))}
269
+ </div>
253
270
  </PatientRegistrationContextProvider>
254
271
  </div>
255
272
  </div>