Skip to content

Commit

Permalink
(fix) fixed missing dependants on some national IDs (#56)
Browse files Browse the repository at this point in the history
* (fix) fixed missing dependants on some national IDs

* (fix) fixed merge conflicts
  • Loading branch information
its-kios09 authored Nov 25, 2024
1 parent 8697927 commit 629133a
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 44 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,13 @@ const DependentInfo: React.FC<{ dependents: any[] }> = ({ dependents }) => {
const name = dependent?.name?.text;
const relationship =
dependent?.relationship?.[0]?.coding?.[0]?.display || t('unknownRelationship', 'Unknown');

const nationalID = dependent?.extension?.find(
(ext) =>
ext.url === 'http://cr.tiberbu.app/fhir/StructureDefinition/dependants-id-number' &&
ext.valueIdentifier?.type?.coding?.[0]?.code === 'national-id',
(ext) => ext?.valueIdentifier?.type?.coding?.some((coding) => coding.code === 'national-id'),
)?.valueIdentifier?.value;

const birthCertificate = dependent?.extension?.find(
(ext) =>
ext.url === 'http://cr.tiberbu.app/fhir/StructureDefinition/dependants-id-number' &&
ext.valueIdentifier?.type?.coding?.[0]?.code === 'birth-certificate-number',
(ext) => ext?.valueIdentifier?.type?.coding?.some((coding) => coding.code === 'birth-certificate'),
)?.valueIdentifier?.value;

const primaryIdentifier = nationalID || birthCertificate;
Expand All @@ -34,7 +32,7 @@ const DependentInfo: React.FC<{ dependents: any[] }> = ({ dependents }) => {
<div key={index} className={styles.dependentInfo}>
<PatientInfo label={t('name', 'Name')} value={name} />
<PatientInfo label={t('relationship', 'Relationship')} value={relationship} />
<PatientInfo label={identifierLabel} value={primaryIdentifier} />
{primaryIdentifier && <PatientInfo label={identifierLabel} value={primaryIdentifier} />}
</div>
);
})}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { type RegistrationConfig } from '../../config-schema';
import { useForm, Controller, type SubmitHandler } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { fetchPatientFromHIE, mapHIEPatientToFormValues } from './hie-resource';
import { type HIEPatient } from './hie-types';
import { type HIEPatientResponse, type HIEPatient } from './hie-types';

type HIEClientRegistryProps = {
props: FormikProps<FormValues>;
Expand Down Expand Up @@ -43,16 +43,18 @@ const HIEClientRegistry: React.FC<HIEClientRegistryProps> = ({ setInitialFormVal
try {
const hieClientRegistry = await fetchPatientFromHIE(data.identifierType, data.identifierValue);

if (hieClientRegistry && hieClientRegistry.resourceType === 'Patient') {
if (hieClientRegistry && hieClientRegistry.resourceType === 'Bundle') {
const dispose = showModal('hie-confirmation-modal', {
patient: hieClientRegistry,
closeModal: () => dispose(),
onUseValues: () =>
setInitialFormValues(mapHIEPatientToFormValues(hieClientRegistry as HIEPatient, props.values)),
setInitialFormValues(
mapHIEPatientToFormValues(hieClientRegistry as unknown as HIEPatientResponse, props.values),
),
});
}

if (hieClientRegistry && hieClientRegistry.resourceType === 'OperationOutcome') {
if (hieClientRegistry && hieClientRegistry?.resourceType === 'OperationOutcome') {
const issueMessage = hieClientRegistry?.['issue']?.map((issue) => issue.diagnostics).join(', ');
const dispose = showModal('empty-client-registry-modal', {
onConfirm: () => dispose(),
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import capitalize from 'lodash-es/capitalize';
import { type PatientIdentifierValue, type FormValues } from '../../patient-registration/patient-registration.types';
import { type MapperConfig, type HIEPatient, type ErrorResponse } from './hie-types';
import { type MapperConfig, type HIEPatient, type ErrorResponse, type HIEPatientResponse } from './hie-types';
import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
import { v4 } from 'uuid';
import { z } from 'zod';
Expand All @@ -13,7 +13,6 @@ class HealthInformationExchangeClient<T> {
async fetchResource(resourceType: string, params: Record<string, string>): Promise<T> {
const [identifierType, identifierValue] = Object.entries(params)[0];
const url = `${restBaseUrl}/kenyaemr/getSHAPatient/${identifierValue}/${identifierType}`;

const response = await openmrsFetch(url);
return response.json();
}
Expand All @@ -35,19 +34,19 @@ class Mapper<T, U> {
/**
* Maps HIEPatient objects to FormValues objects.
*/
class PatientMapper extends Mapper<HIEPatient, FormValues> {
mapHIEPatientToFormValues(hiePatient: HIEPatient, currentFormValues: FormValues): FormValues {
class PatientMapper extends Mapper<HIEPatientResponse, FormValues> {
mapHIEPatientToFormValues(hiePatient: HIEPatientResponse, currentFormValues: FormValues): FormValues {
const { familyName, givenName, middleName } = getPatientName(hiePatient);
const telecom = hiePatient.telecom || [];
const telecom = hiePatient?.entry[0]?.resource.telecom || [];

const telecomAttributes = this.mapTelecomToAttributes(telecom);
const updatedIdentifiers = this.mapIdentifiers(hiePatient, currentFormValues);
const extensionAddressEntries = this.mapExtensionsToAddress(hiePatient.extension);
const extensionAddressEntries = this.mapExtensionsToAddress(hiePatient?.entry[0]?.resource.extension);

return {
isDead: hiePatient.deceasedBoolean || false,
gender: hiePatient.gender || '',
birthdate: hiePatient.birthDate || '',
isDead: hiePatient?.entry[0]?.resource?.active || false,
gender: hiePatient?.entry[0]?.resource.gender || '',
birthdate: hiePatient?.entry[0]?.resource?.birthDate || '',
givenName,
familyName,
telephoneNumber: telecom.find((t) => t.system === 'phone')?.value || '',
Expand All @@ -73,19 +72,19 @@ class PatientMapper extends Mapper<HIEPatient, FormValues> {
}

private mapIdentifiers(
hiePatient: HIEPatient,
hiePatient: HIEPatientResponse,
currentFormValues: FormValues,
): Record<string, PatientIdentifierValue> {
const updatedIdentifiers: Record<string, PatientIdentifierValue> = { ...currentFormValues.identifiers };
// Map Social Health Authority Unique Identification Number to HIE Patient ID
// See https://github.com/palladiumkenya/openmrs-module-kenyaemr/blob/1e1d281eaba8041c45318e60ca0730449b8e4197/api/src/main/distro/metadata/identifierTypes.xml#L33
updatedIdentifiers.socialHealthAuthorityIdentificationNumber = {
...currentFormValues.identifiers['socialHealthAuthorityIdentificationNumber'],
identifierValue: hiePatient.id,
identifierValue: hiePatient?.entry[0]?.resource?.id,
};

// Map fhir.Patient.Identifier to identifiers
hiePatient.identifier?.forEach((identifier: fhir.Identifier) => {
hiePatient.entry[0]?.resource.identifier?.forEach((identifier: fhir.Identifier) => {
const identifierType = identifier.type?.coding?.[0]?.code;
const mappedIdentifierType = this.convertToCamelCase(identifierType);
const identifierValue = identifier.value;
Expand Down Expand Up @@ -159,7 +158,10 @@ export const fetchPatientFromHIE = async (
return hieApiClient.fetchResource('Patient', { [identifierType]: identifierValue });
};

export const mapHIEPatientToFormValues = (hiePatient: HIEPatient, currentFormValues: FormValues): FormValues => {
export const mapHIEPatientToFormValues = (
hiePatient: HIEPatientResponse,
currentFormValues: FormValues,
): FormValues => {
return patientMapper.mapHIEPatientToFormValues(hiePatient, currentFormValues);
};

Expand All @@ -169,7 +171,7 @@ export const mapHIEPatientToFormValues = (hiePatient: HIEPatient, currentFormVal
* @returns {string} - The masked data
*/
export const maskData = (data: string): string => {
const maskedData = data.slice(0, 2) + '*'.repeat(data.length - 2);
const maskedData = data.slice(0, 2) + '*'.repeat(Math.max(0, data.length - 2));
return maskedData;
};

Expand All @@ -178,10 +180,13 @@ export const maskData = (data: string): string => {
* @param patient {fhir.Patient} - The FHIR Patient resource
* @returns {object} - The patient name
*/
export const getPatientName = (patient: fhir.Patient) => {
const familyName = patient?.name[0]?.['family'] ?? '';
const givenName = patient.name[0]?.['given']?.[0]?.split(' ')?.[0] ?? '';
const middleName = patient.name[0]?.['given']?.[0]?.replace(givenName, '')?.trim() ?? '';
export const getPatientName = (patient: HIEPatientResponse) => {
const familyName = patient?.entry?.[0]?.resource?.name?.[0]?.family ?? ''; // Safely access the family name
const givenNames = patient?.entry?.[0]?.resource?.name?.[0]?.given ?? []; // Safely access the given names array

const givenName = givenNames?.[0] ?? ''; // The first item is the given name (first name)
const middleName = givenNames.slice(1).join(' ').trim(); // Combine all other given names as middle name(s)

return { familyName, givenName, middleName };
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,106 @@ export type HIEPatient = fhir.Patient & {
}>;
};

export interface HIEPatientResponse {
id: string;
meta: Metadata;
link: Link[];
entry: Entry[];
}

interface Metadata {
lastUpdated: string;
}

interface Link {
relation: string;
url: string;
}

interface Entry {
resource: Resource;
}

interface Resource {
id: string;
extension: Extension[];
identifier: Identifier[];
active: boolean;
name: Name[];
telecom: Telecom[];
birthDate: string;
address: Address[];
gender: string;
maritalStatus: MaritalStatus;
contact: Contact[];
}

interface Extension {
url: string;
valueString: string;
}

interface Identifier {
type: CodingType;
value: string;
}

interface CodingType {
coding: Coding[];
}

interface Coding {
system?: string;
code: string;
display: string;
}

interface Name {
text: string;
family: string;
given: string[];
}

interface Telecom {
system: string;
value?: string;
}

interface Address {
extension: AddressExtension[];
city: string;
country: string;
}

interface AddressExtension {
url: string;
valueString: string;
}

interface MaritalStatus {
coding: Coding[];
}

interface Contact {
id: string;
extension: ContactExtension[];
relationship: Relationship[];
name: Name;
telecom: Telecom[];
address: Address;
gender: string;
}

interface ContactExtension {
url: string;
valueIdentifier?: Identifier;
valueString?: string;
}

interface Relationship {
coding: Coding[];
}

export type APIClientConfig = {
baseUrl: string;
credentials: string;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Button, ModalBody, ModalFooter, ModalHeader } from '@carbon/react';
import React, { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { type HIEPatient } from '../hie-types';
import { type HIEPatientResponse, type HIEPatient } from '../hie-types';
import styles from './confirm-hie.scss';
import { authorizationFormSchema, generateOTP, getPatientName, persistOTP, sendOtp, verifyOtp } from '../hie-resource';
import HIEPatientDetailPreview from './hie-patient-detail-preview.component';
Expand All @@ -14,17 +14,19 @@ import { showSnackbar } from '@openmrs/esm-framework';

interface HIEConfirmationModalProps {
closeModal: () => void;
patient: HIEPatient;
patient: HIEPatientResponse;
onUseValues: () => void;
}

const HIEConfirmationModal: React.FC<HIEConfirmationModalProps> = ({ closeModal, patient, onUseValues }) => {
const { t } = useTranslation();
const [mode, setMode] = useState<'authorization' | 'preview'>('preview');
const [status, setStatus] = useState<'loadingOtp' | 'otpSendSuccessfull' | 'otpFetchError'>();
const phoneNumber = patient?.telecom?.find((num) => num.value)?.value;
const phoneNumber = patient?.entry[0]?.resource.telecom?.find((num) => num.system === 'phone')?.value;
const getidentifier = (code: string) =>
patient?.identifier?.find((identifier) => identifier?.type?.coding?.some((coding) => coding?.code === code));
patient?.entry[0]?.resource.identifier?.find(
(identifier) => identifier?.type?.coding?.some((coding) => coding?.code === code),
);
const patientId = patient?.id ?? getidentifier('SHA-number')?.value;
const form = useForm<z.infer<typeof authorizationFormSchema>>({
defaultValues: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,19 @@ import DependentInfo from '../dependants/dependants.component';
import { getPatientName, maskData } from '../hie-resource';
import PatientInfo from '../patient-info/patient-info.component';
import styles from './confirm-hie.scss';
import { type HIEPatientResponse } from '../hie-types';

type HIEPatientDetailPreviewProps = {
patient: fhir.Patient;
patient: HIEPatientResponse;
};

const HIEPatientDetailPreview: React.FC<HIEPatientDetailPreviewProps> = ({ patient }) => {
const { familyName, givenName, middleName } = getPatientName(patient);
const { t } = useTranslation();
const getidentifier = (code: string) =>
patient?.identifier?.find((identifier) => identifier?.type?.coding?.some((coding) => coding?.code === code));
patient?.entry[0]?.resource.identifier?.find(
(identifier) => identifier?.type?.coding?.some((coding) => coding?.code === code),
);

return (
<>
Expand All @@ -41,21 +44,22 @@ const HIEPatientDetailPreview: React.FC<HIEPatientDetailPreviewProps> = ({ patie
}
/>

<PatientInfo label={t('age', 'Age')} value={age(patient?.birthDate)} />
<PatientInfo label={t('dateOfBirth', 'Date of birth')} value={formatDate(new Date(patient?.birthDate))} />
<PatientInfo label={t('gender', 'Gender')} value={capitalize(patient?.gender)} />
<PatientInfo label={t('age', 'Age')} value={age(patient?.entry[0]?.resource.birthDate)} />
<PatientInfo
label={t('dateOfBirth', 'Date of birth')}
value={formatDate(new Date(patient?.entry[0]?.resource?.birthDate))}
/>
<PatientInfo label={t('gender', 'Gender')} value={capitalize(patient?.entry[0]?.resource.gender)} />
<PatientInfo
label={t('maritalStatus', 'Marital status')}
value={patient?.maritalStatus?.coding?.map((m) => m.code).join('')}
value={patient?.entry[0]?.resource.maritalStatus?.coding?.map((m) => m.code).join('')}
/>

{(!patient?.contact || patient?.contact.length === 0) && (
<PatientInfo label={t('dependents', 'Dependents')} value="--" />
)}
{!patient?.entry[0]?.resource.contact && <PatientInfo label={t('dependents', 'Dependents')} value="--" />}
</div>
</div>

<DependentInfo dependents={patient?.contact} />
<DependentInfo dependents={patient?.entry[0]?.resource.contact} />

<div>
<Accordion>
Expand Down
2 changes: 1 addition & 1 deletion packages/esm-patient-registration-app/src/config-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -416,7 +416,7 @@ export const esmPatientRegistrationSchema = {
},
},
_default: [
{ identifierType: 'National ID', identifierValue: 'national-id' },
{ identifierType: 'National ID', identifierValue: 'National ID' },
{ identifierType: 'Passport Number', identifierValue: 'passport-number' },
{ identifierType: 'Birth Certificate Number', identifierValue: 'birth-certificate-number' },
{ identifierType: 'Alien ID Number', identifierValue: 'alien-id-number' },
Expand Down

0 comments on commit 629133a

Please sign in to comment.