From 1f03fee3b673009123d44d0310ef76d2f579fc1b Mon Sep 17 00:00:00 2001 From: Chi Bong Ho Date: Tue, 29 Oct 2024 16:58:41 -0400 Subject: [PATCH] (fix) O3-3849 Ward App - make patient header appear consistently in all ward workspaces --- __mocks__/inpatient-request.ts | 144 +++++++++--------- packages/esm-ward-app/mock.tsx | 9 +- packages/esm-ward-app/src/routes.json | 2 +- .../ward-view-header/ward-metrics.test.tsx | 18 +-- ...mission-request-card-actions.component.tsx | 7 +- .../admit-patient-form.scss | 7 + .../admit-patient-form.test.tsx | 40 +++-- .../admit-patient-form.workspace.tsx | 128 ++++++++-------- .../admit-patient-form-workspace/types.ts | 7 - .../ward-patient.workspace.tsx | 59 ++----- 10 files changed, 200 insertions(+), 221 deletions(-) delete mode 100644 packages/esm-ward-app/src/ward-workspace/admit-patient-form-workspace/types.ts diff --git a/__mocks__/inpatient-request.ts b/__mocks__/inpatient-request.ts index 5db08b54a..d8a83ea8f 100644 --- a/__mocks__/inpatient-request.ts +++ b/__mocks__/inpatient-request.ts @@ -3,81 +3,81 @@ import { mockLocationInpatientWard } from './locations.mock'; import { mockPatientAlice } from './patient.mock'; import { mockVisitAlice } from './visits.mock'; -export const mockInpatientRequest: InpatientRequest[] = [ - { - patient: mockPatientAlice, - visit: mockVisitAlice, - dispositionLocation: mockLocationInpatientWard, - dispositionType: 'ADMIT', - disposition: { - uuid: '6c047a20-c2bf-43ef-9e88-6da7b17e8c1a', +export const mockInpatientRequestAlice: InpatientRequest = { + patient: mockPatientAlice, + visit: mockVisitAlice, + dispositionLocation: mockLocationInpatientWard, + dispositionType: 'ADMIT', + disposition: { + uuid: '6c047a20-c2bf-43ef-9e88-6da7b17e8c1a', + display: 'Admit to hospital', + name: { display: 'Admit to hospital', - name: { - display: 'Admit to hospital', + uuid: 'b1e494ef-4779-4262-bc42-56a79c39303c', + name: 'Admit to hospital', + locale: 'en', + localePreferred: true, + conceptNameType: 'FULLY_SPECIFIED', + }, + datatype: { + uuid: '8d4a4c94-c2cc-11de-8d13-0010c6dffd0f', + display: 'N/A', + }, + conceptClass: { + uuid: '8d492774-c2cc-11de-8d13-0010c6dffd0f', + display: 'Misc', + }, + set: false, + version: null, + retired: false, + names: [ + { + uuid: '122a523c-cbec-4283-991f-858f44ffccca', + display: 'Hospital admission', + }, + { uuid: 'b1e494ef-4779-4262-bc42-56a79c39303c', - name: 'Admit to hospital', - locale: 'en', - localePreferred: true, - conceptNameType: 'FULLY_SPECIFIED', + display: 'Admit to hospital', }, - datatype: { - uuid: '8d4a4c94-c2cc-11de-8d13-0010c6dffd0f', - display: 'N/A', + { + uuid: 'f72fadb0-d5db-102d-ad2a-000c29c2a5d7', + display: "ADMIS Á L'HÔPITAL", }, - conceptClass: { - uuid: '8d492774-c2cc-11de-8d13-0010c6dffd0f', - display: 'Misc', + { + uuid: 'acdcc1d2-7414-4337-890e-c8ccbccda41a', + display: 'Admèt nan lopital', }, - set: false, - version: null, - retired: false, - names: [ - { - uuid: '122a523c-cbec-4283-991f-858f44ffccca', - display: 'Hospital admission', - }, - { - uuid: 'b1e494ef-4779-4262-bc42-56a79c39303c', - display: 'Admit to hospital', - }, - { - uuid: 'f72fadb0-d5db-102d-ad2a-000c29c2a5d7', - display: "ADMIS Á L'HÔPITAL", - }, - { - uuid: 'acdcc1d2-7414-4337-890e-c8ccbccda41a', - display: 'Admèt nan lopital', - }, - { - uuid: '4f12edd7-e516-493b-bc21-da1a9d29873f', - display: "Admettre à l'hôpital", - }, - ], - descriptions: [ - { - uuid: '7d29309b-faaa-4767-83e6-6c75117fc569', - display: 'patient will be admitted from the clinic to the hospital for managment of an acute problem.', - }, - ], - mappings: [ - { - uuid: '75a1a11e-4943-102e-96e9-000c29c2a5d7', - display: 'PIH: ADMIT TO HOSPITAL', - }, - { - uuid: 'b260d122-4864-102e-96e9-000c29c2a5d7', - display: 'PIH: 3799', - }, - ], - answers: [], - setMembers: [], - attributes: [], - resourceVersion: '2.0', - }, - dispositionEncounter: { - uuid: '6c047a20-c2bf-43ef-9e88-6da7b17e8c1a', - display: 'Admit to hospital', - encounterDatetime: '2021-09-28T11:00:00.000Z', - }, + { + uuid: '4f12edd7-e516-493b-bc21-da1a9d29873f', + display: "Admettre à l'hôpital", + }, + ], + descriptions: [ + { + uuid: '7d29309b-faaa-4767-83e6-6c75117fc569', + display: 'patient will be admitted from the clinic to the hospital for managment of an acute problem.', + }, + ], + mappings: [ + { + uuid: '75a1a11e-4943-102e-96e9-000c29c2a5d7', + display: 'PIH: ADMIT TO HOSPITAL', + }, + { + uuid: 'b260d122-4864-102e-96e9-000c29c2a5d7', + display: 'PIH: 3799', + }, + ], + answers: [], + setMembers: [], + attributes: [], + resourceVersion: '2.0', + }, + dispositionEncounter: { + uuid: '6c047a20-c2bf-43ef-9e88-6da7b17e8c1a', + display: 'Admit to hospital', + encounterDatetime: '2021-09-28T11:00:00.000Z', }, -]; +}; + +export const mockInpatientRequests = [mockInpatientRequestAlice]; diff --git a/packages/esm-ward-app/mock.tsx b/packages/esm-ward-app/mock.tsx index ff7da8e40..5e606b9f1 100644 --- a/packages/esm-ward-app/mock.tsx +++ b/packages/esm-ward-app/mock.tsx @@ -1,4 +1,4 @@ -import { mockAdmissionLocation, mockInpatientAdmissions, mockInpatientRequest } from '__mocks__'; +import { mockAdmissionLocation, mockInpatientAdmissions, mockInpatientRequests } from '__mocks__'; import { useAdmissionLocation } from './src/hooks/useAdmissionLocation'; import { useInpatientAdmission } from './src/hooks/useInpatientAdmission'; import { createAndGetWardPatientGrouping } from './src/ward-view/ward-view.resource'; @@ -38,22 +38,23 @@ const mockInpatientAdmissionResponse = jest.mocked(useInpatientAdmission).mockRe }); const mockInpatientRequestResponse = jest.mocked(useInpatientRequest).mockReturnValue({ - inpatientRequests: mockInpatientRequest, + inpatientRequests: mockInpatientRequests, hasMore: false, loadMore: jest.fn(), isValidating: false, isLoading: false, error: undefined, mutate: jest.fn(), - totalCount: mockInpatientRequest.length, + totalCount: mockInpatientRequests.length, }); export const mockWardPatientGroupDetails = jest.mocked(useWardPatientGrouping).mockReturnValue({ admissionLocationResponse: mockAdmissionLocationResponse(), inpatientAdmissionResponse: mockInpatientAdmissionResponse(), inpatientRequestResponse: mockInpatientRequestResponse(), - ...createAndGetWardPatientGrouping(mockInpatientAdmissions, mockAdmissionLocation, mockInpatientRequest), + ...createAndGetWardPatientGrouping(mockInpatientAdmissions, mockAdmissionLocation, mockInpatientRequests), isLoading: false, + mutate: jest.fn(), }); export const mockWardViewContext: WardViewContext = { diff --git a/packages/esm-ward-app/src/routes.json b/packages/esm-ward-app/src/routes.json index 2ec17081e..57c0ea538 100644 --- a/packages/esm-ward-app/src/routes.json +++ b/packages/esm-ward-app/src/routes.json @@ -96,7 +96,7 @@ "name": "ward-patient-workspace", "component": "wardPatientWorkspace", "type": "ward", - "title": "Ward Patient", + "title": "Ward patient", "width": "extra-wide", "hasOwnSidebar": true, "sidebarFamily": "ward-patient" diff --git a/packages/esm-ward-app/src/ward-view-header/ward-metrics.test.tsx b/packages/esm-ward-app/src/ward-view-header/ward-metrics.test.tsx index 3991e9075..5299b697f 100644 --- a/packages/esm-ward-app/src/ward-view-header/ward-metrics.test.tsx +++ b/packages/esm-ward-app/src/ward-view-header/ward-metrics.test.tsx @@ -1,19 +1,11 @@ +import { useAppContext } from '@openmrs/esm-framework'; +import { screen } from '@testing-library/react'; import React from 'react'; -import WardMetrics from './ward-metrics.component'; import { renderWithSwr } from '../../../../tools/test-utils'; -import { - createAndGetWardPatientGrouping, - getInpatientAdmissionsUuidMap, - getWardMetrics, -} from '../ward-view/ward-view.resource'; -import { useAdmissionLocation } from '../hooks/useAdmissionLocation'; -import { mockAdmissionLocation, mockInpatientAdmissions, mockInpatientRequest } from '__mocks__'; -import { useInpatientAdmission } from '../hooks/useInpatientAdmission'; -import useWardLocation from '../hooks/useWardLocation'; -import { screen } from '@testing-library/react'; -import { useAppContext } from '@openmrs/esm-framework'; -import { type WardViewContext } from '../types'; import { mockWardViewContext } from '../../mock'; +import { type WardViewContext } from '../types'; +import { getWardMetrics } from '../ward-view/ward-view.resource'; +import WardMetrics from './ward-metrics.component'; const wardMetrics = [ { name: 'patients', key: 'patients', defaultTranslation: 'Patients' }, diff --git a/packages/esm-ward-app/src/ward-workspace/admission-request-card/admission-request-card-actions.component.tsx b/packages/esm-ward-app/src/ward-workspace/admission-request-card/admission-request-card-actions.component.tsx index f75193666..d031793c0 100644 --- a/packages/esm-ward-app/src/ward-workspace/admission-request-card/admission-request-card-actions.component.tsx +++ b/packages/esm-ward-app/src/ward-workspace/admission-request-card/admission-request-card-actions.component.tsx @@ -13,7 +13,6 @@ import useWardLocation from '../../hooks/useWardLocation'; import type { WardPatientCardType, WardPatientWorkspaceProps, WardViewContext } from '../../types'; import { useAdmitPatient } from '../../ward.resource'; import { AdmissionRequestsWorkspaceContext } from '../admission-request-workspace/admission-requests.workspace'; -import type { AdmitPatientFormWorkspaceProps } from '../admit-patient-form-workspace/types'; import styles from './admission-request-card.scss'; const AdmissionRequestCardActions: WardPatientCardType = (wardPatient) => { @@ -25,10 +24,8 @@ const AdmissionRequestCardActions: WardPatientCardType = (wardPatient) => { const { WardPatientHeader, wardPatientGroupDetails } = useAppContext('ward-view-context') ?? {}; const { admitPatient, isLoadingEmrConfiguration, errorFetchingEmrConfiguration } = useAdmitPatient(); - const launchPatientAdmissionForm = useCallback( - () => launchWorkspace('admit-patient-form-workspace', { patient, dispositionType }), - [], - ); + const launchPatientAdmissionForm = () => + launchWorkspace('admit-patient-form-workspace', { wardPatient, WardPatientHeader }); const launchPatientTransferForm = useCallback(() => { launchWorkspace('patient-transfer-request-workspace', { diff --git a/packages/esm-ward-app/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.scss b/packages/esm-ward-app/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.scss index 121adcb22..60d353c36 100644 --- a/packages/esm-ward-app/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.scss +++ b/packages/esm-ward-app/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.scss @@ -2,6 +2,13 @@ @use '@carbon/layout'; @use '@openmrs/esm-styleguide/src/vars' as *; +.flexWrapper { + height: 100%; + display: flex; + flex-direction: column; + color: #393939; +} + .form { display: flex; height: 100%; diff --git a/packages/esm-ward-app/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.test.tsx b/packages/esm-ward-app/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.test.tsx index d40219392..b29370018 100644 --- a/packages/esm-ward-app/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.test.tsx +++ b/packages/esm-ward-app/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.test.tsx @@ -1,16 +1,22 @@ -import { showSnackbar, useAppContext, useFeatureFlag, useSession } from '@openmrs/esm-framework'; +import { + CloseWorkspaceOptions, + type DefaultWorkspaceProps, + showSnackbar, + useAppContext, + useFeatureFlag, + useSession, +} from '@openmrs/esm-framework'; import { screen } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; import React from 'react'; -import { mockLocationInpatientWard, mockPatientAlice } from '../../../../../__mocks__'; +import { mockInpatientRequestAlice, mockLocationInpatientWard, mockPatientAlice } from '../../../../../__mocks__'; import { renderWithSwr } from '../../../../../tools'; import { mockWardPatientGroupDetails, mockWardViewContext } from '../../../mock'; import { useAssignedBedByPatient } from '../../hooks/useAssignedBedByPatient'; import useWardLocation from '../../hooks/useWardLocation'; -import type { DispositionType, WardViewContext } from '../../types'; +import type { DispositionType, WardPatient, WardViewContext } from '../../types'; import { assignPatientToBed, removePatientFromBed, useAdmitPatient } from '../../ward.resource'; import AdmitPatientFormWorkspace from './admit-patient-form.workspace'; -import type { AdmitPatientFormWorkspaceProps } from './types'; jest.mock('../../hooks/useAdmissionLocation', () => ({ useAdmissionLocation: jest.fn(), @@ -52,25 +58,35 @@ const mockedUseAdmitPatient = jest.mocked(useAdmitPatient); jest.mocked(useAppContext).mockReturnValue(mockWardViewContext); +const mockedAdmitPatient = jest.fn(); const mockUseAdmitPatientObj: ReturnType = { - admitPatient: jest.fn(), + admitPatient: mockedAdmitPatient, isLoadingEmrConfiguration: false, errorFetchingEmrConfiguration: false, }; jest.mocked(useAdmitPatient).mockReturnValue(mockUseAdmitPatientObj); -const mockedAdmitPatient = mockUseAdmitPatientObj.admitPatient; -const mockWorkspaceProps: AdmitPatientFormWorkspaceProps = { - patient: mockPatientAlice, - closeWorkspace: jest.fn(), +const mockWorkspaceProps: DefaultWorkspaceProps = { closeWorkspaceWithSavedChanges: jest.fn(), promptBeforeClosing: jest.fn(), setTitle: jest.fn(), - dispositionType: 'ADMIT', + closeWorkspace: jest.fn(), +}; + +const mockWardPatientAliceProps: WardPatient = { + visit: mockInpatientRequestAlice.visit, + patient: mockPatientAlice, + bed: null, + inpatientAdmission: null, + inpatientRequest: mockInpatientRequestAlice, }; -function renderAdmissionForm(dispositionType: DispositionType = 'ADMIT') { - renderWithSwr(); +function renderAdmissionForm() { + renderWithSwr( + , + ); } describe('Testing AdmitPatientForm', () => { diff --git a/packages/esm-ward-app/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.workspace.tsx b/packages/esm-ward-app/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.workspace.tsx index 1eebe85ee..aabb9a1da 100644 --- a/packages/esm-ward-app/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.workspace.tsx +++ b/packages/esm-ward-app/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.workspace.tsx @@ -7,11 +7,11 @@ import { useTranslation } from 'react-i18next'; import { z } from 'zod'; import { useAssignedBedByPatient } from '../../hooks/useAssignedBedByPatient'; import useWardLocation from '../../hooks/useWardLocation'; -import type { WardViewContext } from '../../types'; +import type { WardPatientWorkspaceProps, WardViewContext } from '../../types'; import { assignPatientToBed, removePatientFromBed, useAdmitPatient } from '../../ward.resource'; import BedSelector from '../bed-selector.component'; +import WardPatientWorkspaceBanner from '../patient-banner/patient-banner.component'; import styles from './admit-patient-form.scss'; -import type { AdmitPatientFormWorkspaceProps } from './types'; /** * This form gets rendered when the user clicks "admit patient" in @@ -19,13 +19,15 @@ import type { AdmitPatientFormWorkspaceProps } from './types'; * the bed management module is installed. It asks to (optionally) select * a bed to assign to patient */ -const AdmitPatientFormWorkspace: React.FC = ({ - patient, - dispositionType, +const AdmitPatientFormWorkspace: React.FC = ({ + wardPatient, closeWorkspace, closeWorkspaceWithSavedChanges, promptBeforeClosing, }) => { + const { patient, inpatientRequest } = wardPatient; + const { dispositionType } = inpatientRequest; + const { t } = useTranslation(); const { location } = useWardLocation(); const [isSubmitting, setIsSubmitting] = useState(false); @@ -139,64 +141,68 @@ const AdmitPatientFormWorkspace: React.FC = ({ }, []); if (!wardPatientGroupDetails) return <>; + return ( -
-
- - -

{t('selectABed', 'Select a bed')}

-
- { - return ( - - ); - }} - /> -
-
-
-
- {showErrorNotifications && - Object.entries(errors).map(([key, value]) => { - return ( - - - - - - ); - })} +
+ + +
+ + +

{t('selectABed', 'Select a bed')}

+
+ { + return ( + + ); + }} + /> +
+
+
+
+ {showErrorNotifications && + Object.entries(errors).map(([key, value]) => { + return ( + + + + + + ); + })} +
-
- - - - - + + + + + +
); }; diff --git a/packages/esm-ward-app/src/ward-workspace/admit-patient-form-workspace/types.ts b/packages/esm-ward-app/src/ward-workspace/admit-patient-form-workspace/types.ts deleted file mode 100644 index d2f639b78..000000000 --- a/packages/esm-ward-app/src/ward-workspace/admit-patient-form-workspace/types.ts +++ /dev/null @@ -1,7 +0,0 @@ -import type { DefaultWorkspaceProps, Patient } from '@openmrs/esm-framework'; -import type { DispositionType } from '../../types'; - -export interface AdmitPatientFormWorkspaceProps extends DefaultWorkspaceProps { - patient: Patient; - dispositionType: DispositionType; -} diff --git a/packages/esm-ward-app/src/ward-workspace/patient-details/ward-patient.workspace.tsx b/packages/esm-ward-app/src/ward-workspace/patient-details/ward-patient.workspace.tsx index f96cf342a..29bd9977f 100644 --- a/packages/esm-ward-app/src/ward-workspace/patient-details/ward-patient.workspace.tsx +++ b/packages/esm-ward-app/src/ward-workspace/patient-details/ward-patient.workspace.tsx @@ -1,60 +1,27 @@ -import { age, attach, ExtensionSlot, type Patient } from '@openmrs/esm-framework'; -import React, { useEffect } from 'react'; -import styles from './ward-patient.style.scss'; -import { useTranslation } from 'react-i18next'; +import { attach, ExtensionSlot } from '@openmrs/esm-framework'; +import React from 'react'; import { type WardPatientWorkspaceProps } from '../../types'; -import { getGender } from '../../ward-patient-card/row-elements/ward-patient-gender.component'; +import styles from './ward-patient.style.scss'; attach('ward-patient-workspace-header-slot', 'patient-vitals-info'); -export default function WardPatientWorkspace({ setTitle, wardPatient }: WardPatientWorkspaceProps) { - useEffect(() => { - if (wardPatient) { - const { patient } = wardPatient; - setTitle(patient.person.display, ); - } - }, [wardPatient]); +export default function WardPatientWorkspace({ wardPatient, WardPatientHeader }: WardPatientWorkspaceProps) { + const { patient } = wardPatient ?? {}; + const extensionSlotState = { patient, patientUuid: patient?.uuid }; return ( <> {wardPatient && (
- + +
+ +
+
+ +
)} ); } - -interface WardPatientWorkspaceViewProps { - patient: Patient; -} - -const WardPatientWorkspaceView: React.FC = ({ patient }) => { - const extensionSlotState = { patient, patientUuid: patient.uuid }; - - return ( - <> -
- -
-
- -
- - ); -}; - -const PatientWorkspaceTitle: React.FC = ({ patient }) => { - const { t } = useTranslation(); - - return ( - <> -
{patient.person.display}  
-
·   {getGender(t, patient.person?.gender)}
- {patient.person?.birthdate && ( -
·   {age(patient.person?.birthdate)}
- )} - - ); -};