@kenyaemr/esm-patient-registration-app 8.0.3-pre.138 → 8.0.3-pre.140
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.
- package/.turbo/turbo-build.log +17 -17
- package/dist/108.js +1 -1
- package/dist/250.js +1 -1
- package/dist/574.js +1 -1
- package/dist/610.js +1 -0
- package/dist/610.js.map +1 -0
- package/dist/662.js +1 -1
- package/dist/76.js +1 -1
- package/dist/94.js +2 -0
- package/dist/94.js.map +1 -0
- package/dist/kenyaemr-esm-patient-registration-app.js +1 -1
- package/dist/kenyaemr-esm-patient-registration-app.js.buildmanifest.json +62 -62
- package/dist/kenyaemr-esm-patient-registration-app.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +1 -1
- package/src/client-registry/hie-client-registry/hie-resource.ts +78 -0
- package/src/client-registry/hie-client-registry/modal/confirm-hie.modal.tsx +75 -71
- package/src/client-registry/hie-client-registry/modal/confirm-hie.scss +12 -0
- package/src/client-registry/hie-client-registry/modal/hie-otp-verification-form.component.tsx +88 -0
- package/src/client-registry/hie-client-registry/modal/hie-patient-detail-preview.component.tsx +73 -0
- package/translations/en.json +7 -0
- package/dist/652.js +0 -1
- package/dist/652.js.map +0 -1
- package/dist/895.js +0 -2
- package/dist/895.js.map +0 -1
- /package/dist/{895.js.LICENSE.txt → 94.js.LICENSE.txt} +0 -0
package/dist/routes.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"webservices.rest":"^2.24.0"},"pages":[{"component":"root","route":"patient-registration","online":true,"offline":true},{"component":"editPatient","routeRegex":"patient\\/([a-zA-Z0-9\\-]+)\\/edit","online":true,"offline":true}],"extensions":[{"component":"addPatientLink","name":"add-patient-action","slot":"top-nav-actions-slot","online":true,"offline":true},{"component":"patientPhotoExtension","name":"patient-photo-widget","slot":"patient-photo-slot","online":true,"offline":true},{"component":"editPatientDetailsButton","name":"edit-patient-details-button","slot":"patient-actions-slot","online":true,"offline":true},{"component":"editPatientDetailsButton","name":"edit-patient-details-button","slot":"patient-search-actions-slot","online":true,"offline":true}],"modals":[{"name":"cancel-patient-edit-modal","component":"cancelPatientEditModal"},{"name":"delete-identifier-confirmation-modal","component":"deleteIdentifierConfirmationModal"},{"component":"emptyClientRegistryModal","name":"empty-client-registry-modal"},{"component":"confirmClientRegistryModal","name":"confirm-client-registry-modal"},{"component":"hieConfirmationModal","name":"hie-confirmation-modal"}],"version":"8.0.3-pre.
|
|
1
|
+
{"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"webservices.rest":"^2.24.0"},"pages":[{"component":"root","route":"patient-registration","online":true,"offline":true},{"component":"editPatient","routeRegex":"patient\\/([a-zA-Z0-9\\-]+)\\/edit","online":true,"offline":true}],"extensions":[{"component":"addPatientLink","name":"add-patient-action","slot":"top-nav-actions-slot","online":true,"offline":true},{"component":"patientPhotoExtension","name":"patient-photo-widget","slot":"patient-photo-slot","online":true,"offline":true},{"component":"editPatientDetailsButton","name":"edit-patient-details-button","slot":"patient-actions-slot","online":true,"offline":true},{"component":"editPatientDetailsButton","name":"edit-patient-details-button","slot":"patient-search-actions-slot","online":true,"offline":true}],"modals":[{"name":"cancel-patient-edit-modal","component":"cancelPatientEditModal"},{"name":"delete-identifier-confirmation-modal","component":"deleteIdentifierConfirmationModal"},{"component":"emptyClientRegistryModal","name":"empty-client-registry-modal"},{"component":"confirmClientRegistryModal","name":"confirm-client-registry-modal"},{"component":"hieConfirmationModal","name":"hie-confirmation-modal"}],"version":"8.0.3-pre.140"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kenyaemr/esm-patient-registration-app",
|
|
3
|
-
"version": "8.0.3-pre.
|
|
3
|
+
"version": "8.0.3-pre.140",
|
|
4
4
|
"description": "Patient registration microfrontend for the OpenMRS SPA",
|
|
5
5
|
"browser": "dist/kenyaemr-esm-patient-registration-app.js",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -3,6 +3,8 @@ import { type PatientIdentifierValue, type FormValues } from '../../patient-regi
|
|
|
3
3
|
import { type MapperConfig, type HIEPatient, type ErrorResponse } from './hie-types';
|
|
4
4
|
import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
|
|
5
5
|
import { v4 } from 'uuid';
|
|
6
|
+
import { z } from 'zod';
|
|
7
|
+
import dayjs from 'dayjs';
|
|
6
8
|
/**
|
|
7
9
|
* Represents a client for interacting with a Health Information Exchange (HIE) resource.
|
|
8
10
|
* @template T - The type of the resource being fetched.
|
|
@@ -182,3 +184,79 @@ export const getPatientName = (patient: fhir.Patient) => {
|
|
|
182
184
|
const middleName = patient.name[0]?.['given']?.[0]?.replace(givenName, '')?.trim() ?? '';
|
|
183
185
|
return { familyName, givenName, middleName };
|
|
184
186
|
};
|
|
187
|
+
|
|
188
|
+
export const authorizationFormSchema = z.object({
|
|
189
|
+
otp: z.string().min(1, 'Required'),
|
|
190
|
+
receiver: z
|
|
191
|
+
.string()
|
|
192
|
+
.regex(/^(\+?254|0)((7|1)\d{8})$/)
|
|
193
|
+
.optional(),
|
|
194
|
+
});
|
|
195
|
+
|
|
196
|
+
export function generateOTP(length = 5) {
|
|
197
|
+
let otpNumbers = '0123456789';
|
|
198
|
+
let OTP = '';
|
|
199
|
+
const len = otpNumbers.length;
|
|
200
|
+
for (let i = 0; i < length; i++) {
|
|
201
|
+
OTP += otpNumbers[Math.floor(Math.random() * len)];
|
|
202
|
+
}
|
|
203
|
+
return OTP;
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
export function persistOTP(otp: string, patientUuid: string) {
|
|
207
|
+
sessionStorage.setItem(
|
|
208
|
+
patientUuid,
|
|
209
|
+
JSON.stringify({
|
|
210
|
+
otp,
|
|
211
|
+
timestamp: new Date().toISOString(),
|
|
212
|
+
}),
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export async function sendOtp({ otp, receiver }: z.infer<typeof authorizationFormSchema>, patientName: string) {
|
|
217
|
+
const payload = parseMessage(
|
|
218
|
+
{ otp, patient_name: patientName, expiry_time: 5 },
|
|
219
|
+
'Dear {{patient_name}}, your OTP for accessing your Shared Health Records (SHR) is {{otp}}. Please enter this code to proceed. The code is valid for {{expiry_time}} minutes.',
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
const url = `${restBaseUrl}/kenyaemr/send-kenyaemr-sms?message=${payload}&phone=${receiver}`;
|
|
223
|
+
|
|
224
|
+
const res = await openmrsFetch(url, {
|
|
225
|
+
method: 'POST',
|
|
226
|
+
redirect: 'follow',
|
|
227
|
+
});
|
|
228
|
+
if (res.ok) {
|
|
229
|
+
return await res.json();
|
|
230
|
+
}
|
|
231
|
+
throw new Error('Error sending otp');
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function parseMessage(object, template) {
|
|
235
|
+
const placeholderRegex = /{{(.*?)}}/g;
|
|
236
|
+
|
|
237
|
+
const parsedMessage = template.replace(placeholderRegex, (match, fieldName) => {
|
|
238
|
+
if (object.hasOwnProperty(fieldName)) {
|
|
239
|
+
return object[fieldName];
|
|
240
|
+
} else {
|
|
241
|
+
return match;
|
|
242
|
+
}
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
return parsedMessage;
|
|
246
|
+
}
|
|
247
|
+
export function verifyOtp(otp: string, patientUuid: string) {
|
|
248
|
+
const data = sessionStorage.getItem(patientUuid);
|
|
249
|
+
if (!data) {
|
|
250
|
+
throw new Error('Invalid OTP');
|
|
251
|
+
}
|
|
252
|
+
const { otp: storedOtp, timestamp } = JSON.parse(data);
|
|
253
|
+
const isExpired = dayjs(timestamp).add(5, 'minutes').isBefore(dayjs());
|
|
254
|
+
if (storedOtp !== otp) {
|
|
255
|
+
throw new Error('Invalid OTP');
|
|
256
|
+
}
|
|
257
|
+
if (isExpired) {
|
|
258
|
+
throw new Error('OTP Expired');
|
|
259
|
+
}
|
|
260
|
+
sessionStorage.removeItem(patientUuid);
|
|
261
|
+
return 'Verification success';
|
|
262
|
+
}
|
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { Button, ModalBody, ModalFooter, ModalHeader } from '@carbon/react';
|
|
2
|
+
import React, { useState } from 'react';
|
|
2
3
|
import { useTranslation } from 'react-i18next';
|
|
3
|
-
import { Button, ModalBody, ModalHeader, ModalFooter, Accordion, AccordionItem, CodeSnippet } from '@carbon/react';
|
|
4
|
-
import { age, ExtensionSlot, formatDate } from '@openmrs/esm-framework';
|
|
5
4
|
import { type HIEPatient } from '../hie-types';
|
|
6
|
-
import capitalize from 'lodash-es/capitalize';
|
|
7
5
|
import styles from './confirm-hie.scss';
|
|
8
|
-
import
|
|
9
|
-
import
|
|
10
|
-
import
|
|
6
|
+
import { authorizationFormSchema, generateOTP, getPatientName, persistOTP, sendOtp, verifyOtp } from '../hie-resource';
|
|
7
|
+
import HIEPatientDetailPreview from './hie-patient-detail-preview.component';
|
|
8
|
+
import HIEOTPVerficationForm from './hie-otp-verification-form.component';
|
|
9
|
+
import { Form } from '@carbon/react';
|
|
10
|
+
import { FormProvider, useForm } from 'react-hook-form';
|
|
11
|
+
import { type z } from 'zod';
|
|
12
|
+
import { zodResolver } from '@hookform/resolvers/zod';
|
|
13
|
+
import { showSnackbar } from '@openmrs/esm-framework';
|
|
11
14
|
|
|
12
15
|
interface HIEConfirmationModalProps {
|
|
13
16
|
closeModal: () => void;
|
|
@@ -17,77 +20,78 @@ interface HIEConfirmationModalProps {
|
|
|
17
20
|
|
|
18
21
|
const HIEConfirmationModal: React.FC<HIEConfirmationModalProps> = ({ closeModal, patient, onUseValues }) => {
|
|
19
22
|
const { t } = useTranslation();
|
|
20
|
-
const
|
|
23
|
+
const [mode, setMode] = useState<'authorization' | 'preview'>('preview');
|
|
24
|
+
const [status, setStatus] = useState<'loadingOtp' | 'otpSendSuccessfull' | 'otpFetchError'>();
|
|
25
|
+
const phoneNumber = patient?.telecom?.find((num) => num.value)?.value;
|
|
26
|
+
const getidentifier = (code: string) =>
|
|
27
|
+
patient?.identifier?.find((identifier) => identifier?.type?.coding?.some((coding) => coding?.code === code));
|
|
28
|
+
const patientId = patient?.id ?? getidentifier('SHA-number')?.value;
|
|
29
|
+
const form = useForm<z.infer<typeof authorizationFormSchema>>({
|
|
30
|
+
defaultValues: {
|
|
31
|
+
receiver: phoneNumber,
|
|
32
|
+
},
|
|
33
|
+
resolver: zodResolver(authorizationFormSchema),
|
|
34
|
+
});
|
|
35
|
+
const patientName = getPatientName(patient);
|
|
21
36
|
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
37
|
+
const onSubmit = async (values: z.infer<typeof authorizationFormSchema>) => {
|
|
38
|
+
try {
|
|
39
|
+
verifyOtp(values.otp, patientId);
|
|
40
|
+
showSnackbar({ title: 'Success', kind: 'success', subtitle: 'Access granted successfully' });
|
|
41
|
+
onUseValues();
|
|
42
|
+
closeModal();
|
|
43
|
+
} catch (error) {
|
|
44
|
+
showSnackbar({ title: 'Faulure', kind: 'error', subtitle: `${error}` });
|
|
45
|
+
}
|
|
25
46
|
};
|
|
26
47
|
|
|
27
48
|
return (
|
|
28
|
-
<
|
|
29
|
-
<
|
|
30
|
-
<
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
<span className={styles.patientNameValue}>
|
|
45
|
-
<p>{maskData(givenName)}</p>
|
|
46
|
-
<span>•</span>
|
|
47
|
-
<p>{maskData(middleName)}</p>
|
|
48
|
-
<span>•</span>
|
|
49
|
-
<p>{maskData(familyName)}</p>
|
|
50
|
-
</span>
|
|
51
|
-
}
|
|
49
|
+
<FormProvider {...form}>
|
|
50
|
+
<Form onSubmit={form.handleSubmit(onSubmit)}>
|
|
51
|
+
<ModalHeader closeModal={closeModal}>
|
|
52
|
+
<span className={styles.header}>
|
|
53
|
+
{mode === 'authorization'
|
|
54
|
+
? t('hiePatientVerification', 'HIE Patient Verification')
|
|
55
|
+
: t('hieModal', 'HIE Patient Record Found')}
|
|
56
|
+
</span>
|
|
57
|
+
</ModalHeader>
|
|
58
|
+
<ModalBody>
|
|
59
|
+
{mode === 'authorization' ? (
|
|
60
|
+
<HIEOTPVerficationForm
|
|
61
|
+
name={`${patientName.givenName} ${patientName.middleName}`}
|
|
62
|
+
patientId={patientId}
|
|
63
|
+
status={status}
|
|
64
|
+
setStatus={setStatus}
|
|
52
65
|
/>
|
|
66
|
+
) : (
|
|
67
|
+
<HIEPatientDetailPreview patient={patient} />
|
|
68
|
+
)}
|
|
69
|
+
</ModalBody>
|
|
70
|
+
<ModalFooter>
|
|
71
|
+
<Button kind="secondary" onClick={closeModal}>
|
|
72
|
+
{t('cancel', 'Cancel')}
|
|
73
|
+
</Button>
|
|
53
74
|
|
|
54
|
-
|
|
55
|
-
<
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
<div>
|
|
71
|
-
<Accordion>
|
|
72
|
-
<AccordionItem title={t('viewFullResponse', 'View full response')}>
|
|
73
|
-
<CodeSnippet type="multi" feedback="Copied to clipboard">
|
|
74
|
-
{JSON.stringify(patient, null, 2)}
|
|
75
|
-
</CodeSnippet>
|
|
76
|
-
</AccordionItem>
|
|
77
|
-
</Accordion>
|
|
78
|
-
</div>
|
|
79
|
-
</ModalBody>
|
|
80
|
-
<ModalFooter>
|
|
81
|
-
<Button kind="secondary" onClick={closeModal}>
|
|
82
|
-
{t('cancel', 'Cancel')}
|
|
83
|
-
</Button>
|
|
84
|
-
|
|
85
|
-
<Button onClick={handleUseValues} kind="primary">
|
|
86
|
-
{t('useValues', 'Use values')}
|
|
87
|
-
</Button>
|
|
88
|
-
</ModalFooter>
|
|
89
|
-
</div>
|
|
75
|
+
{mode === 'preview' && (
|
|
76
|
+
<Button onClick={() => setMode('authorization')} kind="primary">
|
|
77
|
+
{t('useValues', 'Use values')}
|
|
78
|
+
</Button>
|
|
79
|
+
)}
|
|
80
|
+
{mode === 'authorization' && (
|
|
81
|
+
<Button
|
|
82
|
+
kind="primary"
|
|
83
|
+
type="submit"
|
|
84
|
+
disabled={form.formState.isSubmitting || status !== 'otpSendSuccessfull'}>
|
|
85
|
+
{t('verifyAndUseValues', 'Verify & Use values')}
|
|
86
|
+
</Button>
|
|
87
|
+
)}
|
|
88
|
+
</ModalFooter>
|
|
89
|
+
</Form>
|
|
90
|
+
</FormProvider>
|
|
90
91
|
);
|
|
91
92
|
};
|
|
92
93
|
|
|
93
94
|
export default HIEConfirmationModal;
|
|
95
|
+
function onVerificationSuccesfull() {
|
|
96
|
+
throw new Error('Function not implemented.');
|
|
97
|
+
}
|
|
@@ -48,3 +48,15 @@
|
|
|
48
48
|
color: colors.$gray-40;
|
|
49
49
|
}
|
|
50
50
|
}
|
|
51
|
+
|
|
52
|
+
.grid {
|
|
53
|
+
margin: 0 layout.$spacing-05;
|
|
54
|
+
padding: layout.$spacing-05 0rem 0rem orem;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.otpInputRow {
|
|
58
|
+
display: flex;
|
|
59
|
+
flex-direction: row;
|
|
60
|
+
gap: layout.$spacing-03;
|
|
61
|
+
align-items: flex-end;
|
|
62
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { Button, Column, Row, Stack, Tag, TextInput, InlineLoading } from '@carbon/react';
|
|
2
|
+
import { showSnackbar } from '@openmrs/esm-framework';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { Controller, useFormContext } from 'react-hook-form';
|
|
5
|
+
import { useTranslation } from 'react-i18next';
|
|
6
|
+
import { type authorizationFormSchema, generateOTP, persistOTP, sendOtp } from '../hie-resource';
|
|
7
|
+
import styles from './confirm-hie.scss';
|
|
8
|
+
import { type z } from 'zod';
|
|
9
|
+
|
|
10
|
+
type HIEOTPVerficationFormProps = {
|
|
11
|
+
name: string;
|
|
12
|
+
patientId: string;
|
|
13
|
+
status?: 'loadingOtp' | 'otpSendSuccessfull' | 'otpFetchError';
|
|
14
|
+
setStatus: React.Dispatch<React.SetStateAction<'loadingOtp' | 'otpSendSuccessfull' | 'otpFetchError'>>;
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
const HIEOTPVerficationForm: React.FC<HIEOTPVerficationFormProps> = ({ name, patientId, setStatus, status }) => {
|
|
18
|
+
const form = useFormContext<z.infer<typeof authorizationFormSchema>>();
|
|
19
|
+
const { t } = useTranslation();
|
|
20
|
+
|
|
21
|
+
const handleGetOTP = async () => {
|
|
22
|
+
try {
|
|
23
|
+
setStatus('loadingOtp');
|
|
24
|
+
const otp = generateOTP(5);
|
|
25
|
+
await sendOtp({ otp, receiver: form.watch('receiver') }, name);
|
|
26
|
+
setStatus('otpSendSuccessfull');
|
|
27
|
+
persistOTP(otp, patientId);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
setStatus('otpFetchError');
|
|
30
|
+
showSnackbar({ title: t('error', 'Error'), kind: 'error', subtitle: error?.message });
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<Stack gap={4} className={styles.grid}>
|
|
36
|
+
<Column>
|
|
37
|
+
<Controller
|
|
38
|
+
control={form.control}
|
|
39
|
+
name="receiver"
|
|
40
|
+
render={({ field }) => (
|
|
41
|
+
<TextInput
|
|
42
|
+
invalid={form.formState.errors[field.name]?.message}
|
|
43
|
+
invalidText={form.formState.errors[field.name]?.message}
|
|
44
|
+
{...field}
|
|
45
|
+
placeholder={t('patientPhoneNUmber', 'Patient Phone number')}
|
|
46
|
+
labelText={t('patientPhoneNUmber', 'Patient Phone number')}
|
|
47
|
+
helperText={t('phoneNumberHelper', 'Patient will receive OTP on this number')}
|
|
48
|
+
/>
|
|
49
|
+
)}
|
|
50
|
+
/>
|
|
51
|
+
</Column>
|
|
52
|
+
|
|
53
|
+
<Column>
|
|
54
|
+
<Controller
|
|
55
|
+
control={form.control}
|
|
56
|
+
name="otp"
|
|
57
|
+
render={({ field }) => (
|
|
58
|
+
<Row className={styles.otpInputRow}>
|
|
59
|
+
<TextInput
|
|
60
|
+
invalid={form.formState.errors[field.name]?.message}
|
|
61
|
+
invalidText={form.formState.errors[field.name]?.message}
|
|
62
|
+
{...field}
|
|
63
|
+
placeholder={t('otpCode', 'OTP Authorization code')}
|
|
64
|
+
labelText={t('otpCode', 'OTP Authorization code')}
|
|
65
|
+
/>
|
|
66
|
+
<Button
|
|
67
|
+
onClick={handleGetOTP}
|
|
68
|
+
role="button"
|
|
69
|
+
type="blue"
|
|
70
|
+
kind="tertiary"
|
|
71
|
+
disabled={['loadingOtp', 'otpSendSuccessfull'].includes(status)}>
|
|
72
|
+
{status === 'loadingOtp' ? (
|
|
73
|
+
<InlineLoading status="active" iconDescription="Loading" description="Loading data..." />
|
|
74
|
+
) : status === 'otpFetchError' ? (
|
|
75
|
+
t('retry', 'Retry')
|
|
76
|
+
) : (
|
|
77
|
+
t('verifyOTP', 'Verify with OTP')
|
|
78
|
+
)}
|
|
79
|
+
</Button>
|
|
80
|
+
</Row>
|
|
81
|
+
)}
|
|
82
|
+
/>
|
|
83
|
+
</Column>
|
|
84
|
+
</Stack>
|
|
85
|
+
);
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
export default HIEOTPVerficationForm;
|
package/src/client-registry/hie-client-registry/modal/hie-patient-detail-preview.component.tsx
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Accordion, AccordionItem, CodeSnippet } from '@carbon/react';
|
|
2
|
+
import { age, ExtensionSlot, formatDate } from '@openmrs/esm-framework';
|
|
3
|
+
import capitalize from 'lodash-es/capitalize';
|
|
4
|
+
import React from 'react';
|
|
5
|
+
import { useTranslation } from 'react-i18next';
|
|
6
|
+
import DependentInfo from '../dependants/dependants.component';
|
|
7
|
+
import { getPatientName, maskData } from '../hie-resource';
|
|
8
|
+
import PatientInfo from '../patient-info/patient-info.component';
|
|
9
|
+
import styles from './confirm-hie.scss';
|
|
10
|
+
|
|
11
|
+
type HIEPatientDetailPreviewProps = {
|
|
12
|
+
patient: fhir.Patient;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const HIEPatientDetailPreview: React.FC<HIEPatientDetailPreviewProps> = ({ patient }) => {
|
|
16
|
+
const { familyName, givenName, middleName } = getPatientName(patient);
|
|
17
|
+
const { t } = useTranslation();
|
|
18
|
+
const getidentifier = (code: string) =>
|
|
19
|
+
patient?.identifier?.find((identifier) => identifier?.type?.coding?.some((coding) => coding?.code === code));
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<>
|
|
23
|
+
<div className={styles.patientDetails}>
|
|
24
|
+
<ExtensionSlot
|
|
25
|
+
className={styles.patientPhotoContainer}
|
|
26
|
+
name="patient-photo-slot"
|
|
27
|
+
state={{ patientName: `${maskData(givenName)} . ${maskData(middleName)} . ${maskData(familyName)}` }}
|
|
28
|
+
/>
|
|
29
|
+
<div className={styles.patientInfoContainer}>
|
|
30
|
+
<PatientInfo label={t('healthID', 'HealthID')} value={getidentifier('SHA-number')?.value} />
|
|
31
|
+
<PatientInfo
|
|
32
|
+
label={t('patientName', 'Patient name')}
|
|
33
|
+
customValue={
|
|
34
|
+
<span className={styles.patientNameValue}>
|
|
35
|
+
<p>{maskData(givenName)}</p>
|
|
36
|
+
<span>•</span>
|
|
37
|
+
<p>{maskData(middleName)}</p>
|
|
38
|
+
<span>•</span>
|
|
39
|
+
<p>{maskData(familyName)}</p>
|
|
40
|
+
</span>
|
|
41
|
+
}
|
|
42
|
+
/>
|
|
43
|
+
|
|
44
|
+
<PatientInfo label={t('age', 'Age')} value={age(patient?.birthDate)} />
|
|
45
|
+
<PatientInfo label={t('dateOfBirth', 'Date of birth')} value={formatDate(new Date(patient?.birthDate))} />
|
|
46
|
+
<PatientInfo label={t('gender', 'Gender')} value={capitalize(patient?.gender)} />
|
|
47
|
+
<PatientInfo
|
|
48
|
+
label={t('maritalStatus', 'Marital status')}
|
|
49
|
+
value={patient?.maritalStatus?.coding?.map((m) => m.code).join('')}
|
|
50
|
+
/>
|
|
51
|
+
|
|
52
|
+
{(!patient?.contact || patient?.contact.length === 0) && (
|
|
53
|
+
<PatientInfo label={t('dependents', 'Dependents')} value="--" />
|
|
54
|
+
)}
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
|
|
58
|
+
<DependentInfo dependents={patient?.contact} />
|
|
59
|
+
|
|
60
|
+
<div>
|
|
61
|
+
<Accordion>
|
|
62
|
+
<AccordionItem title={t('viewFullResponse', 'View full response')}>
|
|
63
|
+
<CodeSnippet type="multi" feedback="Copied to clipboard">
|
|
64
|
+
{JSON.stringify(patient, null, 2)}
|
|
65
|
+
</CodeSnippet>
|
|
66
|
+
</AccordionItem>
|
|
67
|
+
</Accordion>
|
|
68
|
+
</div>
|
|
69
|
+
</>
|
|
70
|
+
);
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export default HIEPatientDetailPreview;
|
package/translations/en.json
CHANGED
|
@@ -74,6 +74,7 @@
|
|
|
74
74
|
"givenNameRequired": "Given name is required",
|
|
75
75
|
"healthID": "HealthID",
|
|
76
76
|
"hieModal": "HIE Patient Record Found",
|
|
77
|
+
"hiePatientVerification": "HIE Patient Verification",
|
|
77
78
|
"identifierSearch": "Identifier search",
|
|
78
79
|
"identifierType": "Identifier type",
|
|
79
80
|
"identifierValueRequired": "Identifier value is required",
|
|
@@ -101,12 +102,15 @@
|
|
|
101
102
|
"optional": "optional",
|
|
102
103
|
"optionalIdentifierLabel": "{{identifierName}} (optional)",
|
|
103
104
|
"other": "Other",
|
|
105
|
+
"otpCode": "OTP Authorization code",
|
|
104
106
|
"patientDetailsFound": "Patient information found in the registry, do you want to use the information to continue with registration?",
|
|
105
107
|
"patientName": "Patient name",
|
|
106
108
|
"patientNameKnown": "Patient's Name is Known?",
|
|
107
109
|
"patientNotFound": "The patient records could not be found in Client registry, do you want to continue to create and post patient to registry",
|
|
110
|
+
"patientPhoneNUmber": "Patient Phone number",
|
|
108
111
|
"patientRegistrationBreadcrumb": "Patient Registration",
|
|
109
112
|
"patientVerificationFromHIE": "Patient verification from HIE",
|
|
113
|
+
"phoneNumberHelper": "Patient will receive OTP on this number",
|
|
110
114
|
"postToRegistry": "Post to registry",
|
|
111
115
|
"refreshOrContactAdmin": "Try refreshing the page or contact your system administrator",
|
|
112
116
|
"registerPatient": "Register patient",
|
|
@@ -124,6 +128,7 @@
|
|
|
124
128
|
"removeIdentifierButton": "Remove identifier",
|
|
125
129
|
"resetIdentifierTooltip": "Reset",
|
|
126
130
|
"restoreRelationshipActionButton": "Undo",
|
|
131
|
+
"retry": "Retry",
|
|
127
132
|
"searchAddress": "Search address",
|
|
128
133
|
"searchClientRegistry": "Search client registry",
|
|
129
134
|
"searchIdentifierPlaceholder": "Search identifier",
|
|
@@ -149,6 +154,8 @@
|
|
|
149
154
|
"updatePatientSuccessSnackbarTitle": "Patient Details Updated",
|
|
150
155
|
"useValues": "Use values",
|
|
151
156
|
"validate": "Validate",
|
|
157
|
+
"verifyAndUseValues": "Verify & Use values",
|
|
158
|
+
"verifyOTP": "Verify with OTP",
|
|
152
159
|
"viewFullResponse": "View full response",
|
|
153
160
|
"yearsEstimateRequired": "Estimated years required",
|
|
154
161
|
"yes": "Yes"
|