From 969cf9ac69c8f9b8c91fe15faa1cf12e621cf7f3 Mon Sep 17 00:00:00 2001 From: Laurent Ouma <98098891+Omoshlawi@users.noreply.github.com> Date: Tue, 3 Dec 2024 19:02:48 +0400 Subject: [PATCH] Add ccc and patient name to manifest samples table and flu sample types (#498) * Added clickable patient name together with cc number * Cleaned styles for form * Showing kdod dentifier for kdod sites and ccc identifier type for none kdod sites.Added sample types for FLU manifest type --- .../esm-lab-manifest-app/src/config-schema.ts | 21 ++++++- .../src/forms/lab-manifest-form.scss | 62 +++++++++++-------- .../src/forms/lab-manifest-form.workspace.tsx | 3 + .../lab-manifest-orders-to-manifest.modal.tsx | 30 ++++++--- .../sample-delete-confirm-dialog.modal.tsx | 15 ++++- .../src/hooks/useIsKDoDSite.ts | 16 +++++ .../src/hooks/usePatient.ts | 16 +++++ .../tables/lab-manifest-samples.component.tsx | 27 +++++++- .../tables/patient-ccc-no-cell.component.tsx | 28 +++++++++ .../tables/patient-name-cell.component.tsx | 29 +++++++++ 10 files changed, 205 insertions(+), 42 deletions(-) create mode 100644 packages/esm-lab-manifest-app/src/hooks/useIsKDoDSite.ts create mode 100644 packages/esm-lab-manifest-app/src/hooks/usePatient.ts create mode 100644 packages/esm-lab-manifest-app/src/tables/patient-ccc-no-cell.component.tsx create mode 100644 packages/esm-lab-manifest-app/src/tables/patient-name-cell.component.tsx diff --git a/packages/esm-lab-manifest-app/src/config-schema.ts b/packages/esm-lab-manifest-app/src/config-schema.ts index 53102553..54488980 100644 --- a/packages/esm-lab-manifest-app/src/config-schema.ts +++ b/packages/esm-lab-manifest-app/src/config-schema.ts @@ -15,7 +15,7 @@ export const configSchema = { }, { id: 3, - type: 'FLU', + type: 'Influenza', }, ], }, @@ -23,13 +23,28 @@ export const configSchema = { _type: Type.Array, _description: 'List of sample types and list of manifest type id it applies', _default: [ - { sampleType: 'Frozen plasma', labManifestType: ['2', '3'] }, - { sampleType: 'Whole Blood', labManifestType: ['2', '3'] }, + { sampleType: 'Frozen plasma', labManifestType: ['2'] }, + { sampleType: 'Whole Blood', labManifestType: ['2'] }, { sampleType: 'DBS', labManifestType: ['1'] }, + { sampleType: 'Nasal Swab', labManifestType: ['3'] }, + { sampleType: 'NP', labManifestType: ['3'] }, + { sampleType: 'OP', labManifestType: ['3'] }, ], }, + patientIdentifierTypes: { + _type: Type.Object, + _description: 'Patient identifier type uuids', + _default: { + cccNumberIdentifierType: '05ee9cf4-7242-4a17-b4d4-00f707265c8a', + kdodIdentifierType: 'b51ffe55-3e76-44f8-89a2-14f5eaf11079', + }, + }, }; export interface LabManifestConfig { labmanifestTypes: Array<{ id: number; type: string }>; sampleTypes: Array<{ sampleType: string; labManifestType: Array }>; + patientIdentifierTypes: { + cccNumberIdentifierType: string; + kdodIdentifierType: string; + }; } diff --git a/packages/esm-lab-manifest-app/src/forms/lab-manifest-form.scss b/packages/esm-lab-manifest-app/src/forms/lab-manifest-form.scss index c252e85f..b87ed36c 100644 --- a/packages/esm-lab-manifest-app/src/forms/lab-manifest-form.scss +++ b/packages/esm-lab-manifest-app/src/forms/lab-manifest-form.scss @@ -1,22 +1,22 @@ -@use '@carbon/styles/scss/spacing'; -@use '@carbon/styles/scss/type'; +@use '@carbon/type'; @use '@carbon/layout'; -@import '~@openmrs/esm-styleguide/src/vars'; +@use '@carbon/colors'; .heading { @include type.type-style('heading-compact-01'); - margin: spacing.$spacing-05 0 spacing.$spacing-05; + margin: layout.$spacing-05 0 layout.$spacing-05; } .warningContainer { - background-color: $carbon--red-50; - padding: spacing.$spacing-04; - margin: spacing.$spacing-03 0 spacing.$spacing-03; + background-color: colors.$red-50; + padding: layout.$spacing-04; + margin: layout.$spacing-03 0 layout.$spacing-03; display: flex; justify-content: space-between; + .warning { @include type.type-style('heading-01'); - color: $ui-05; + color: colors.$gray-100; } } @@ -28,33 +28,32 @@ } .grid { - margin: 0 spacing.$spacing-05; + margin: 0 layout.$spacing-05; padding: 0rem; } .input { - margin-top: spacing.$spacing-05; + margin-top: layout.$spacing-05; } .inputRow { - margin-top: spacing.$spacing-05; - width: 50%; // Adjust width as per your design requirements + margin-top: layout.$spacing-05; + display: flex; + flex: 1; + } .datePickersRow { display: flex; flex-wrap: nowrap; - gap: spacing.$spacing-05; // Adjust gap between columns + gap: layout.$spacing-05; // Adjust gap between columns align-items: center; width: 100%; } -.datePickerInput { - flex-grow: 1; -} .button { - height: spacing.$spacing-10; + height: layout.$spacing-10; display: flex; align-content: flex-start; align-items: baseline; @@ -63,11 +62,11 @@ .buttonSet { padding: 0rem; - margin-top: spacing.$spacing-05; + margin-top: layout.$spacing-05; display: flex; justify-content: space-between; width: 100%; - margin-bottom: spacing.$spacing-05; + margin-bottom: layout.$spacing-05; } .datesContainer { @@ -76,9 +75,11 @@ width: 100%; display: flex; } + .inlineActions { display: flex; - gap: spacing.$spacing-05; /* Adjust the spacing as needed */ + gap: layout.$spacing-05; + /* Adjust the spacing as needed */ } .formTitle { @@ -86,7 +87,7 @@ display: flex; align-items: center; justify-content: space-between; - margin: spacing.$spacing-05; + margin: layout.$spacing-05; row-gap: 1.5rem; position: relative; @@ -100,7 +101,7 @@ left: 0; } - & > span { + &>span { @include type.type-style('body-01'); } } @@ -115,14 +116,21 @@ } .buttonSet { - padding: spacing.$spacing-06 spacing.$spacing-05; - background-color: $ui-02; + padding: layout.$spacing-06 layout.$spacing-05; + background-color: colors.$white; justify-content: flex-end; - gap: spacing.$spacing-05; + gap: layout.$spacing-05; } } .previewContainer { - margin-top: spacing.$spacing-05; - margin-bottom: spacing.$spacing-05; + margin-top: layout.$spacing-05; + margin-bottom: layout.$spacing-05; } + +.datePickerInput span, +.datePickerInput div, +.datePickerInput input, +.datePickerInput { + min-width: 100%; +} \ No newline at end of file diff --git a/packages/esm-lab-manifest-app/src/forms/lab-manifest-form.workspace.tsx b/packages/esm-lab-manifest-app/src/forms/lab-manifest-form.workspace.tsx index 06820b24..5d7b53c8 100644 --- a/packages/esm-lab-manifest-app/src/forms/lab-manifest-form.workspace.tsx +++ b/packages/esm-lab-manifest-app/src/forms/lab-manifest-form.workspace.tsx @@ -76,6 +76,7 @@ const LabManifestForm: React.FC = ({ closeWorkspace, manif dateFormat="d/m/Y" id="startDate" datePickerType="single" + className={styles.datePickerInput} invalid={form.formState.errors[field.name]?.message} invalidText={form.formState.errors[field.name]?.message}> = ({ closeWorkspace, manif = ({ closeWorkspace, manif dateFormat="d/m/Y" id="dispatchDate" datePickerType="single" + className={styles.datePickerInput} {...field} invalid={form.formState.errors[field.name]?.message} invalidText={form.formState.errors[field.name]?.message}> diff --git a/packages/esm-lab-manifest-app/src/forms/lab-manifest-orders-to-manifest.modal.tsx b/packages/esm-lab-manifest-app/src/forms/lab-manifest-orders-to-manifest.modal.tsx index e88192cb..59f79c12 100644 --- a/packages/esm-lab-manifest-app/src/forms/lab-manifest-orders-to-manifest.modal.tsx +++ b/packages/esm-lab-manifest-app/src/forms/lab-manifest-orders-to-manifest.modal.tsx @@ -17,8 +17,8 @@ import { showSnackbar, useConfig } from '@openmrs/esm-framework'; import React from 'react'; import { Controller, useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; -import { mutate } from 'swr'; import { z } from 'zod'; +import { LabManifestConfig } from '../config-schema'; import { useLabManifest } from '../hooks'; import { addOrderToManifest, @@ -28,7 +28,6 @@ import { import { ActiveRequestOrder } from '../types'; import ActiveOrdersSelectionPreview from './active-order-selection-preview'; import styles from './lab-manifest-form.scss'; -import { LabManifestConfig } from '../config-schema'; interface LabManifestOrdersToManifestFormProps { onClose: () => void; @@ -54,7 +53,6 @@ const LabManifestOrdersToManifestForm: React.FC { const { t } = useTranslation(); const form = useForm({ - defaultValues: {}, resolver: zodResolver(labManifestOrderToManifestFormSchema), }); const { sampleTypes } = useConfig(); @@ -67,15 +65,27 @@ const LabManifestOrdersToManifestForm: React.FC { if (res.status === 'fulfilled') { - showSnackbar({ title: 'Success', kind: 'success', subtitle: 'Order added succesfully' }); + showSnackbar({ + title: 'Success', + kind: 'success', + subtitle: t('manifestorderAddSuccess', 'Order added succesfully'), + }); } else { - showSnackbar({ title: 'Failure', kind: 'error', subtitle: 'Error adding order to the manifest' }); + showSnackbar({ + title: 'Failure', + kind: 'error', + subtitle: t('manifestOrderError', 'Error adding order to the manifest') + ` ${res.reason}`, + }); } }); mutateManifestLinks(manifest?.uuid, manifest?.manifestStatus); onClose(); } catch (error) { - showSnackbar({ title: 'Failure', kind: 'error', subtitle: 'Error adding orders to the manifest' }); + showSnackbar({ + title: 'Failure', + kind: 'error', + subtitle: t('manifestOrderError', 'Error adding order to the manifest') + ` ${error?.message}`, + }); } }; @@ -112,12 +122,13 @@ const LabManifestOrdersToManifestForm: React.FC - + ( )} /> - + ( )} diff --git a/packages/esm-lab-manifest-app/src/forms/sample-delete-confirm-dialog.modal.tsx b/packages/esm-lab-manifest-app/src/forms/sample-delete-confirm-dialog.modal.tsx index f0a54d2d..82317103 100644 --- a/packages/esm-lab-manifest-app/src/forms/sample-delete-confirm-dialog.modal.tsx +++ b/packages/esm-lab-manifest-app/src/forms/sample-delete-confirm-dialog.modal.tsx @@ -19,6 +19,8 @@ import React from 'react'; import { useTranslation } from 'react-i18next'; import styles from '../tables/lab-manifest-table.scss'; import { LabManifestSample } from '../types'; +import PatientNameCell from '../tables/patient-name-cell.component'; +import PatientCCCNumbercell from '../tables/patient-ccc-no-cell.component'; interface SampleDeleteConfirmDialogProps { onClose: () => void; @@ -30,7 +32,11 @@ const SampleDeleteConfirmDialog: React.FC = ({ o const headers = [ { - header: t('patientIdentifier', 'Patient Identifier'), + header: t('patient', 'Patient'), + key: 'patient', + }, + { + header: t('cccKDODNumber', 'CCC/KDOD Number'), key: 'cccKDODNumber', }, { @@ -62,7 +68,12 @@ const SampleDeleteConfirmDialog: React.FC = ({ o sampleType: sample.sampleType ?? '--', status: sample.status, batchNumber: sample.batchNumber ?? '--', - cccKDODNumber: sample?.order?.patient?.identifiers[0]?.identifier ?? '--', + patient: sample?.order?.patient?.uuid ? : '--', + cccKDODNumber: sample?.order?.patient?.uuid ? ( + + ) : ( + '--' + ), dateRequested: sample.dateSent ? formatDate(parseDate(sample.dateSent)) : '--', resultDate: sample.resultDate ? formatDate(parseDate(sample.resultDate)) : '--', result: sample.result ?? '--', diff --git a/packages/esm-lab-manifest-app/src/hooks/useIsKDoDSite.ts b/packages/esm-lab-manifest-app/src/hooks/useIsKDoDSite.ts new file mode 100644 index 00000000..afde16f3 --- /dev/null +++ b/packages/esm-lab-manifest-app/src/hooks/useIsKDoDSite.ts @@ -0,0 +1,16 @@ +import { FetchResponse, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework'; +import React from 'react'; +import useSWR from 'swr'; + +const useIsKDoDSite = () => { + const key = 'kenyaemr.isKDoD'; + const customeRep = 'custom:(property,value)'; + const url = `${restBaseUrl}/systemsetting?v=${customeRep}&q=${key}`; + const { data, error, isLoading } = useSWR }>>( + url, + openmrsFetch, + ); + return { isKDoDSite: data?.data?.results?.find((res) => res?.property === key)?.value === 'true', isLoading, error }; +}; + +export default useIsKDoDSite; diff --git a/packages/esm-lab-manifest-app/src/hooks/usePatient.ts b/packages/esm-lab-manifest-app/src/hooks/usePatient.ts new file mode 100644 index 00000000..6d57639e --- /dev/null +++ b/packages/esm-lab-manifest-app/src/hooks/usePatient.ts @@ -0,0 +1,16 @@ +import { FetchResponse, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework'; +import useSWR from 'swr'; + +const usePatient = (patientUuid: string) => { + const customerep = 'custom:(person:(display),identifiers:(identifier,identifierType:(uuid)))'; + const url = `${restBaseUrl}/patient/${patientUuid}?v=${customerep}`; + const { data, error, isLoading } = useSWR< + FetchResponse<{ + person: { display: string }; + identifiers: Array<{ identifier: string; identifierType: { uuid: string } }>; + }> + >(url, openmrsFetch); + return { patient: data?.data, error, isLoading }; +}; + +export default usePatient; diff --git a/packages/esm-lab-manifest-app/src/tables/lab-manifest-samples.component.tsx b/packages/esm-lab-manifest-app/src/tables/lab-manifest-samples.component.tsx index 7bac1039..81f07113 100644 --- a/packages/esm-lab-manifest-app/src/tables/lab-manifest-samples.component.tsx +++ b/packages/esm-lab-manifest-app/src/tables/lab-manifest-samples.component.tsx @@ -15,7 +15,15 @@ import { TableSelectRow, } from '@carbon/react'; import { ArrowRight, Printer, TrashCan } from '@carbon/react/icons'; -import { ErrorState, formatDate, parseDate, showModal, showSnackbar, usePagination } from '@openmrs/esm-framework'; +import { + ConfigurableLink, + ErrorState, + formatDate, + parseDate, + showModal, + showSnackbar, + usePagination, +} from '@openmrs/esm-framework'; import { CardHeader, EmptyState, usePaginationInfo } from '@openmrs/esm-patient-common-lib'; import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -28,6 +36,8 @@ import { } from '../lab-manifest.resources'; import { LabManifestSample } from '../types'; import styles from './lab-manifest-table.scss'; +import PatientNameCell from './patient-name-cell.component'; +import PatientCCCNumbercell from './patient-ccc-no-cell.component'; interface LabManifestSamplesProps { manifestUuid: string; @@ -45,6 +55,10 @@ const LabManifestSamples: React.FC = ({ manifestUuid }) const headers = [ { header: t('patient', 'Patient'), + key: 'patientName', + }, + { + header: t('cccKDODNumber', 'CCC/KDOD Number'), key: 'cccKDODNumber', }, { @@ -130,7 +144,16 @@ const LabManifestSamples: React.FC = ({ manifestUuid }) sampleType: sample.sampleType ?? '--', status: sample.status, batchNumber: sample.batchNumber ?? '--', - cccKDODNumber: sample?.order?.patient?.identifiers[0]?.identifier ?? '--', + patientName: sample?.order?.patient?.uuid ? ( + + ) : ( + '--' + ), + cccKDODNumber: sample?.order?.patient ? ( + + ) : ( + '--' + ), dateRequested: sample.dateSent ? formatDate(parseDate(sample.dateSent)) : '--', resultDate: sample.resultDate ? formatDate(parseDate(sample.resultDate)) : '--', result: sample.result ?? '--', diff --git a/packages/esm-lab-manifest-app/src/tables/patient-ccc-no-cell.component.tsx b/packages/esm-lab-manifest-app/src/tables/patient-ccc-no-cell.component.tsx new file mode 100644 index 00000000..a59bd3f0 --- /dev/null +++ b/packages/esm-lab-manifest-app/src/tables/patient-ccc-no-cell.component.tsx @@ -0,0 +1,28 @@ +import React from 'react'; +import { Order } from '../types'; +import usePatient from '../hooks/usePatient'; +import { InlineLoading } from '@carbon/react'; +import { useConfig } from '@openmrs/esm-framework'; +import { LabManifestConfig } from '../config-schema'; +import useIsKDoDSite from '../hooks/useIsKDoDSite'; + +type Props = { + patientUuid: string; +}; + +const PatientCCCNumbercell: React.FC = ({ patientUuid }) => { + const { isLoading, patient } = usePatient(patientUuid); + const { + patientIdentifierTypes: { cccNumberIdentifierType, kdodIdentifierType }, + } = useConfig(); + const { isKDoDSite, error, isLoading: siteLoading } = useIsKDoDSite(); + + if (isLoading || siteLoading) { + return ; + } + + const identifierType = isKDoDSite ? kdodIdentifierType : cccNumberIdentifierType; + return <>{patient?.identifiers?.find((id) => id.identifierType.uuid === identifierType)?.identifier ?? '--'}; +}; + +export default PatientCCCNumbercell; diff --git a/packages/esm-lab-manifest-app/src/tables/patient-name-cell.component.tsx b/packages/esm-lab-manifest-app/src/tables/patient-name-cell.component.tsx new file mode 100644 index 00000000..3c4f7cac --- /dev/null +++ b/packages/esm-lab-manifest-app/src/tables/patient-name-cell.component.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { Order } from '../types'; +import { ConfigurableLink } from '@openmrs/esm-framework'; +import usePatient from '../hooks/usePatient'; +import { InlineLoading } from '@carbon/react'; + +type Props = { + patientUuid: string; +}; + +const PatientNameCell: React.FC = ({ patientUuid }) => { + const { isLoading, patient } = usePatient(patientUuid); + const patientChartUrl = '${openmrsSpaBase}/patient/${patientUuid}/chart/Patient Summary'; + + if (isLoading) { + return ; + } + + return ( + + {patient?.person?.display} + + ); +}; + +export default PatientNameCell;