Skip to content

Commit

Permalink
feat: Implement Health Worker Registration Number Verification and Da…
Browse files Browse the repository at this point in the history
…ta Retrieval (#468)

* (feat) added the registration number

* (feat) updated the modal

* (feat) updated the modal

* (feat) updated the HIE verification with Registration number
  • Loading branch information
its-kios09 authored Nov 14, 2024
1 parent 11f386c commit baa9d70
Show file tree
Hide file tree
Showing 7 changed files with 284 additions and 112 deletions.
33 changes: 27 additions & 6 deletions packages/esm-providers-app/src/config-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,12 @@ import _default from 'react-hook-form/dist/logic/appendErrors';
export const configSchema = {
nationalIDUuid: {
_type: Type.String,
_description: ' UUID for national identification',
_description: 'UUID for national identification',
_default: '49af6cdc-7968-4abb-bf46-de10d7f4859f',
},
passportNumberUuid: {
_type: Type.String,
_description: ' UUID for national identification',
_description: 'UUID for national identification',
_default: 'be9beef6-aacc-4e1f-ac4e-5babeaa1e303',
},
licenseExpiryDateUuid: {
Expand All @@ -20,7 +20,7 @@ export const configSchema = {
licenseBodyUuid: {
_type: Type.String,
_description: 'UUID for license body',
_defualt: 'ba18bb97-d17c-4640-80d2-58e7df90ca4c',
_default: 'ba18bb97-d17c-4640-80d2-58e7df90ca4c',
},
providerNationalIdUuid: {
_type: Type.String,
Expand All @@ -32,16 +32,32 @@ export const configSchema = {
_description: 'UUID for provider licensing body',
_default: 'bcaaa67b-cc72-4662-90c2-e1e992ceda66',
},
defaultprimaryFacility: {
defaultPrimaryFacility: {
_type: Type.String,
_description: 'Default facility for a provider',
_default: '5a53dddd-b382-4245-9bf1-03bce973f24b',
},
defualtTelephonePhone: {
defaultTelephonePhone: {
_type: Type.String,
_description: 'Default Telephone number for a provider',
_default: 'b2c38640-2603-4629-aebd-3b54f33f1e3a',
},
identifierTypes: {
_type: Type.Array,
_elements: {
_type: Type.Object,
properties: {
key: { _type: Type.String },
name: { _type: Type.String },
},
},
_default: [
{ key: 'national-id', name: 'National ID' },
{ key: 'registration_number', name: 'Registration Number' },
{ key: 'passport-number', name: 'Passport Number' },
],
_description: 'List of identifier types with unique keys for each.',
},
};

export interface ConfigObject {
Expand All @@ -51,5 +67,10 @@ export interface ConfigObject {
licenseBodyUuid: string;
providerNationalIdUuid: string;
licenseNumberUuid: string;
defaultprimaryFacility: string;
defaultPrimaryFacility: string;
defaultTelephonePhone: string;
identifierTypes: Array<{
key: string;
name: string;
}>;
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { Button, Tag } from '@carbon/react';
import { displayName, ExtensionSlot } from '@openmrs/esm-framework';
import { displayName, ExtensionSlot, formatDate } from '@openmrs/esm-framework';
import { Practitioner } from '../types';
import { boolean } from 'zod';

import capitalize from 'lodash/capitalize';
interface HealthWorkerInfoProps {
label: string;
value: string | boolean | React.ReactNode;
Expand All @@ -27,10 +26,11 @@ interface HWRConfirmModalProps {

const HWRConfirmModal: React.FC<HWRConfirmModalProps> = ({ close, onConfirm, healthWorker }) => {
const { t } = useTranslation();

return (
<>
<div className="cds--modal-header">
<h3 className="cds--modal-header__heading">{t('healthWorkerRegistry', `Health worker registry`)}</h3>
<h3 className="cds--modal-header__heading">{t('healthWorkerRegistry', 'Health worker registry')}</h3>
</div>
<div className="cds--modal-content">
<p>
Expand All @@ -43,58 +43,55 @@ const HWRConfirmModal: React.FC<HWRConfirmModalProps> = ({ close, onConfirm, hea
<ExtensionSlot
style={{ display: 'flex', alignItems: 'center' }}
name="patient-photo-slot"
state={{ patientName: `${healthWorker?.name?.[0]?.given?.join(' ')} ${healthWorker?.name?.[0]?.family}` }}
state={{
patientName: healthWorker?.entry[0]?.resource?.name[0]?.text || '',
}}
/>
<div style={{ width: '100%', marginLeft: '0.625rem' }}>
<HealthWorkerInfo label={t('healthWorkerName', 'Health worker name')} value={displayName(healthWorker)} />
{healthWorker?.telecom?.map((telecom, index) => (
<HealthWorkerInfo key={index} label={telecom.system} value={telecom.use || ''} />
<HealthWorkerInfo
label={t('healthWorkerName', 'Health worker name')}
value={healthWorker?.entry[0]?.resource?.name[0]?.text}
/>

{healthWorker?.entry[0]?.resource?.telecom?.map((telecom, index) => (
<HealthWorkerInfo key={index} label={capitalize(telecom?.system)} value={telecom?.value || ''} />
))}
{healthWorker?.identifier?.map((identifier, index) => (

{healthWorker?.entry[0]?.resource?.identifier?.map((identifier, index) => (
<HealthWorkerInfo
key={index}
label={identifier.type?.coding?.map((code) => code.code).join('') || 'Unknown'}
value={identifier.value || 'Unknown'}
label={identifier.type?.coding?.map((code) => code.display).join(' ') || t('unknown', 'Unknown')}
value={identifier.value || t('unknown', 'Unknown')}
/>
))}

<HealthWorkerInfo
label={t('licenseNo', 'License Number')}
value={
healthWorker?.qualification[0]?.extension.find(
(ext) => ext.url === 'https://shr.tiberbuapps.com/fhir/StructureDefinition/current-license-number',
)?.valueString || 'Unknown'
}
/>
<HealthWorkerInfo
label={t('renewalDuration', 'Renewal duration')}
value={
healthWorker?.qualification[0]?.extension.find(
(ext) => ext.url === 'https://shr.tiberbuapps.com/fhir/StructureDefinition/license-renewal-duration',
)?.valueCoding?.display || 'Unknown'
}
label={t('renewalDate', 'Renewal Date')}
value={formatDate(
new Date(
healthWorker?.entry[0]?.resource.identifier?.find((id) =>
id.type?.coding?.some((code) => code.code === 'license-number'),
)?.period?.end || t('unknown', 'Unknown'),
),
)}
/>

<HealthWorkerInfo
label={t('licensingBody', 'Licensing Body')}
value={
healthWorker?.qualification[0]?.extension.find(
(ext) => ext.url === 'https://shr.tiberbuapps.com/fhir/StructureDefinition/licensing-body',
)?.valueString || 'Unknown'
}
/>
<HealthWorkerInfo
label={t('qualificationType', 'Qualification Type')}
value={
healthWorker?.qualification[0]?.code?.coding.find(
(coding) =>
coding.system === 'https://shr.tiberbuapps.com/fhir' && coding.display === 'Medical Doctor License',
)?.display || 'Unknown'
healthWorker?.entry[0]?.resource?.qualification?.[0]?.extension?.find(
(ext) => ext.url === 'https://hwr-kenyahie/StructureDefinition/licensing-body',
)?.valueCodeableConcept?.coding?.[0]?.display || t('unknown', 'Unknown')
}
/>

<HealthWorkerInfo
label={t('licenseValid', 'License validity')}
label={t('licenseValid', 'License Validity')}
value={
<Tag type={healthWorker?.active ? 'green' : 'red'}>
{healthWorker?.active ? 'license valid' : 'license expired'}
<Tag type={healthWorker?.entry[0]?.resource?.active ? 'green' : 'red'}>
{healthWorker?.entry[0]?.resource?.active
? t('licenseValid', 'License Valid')
: t('licenseExpired', 'License Expired')}
</Tag>
}
/>
Expand Down
22 changes: 11 additions & 11 deletions packages/esm-providers-app/src/overflow/overflow-component.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,14 @@ function CustomActionMenu({ provider }: CustomActionMenuProps) {
const { user, isLoading, error } = useProviderUser(provider.uuid);
const { t } = useTranslation();
const [syncLoading, setSyncLoading] = useState(false);
const { licenseNumberUuid, licenseExpiryDateUuid, providerNationalIdUuid } = useConfig<ConfigObject>();
const { providerIdentifierTypes } = useIdentifierTypes();
const { licenseNumberUuid, licenseExpiryDateUuid, providerNationalIdUuid, licenseBodyUuid } =
useConfig<ConfigObject>();

const providerNationalId = provider.attributes.find((attr) => attr.attributeType.uuid === providerNationalIdUuid);
const registrationNumber = provider.attributes.find((attr) => attr.attributeType.uuid === licenseBodyUuid);
if (isLoading) {
return null;
}

const handleUpdateProvider = (provider: ProviderResponse) => {
launchWorkspace('provider-register-form', {
Expand All @@ -38,26 +42,22 @@ function CustomActionMenu({ provider }: CustomActionMenuProps) {
const handleSync = async () => {
try {
setSyncLoading(true);
const healthWorker: Practitioner = await searchHealthCareWork('National id', providerNationalId.value);
const licenseNumber =
healthWorker?.qualification?.[0]?.extension?.find(
(ext) => ext.url === 'https://shr.tiberbuapps.com/fhir/StructureDefinition/current-license-number',
)?.valueString || 'Unknown';

const healthWorker: Practitioner = await searchHealthCareWork('registration_number', registrationNumber.value);
const licenseNumber = healthWorker.entry[0]?.resource.identifier?.find((id) =>
id.type?.coding?.some((code) => code.code === 'license-number'),
)?.value;
const updatableAttributes = [
{
attributeType: licenseNumberUuid,
value: licenseNumber,
},
];

await Promise.all(
updatableAttributes.map((attr) => {
const _attribute = provider.attributes.find((at) => at.attributeType.uuid === attr.attributeType)?.uuid;
if (!_attribute) {
return createProviderAttribute(attr, provider.uuid);
}

return updateProviderAttributes(
{ value: attr.value },
provider.uuid,
Expand All @@ -77,7 +77,7 @@ function CustomActionMenu({ provider }: CustomActionMenuProps) {
showSnackbar({
title: 'Failure',
kind: 'error',
subtitle: t('errorMsg', 'Failed to sync the account'),
subtitle: t('errorMsg', `Failed to sync the account ${err}`),
});
} finally {
setSyncLoading(false);
Expand Down
53 changes: 31 additions & 22 deletions packages/esm-providers-app/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,51 +69,49 @@ export interface UserRoles {
export interface Practitioner {
id: string;
meta: Metadata;
extension: ExtensionCadre[];
identifier: IdentifierType[];
active: boolean;
name: PractitionerName[];
telecom: ContactList[];
qualification: Qualification[];
entry: Entry[];
}

interface Metadata {
lastUpdated: string;
profile: string[];
}

interface ExtensionCadre {
url: string;
valueCoding?: {
system?: string;
code?: string;
display?: string;
interface Entry {
resource: {
id: string;
identifier: IdentifierType[];
active: boolean;
name: Name[];
telecom: ContactList[];
gender: string;
qualification: Qualification[];
};
valueString?: string;
}

interface IdentifierType {
use: string;
type: {
coding: {
code: string;
display: string;
system?: string;
}[];
};
value: string;
}

interface PractitionerName {
family: string;
given: string[];
prefix: string[];
period?: {
start?: string;
end?: string;
};
}

interface ContactList {
system: string;
use: string;
value?: string;
}

interface Name {
text: string;
}

interface Qualification {
extension: QualificationExtension[];
code: {
Expand All @@ -123,6 +121,10 @@ interface Qualification {
display: string;
}[];
};
period: {
start: string;
end: string;
};
}

interface QualificationExtension {
Expand All @@ -133,6 +135,13 @@ interface QualificationExtension {
display?: string;
};
valueString?: string;
valueCodeableConcept?: {
coding: {
system: string;
code: string;
display: string;
}[];
};
}

export interface User {
Expand Down
Loading

0 comments on commit baa9d70

Please sign in to comment.