From 80ef84c5637486bd326f14caab98c256993e371a Mon Sep 17 00:00:00 2001 From: Bhargav kodali <115476530+kb019@users.noreply.github.com> Date: Thu, 25 Jul 2024 05:02:15 +0530 Subject: [PATCH] (feat) O3-3243: Ward App - add configurable extension to include patient identifier (#1197) * configure patient identifier * correct config syntax * fix test --------- Co-authored-by: chibongho --- .../src/queue-table/queue-table.test.tsx | 2 +- packages/esm-ward-app/src/config-schema.ts | 45 +++++++++++++++++- packages/esm-ward-app/src/types/index.ts | 1 + .../row-elements/ward-patient-identifier.tsx | 46 +++++++++++++++++++ .../ward-patient-card-row.resources.tsx | 4 ++ 5 files changed, 95 insertions(+), 3 deletions(-) create mode 100644 packages/esm-ward-app/src/ward-patient-card/row-elements/ward-patient-identifier.tsx diff --git a/packages/esm-service-queues-app/src/queue-table/queue-table.test.tsx b/packages/esm-service-queues-app/src/queue-table/queue-table.test.tsx index 702f1f1f2..4fdcd03e2 100644 --- a/packages/esm-service-queues-app/src/queue-table/queue-table.test.tsx +++ b/packages/esm-service-queues-app/src/queue-table/queue-table.test.tsx @@ -80,7 +80,7 @@ describe('QueueTable', () => { renderWithSwr(); for (const entry of mockQueueEntries) { - const patientName = entry.patient.display; + const patientName = entry.patient.person.display; const row = screen.getByText(patientName).closest('tr'); expect(within(row).getByText(entry.status.display)).toBeInTheDocument(); diff --git a/packages/esm-ward-app/src/config-schema.ts b/packages/esm-ward-app/src/config-schema.ts index aaca08bc4..3afcacd43 100644 --- a/packages/esm-ward-app/src/config-schema.ts +++ b/packages/esm-ward-app/src/config-schema.ts @@ -6,19 +6,23 @@ const defaultWardPatientCard: WardPatientCardDefinition = { rows: [ { rowType: 'header', - elements: ['bed-number', 'patient-name', 'patient-age', 'patient-address'], + elements: ['bed-number', 'patient-name', 'patient-age', 'patient-address', 'patient-identifier'], }, ], appliedTo: null, }; const defaultPatientAddressFields: Array = ['cityVillage', 'country']; - +const defaultIdentifierTypeUuid = null; +const defaultLabel = null; export const defaultPatientCardElementConfig: PatientCardElementConfig = { address: { addressFields: defaultPatientAddressFields, }, obs: null, + identifier: { + identifierTypeUuid: defaultIdentifierTypeUuid, + }, codedObsTags: null, }; @@ -27,6 +31,7 @@ export const builtInPatientCardElements: PatientCardElementType[] = [ 'patient-name', 'patient-age', 'patient-address', + 'patient-identifier', ]; export const configSchema: ConfigSchema = { @@ -91,6 +96,25 @@ export const configSchema: ConfigSchema = { _default: false, }, }, + identifier: { + _description: 'Config for the patientCardElementType "patient-identifier"', + identifierTypeUuid: { + _type: Type.UUID, + _description: 'The UUID of the identifier type to display', + _default: defaultIdentifierTypeUuid, + }, + label: { + _type: Type.String, + _description: + 'the custom label or i18n key to the translated label to display for patient identifier. If not provided, defaults to the patient-identifier name.', + _default: defaultLabel, + }, + labelI18nModule: { + _type: Type.String, + _description: 'Optional. The custom module to use for translation of the label', + _default: null, + }, + }, codedObsTags: { _description: 'Config for the patientCardElementType "patient-coded-obs-tags"', conceptUuid: { @@ -251,6 +275,22 @@ export interface PatientObsElementConfig { onlyWithinCurrentVisit?: boolean; } +export interface PatientIdentifierElementConfig { + /** + * By default the preferred patient identifier is chosen,but + * if uuid is given the identifier corresponding to uuid is displayed + */ + identifierTypeUuid: string | null; + /** + * Optional. The custom label or i18n key to the translated label to display for patient identifier. If not provided, defaults to the patient-identifier name. + * (Note that this can be set to an empty string to not show a label) + */ + label?: string; + /** + * Optional. The custom module to use for translation of the label + */ + labelI18nModule?: string; +} export interface PatientCodedObsTagsElementConfig { /** * Required. Identifies the concept to use to identify the desired observations. @@ -298,5 +338,6 @@ export interface PatientCodedObsTagsElementConfig { export type PatientCardElementConfig = { address: PatientAddressElementConfig; obs: PatientObsElementConfig; + identifier: PatientIdentifierElementConfig; codedObsTags: PatientCodedObsTagsElementConfig; }; diff --git a/packages/esm-ward-app/src/types/index.ts b/packages/esm-ward-app/src/types/index.ts index 9d5437f6b..c89a22a71 100644 --- a/packages/esm-ward-app/src/types/index.ts +++ b/packages/esm-ward-app/src/types/index.ts @@ -25,6 +25,7 @@ export const patientCardElementTypes = [ 'patient-obs', 'patient-coded-obs-tags', 'admission-time', + 'patient-identifier', ] as const; export type PatientCardElementType = (typeof patientCardElementTypes)[number]; diff --git a/packages/esm-ward-app/src/ward-patient-card/row-elements/ward-patient-identifier.tsx b/packages/esm-ward-app/src/ward-patient-card/row-elements/ward-patient-identifier.tsx new file mode 100644 index 000000000..9011358da --- /dev/null +++ b/packages/esm-ward-app/src/ward-patient-card/row-elements/ward-patient-identifier.tsx @@ -0,0 +1,46 @@ +import React from 'react'; +import { type WardPatientCardElement } from '../../types'; +import { type PatientCardElementConfig } from '../../config-schema'; +import { Tag } from '@carbon/react'; +import { translateFrom, type PatientIdentifier } from '@openmrs/esm-framework'; +import { moduleName } from '../../constant'; +import { useTranslation } from 'react-i18next'; + +//sort the identifiers by preferred first.The identifier with value of true +//takes precedence over false. if both identifiers have same preferred value +//sort them by (dateChanged or dateCreated) in descending order +const identifierCompareFunction = (pi1: PatientIdentifier, pi2: PatientIdentifier) => { + let comp = (pi2.preferred ? 1 : 0) - (pi1.preferred ? 1 : 0); + + if (comp == 0) { + const date1 = pi1.auditInfo.dateChanged ?? pi1.auditInfo.dateCreated; + const date2 = pi2.auditInfo.dateChanged ?? pi2.auditInfo.dateCreated; + comp = date2.localeCompare(date1); + } + return comp; +}; + +const wardPatientIdentifier = (config: PatientCardElementConfig) => { + const WardPatientIdentifier: WardPatientCardElement = ({ patient }) => { + const { t } = useTranslation(); + const { identifier } = config; + const { identifierTypeUuid, labelI18nModule: labelModule, label } = identifier; + const patientIdentifiers = patient.identifiers.filter( + (patientIdentifier: PatientIdentifier) => + identifierTypeUuid == null || patientIdentifier.identifierType?.uuid === identifierTypeUuid, + ); + patientIdentifiers.sort(identifierCompareFunction); + const patientIdentifier = patientIdentifiers[0]; + const labelToDisplay = + label != null ? translateFrom(labelModule ?? moduleName, label) : patientIdentifier?.identifierType?.name; + return ( +
+ {labelToDisplay ? {t('identifierTypelabel', '{{label}}:', { label: labelToDisplay })} : <>} + {patientIdentifier?.identifier} +
+ ); + }; + return WardPatientIdentifier; +}; + +export default wardPatientIdentifier; diff --git a/packages/esm-ward-app/src/ward-patient-card/ward-patient-card-row.resources.tsx b/packages/esm-ward-app/src/ward-patient-card/ward-patient-card-row.resources.tsx index 1697f5466..aeebaa5ce 100644 --- a/packages/esm-ward-app/src/ward-patient-card/ward-patient-card-row.resources.tsx +++ b/packages/esm-ward-app/src/ward-patient-card/ward-patient-card-row.resources.tsx @@ -16,6 +16,8 @@ import styles from './ward-patient-card.scss'; import wardPatientObs from './row-elements/ward-patient-obs'; import wardPatientCodedObsTags from './row-elements/ward-patient-coded-obs-tags'; +import wardPatientIdentifier from './row-elements/ward-patient-identifier'; + export function usePatientCardRows(location: string) { const { wardPatientCards } = useConfig(); const patientCardRows = useMemo(() => { @@ -85,6 +87,8 @@ function getPatientCardElementFromDefinition( case 'patient-obs': { return wardPatientObs(config.obs); } + case 'patient-identifier': + return wardPatientIdentifier(config); case 'patient-coded-obs-tags': { return wardPatientCodedObsTags(config.codedObsTags); }