Skip to content

Commit

Permalink
(feat) O3-3124 Ward App - add patient serach for admitting patient
Browse files Browse the repository at this point in the history
  • Loading branch information
chibongho committed Jan 3, 2025
1 parent 119a1f7 commit 3ff36ca
Show file tree
Hide file tree
Showing 24 changed files with 641 additions and 75 deletions.
1 change: 1 addition & 0 deletions __mocks__/wards.mock.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,5 +78,6 @@ export const mockInpatientAdmissions: InpatientAdmission[] = [
encounterAssigningToCurrentInpatientLocation: null,
currentInpatientRequest: null,
firstAdmissionOrTransferEncounter: null,
currentInpatientLocation: mockLocationInpatientWard
},
];
Original file line number Diff line number Diff line change
Expand Up @@ -269,7 +269,7 @@ const BedAdministrationForm: React.FC<BedAdministrationFormProps> = ({
onClose={() => setShowErrorNotification(false)}
role="alert"
style={{ minWidth: '100%', margin: '0', padding: '0' }}
subtitle={t('pleaseFillField', formStateError) + '.'}
subtitle={formStateError}
title={t('error', 'Error')}
/>
)}
Expand Down
4 changes: 3 additions & 1 deletion packages/esm-patient-search-app/src/routes.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,9 @@
{
"name": "patient-search-workspace",
"component": "patientSearchWorkspace",
"title": "searchPatient"
"title": "searchPatient",
"type": "patient-search-workspace",
"groups": ["ward-patient-admission-requests"]
}
]
}
2 changes: 2 additions & 0 deletions packages/esm-ward-app/mock.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const mockInpatientAdmissionResponse = jest.mocked(useInpatientAdmission).mockRe
error: undefined,
mutate: jest.fn(),
totalCount: mockInpatientAdmissions.length,
nextUri: null,
});

const mockInpatientRequestResponse = jest.mocked(useInpatientRequest).mockReturnValue({
Expand All @@ -46,6 +47,7 @@ const mockInpatientRequestResponse = jest.mocked(useInpatientRequest).mockReturn
error: undefined,
mutate: jest.fn(),
totalCount: mockInpatientRequests.length,
nextUri: null,
});

export const mockWardPatientGroupDetails = jest.mocked(useWardPatientGrouping).mockReturnValue({
Expand Down
3 changes: 3 additions & 0 deletions packages/esm-ward-app/src/hooks/useInpatientAdmission.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ import { restBaseUrl, useOpenmrsFetchAll } from '@openmrs/esm-framework';
import { type InpatientAdmission } from '../types';
import useWardLocation from './useWardLocation';

/**
* fetches a list of inpatient admissions for the current ward location
*/
export function useInpatientAdmission() {
const { location } = useWardLocation();

Expand Down
24 changes: 24 additions & 0 deletions packages/esm-ward-app/src/hooks/useInpatientAdmissionByPatients.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { restBaseUrl, useOpenmrsFetchAll } from '@openmrs/esm-framework';
import { type InpatientAdmission } from '../types';

/**
* fetches a list of inpatient admissions (in any location) for the given patients
*/
export function useInpatientAdmissionByPatients(patientUuids: string[]) {
// prettier-ignore
const customRepresentation =
'custom:(visit,' +
'patient:(uuid,identifiers:(uuid,display,identifier,identifierType),voided,' +
'person:(uuid,display,gender,age,birthdate,birthtime,preferredName,preferredAddress,dead,deathDate)),' +
'encounterAssigningToCurrentInpatientLocation:(encounterDatetime),' +
'currentInpatientRequest:(dispositionLocation,dispositionType,disposition:(uuid,display),dispositionEncounter:(uuid,display),dispositionObsGroup:(uuid,display),visit:(uuid),patient:(uuid)),' +
'firstAdmissionOrTransferEncounter:(encounterDatetime),' +
'currentInpatientLocation' +
')';

return useOpenmrsFetchAll<InpatientAdmission>(
patientUuids?.length > 0
? `${restBaseUrl}/emrapi/inpatient/admission?patients=${patientUuids.join(',')}&v=${customRepresentation}`
: null,
);
}
3 changes: 3 additions & 0 deletions packages/esm-ward-app/src/hooks/useInpatientRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ const defaultRep =
'dispositionObsGroup,' +
'visit)';

/**
* fetches a list of pending inpatient requests for the current ward location
*/
export function useInpatientRequest(
dispositionType: Array<DispositionType> = ['ADMIT', 'TRANSFER'],
rep: string = defaultRep,
Expand Down
35 changes: 35 additions & 0 deletions packages/esm-ward-app/src/hooks/useInpatientRequestByPatients.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { restBaseUrl, useOpenmrsFetchAll } from '@openmrs/esm-framework';
import type { DispositionType, InpatientRequest } from '../types';
import useWardLocation from './useWardLocation';

// prettier-ignore
const defaultRep =
'custom:(' +
'dispositionLocation,' +
'dispositionType,' +
'disposition,' +
'dispositionEncounter:full,' +
'patient:(uuid,identifiers,voided,' +
'person:(uuid,display,gender,age,birthdate,birthtime,preferredName,preferredAddress,dead,deathDate)),' +
'dispositionObsGroup,' +
'visit)';

/**
* fetches a list of pending inpatient requests (in any location) for the given patients
*/
export function useInpatientRequestByPatients(
patientUuids: string[],
dispositionType: Array<DispositionType> = ['ADMIT', 'TRANSFER'],
rep: string = defaultRep,
) {
const searchParams = new URLSearchParams();
searchParams.set('dispositionType', dispositionType.join(','));
searchParams.set('patients', patientUuids?.join(','));
searchParams.set('v', rep);

const { data, ...rest } = useOpenmrsFetchAll<InpatientRequest>(
patientUuids?.length > 0 ? `${restBaseUrl}/emrapi/inpatient/request?${searchParams.toString()}` : null,
);

return { inpatientRequests: data, ...rest };
}
13 changes: 11 additions & 2 deletions packages/esm-ward-app/src/hooks/useRestPatient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,18 @@ import { openmrsFetch, restBaseUrl, type FetchResponse, type Patient } from '@op
import { useMemo } from 'react';
import useSWRImmutable from 'swr/immutable';

export default function useRestPatient(patientUuid: string) {

// prettier-ignore
const defaultRep =
'custom:(' +
'uuid,identifiers,voided,' +
'person:(' +
'uuid,display,gender,age,birthdate,birthtime,preferredName,preferredAddress,dead,deathDate)'
+ ')';

export default function useRestPatient(patientUuid: string, rep = defaultRep) {
const { data, ...rest } = useSWRImmutable<FetchResponse<Patient>>(
patientUuid ? `${restBaseUrl}/patient/${patientUuid}` : null,
patientUuid ? `${restBaseUrl}/patient/${patientUuid}?v=${rep}` : null,
openmrsFetch,
);
const results = useMemo(
Expand Down
15 changes: 15 additions & 0 deletions packages/esm-ward-app/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,21 @@ export const clinicalFormWorkspaceSideRailIcon = getAsyncLifecycle(
options,
);

export const admissionRequestsWorkspaceSideRailIcon = getAsyncLifecycle(
() => import('./ward-workspace/admission-request-workspace/admission-requests-action-button.extension'),
options,
);

export const createAdmissionEncounterWorkspaceSideRailIcon = getAsyncLifecycle(
() => import('./ward-workspace/create-admission-encounter/create-admission-encounter-action-button.extension'),
options,
);

export const createAdmissionEncounterWorkspace = getAsyncLifecycle(
() => import('./ward-workspace/create-admission-encounter/create-admission-encounter.workspace'),
options,
)

export const defaultWardView = getAsyncLifecycle(
() => import('./ward-view/default-ward/default-ward-view.component'),
options,
Expand Down
34 changes: 21 additions & 13 deletions packages/esm-ward-app/src/routes.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,16 @@
"component": "clinicalFormWorkspaceSideRailIcon",
"slot": "action-menu-ward-patient-items-slot"
},
{
"name": "admission-requests-workspace-siderail-button",
"component": "admissionRequestsWorkspaceSideRailIcon",
"slot": "action-menu-ward-patient-admission-requests-items-slot"
},
{
"name": "create-admission-request-workspace-siderail-button",
"component": "createAdmissionEncounterWorkspaceSideRailIcon",
"slot": "action-menu-ward-patient-admission-requests-items-slot"
},
{
"component": "defaultWardView",
"name": "default-ward",
Expand All @@ -76,15 +86,22 @@
"name": "admission-requests-workspace",
"component": "admissionRequestWorkspace",
"title": "admissionRequests",
"type": "admission-requests"
"type": "pending-admission-requests",
"width": "wider",
"groups": ["ward-patient-admission-requests"]
},{
"name": "create-admission-encounter-workspace",
"component": "createAdmissionEncounterWorkspace",
"title": "admitPatient",
"type": "patient-search-workspace",
"width": "wider",
"groups": ["ward-patient-admission-requests"]
},
{
"name": "ward-patient-notes-workspace",
"component": "wardPatientNotesWorkspace",
"type": "ward-patient-notes",
"title": "inpatientNotesWorkspaceTitle",
"sidebarFamily": "ward-patient",
"hasOwnSidebar": true,
"groups": ["ward-patient"]

},
Expand All @@ -100,17 +117,13 @@
"type": "ward",
"title": "Ward patient",
"width": "extra-wide",
"hasOwnSidebar": true,
"sidebarFamily": "ward-patient",
"groups": ["ward-patient"]
},
{
"name": "patient-transfer-swap-workspace",
"component": "patientTransferAndSwapWorkspace",
"title": "transfers",
"type": "transfer-swap-bed-form",
"hasOwnSidebar": true,
"sidebarFamily": "ward-patient",
"groups": ["ward-patient"]

},
Expand All @@ -125,26 +138,21 @@
"component": "patientDischargeWorkspace",
"title": "discharge",
"type": "ward-patient-discharge",
"hasOwnSidebar": true,
"sidebarFamily": "ward-patient",
"groups": ["ward-patient"]
},
{
"name": "ward-patient-clinical-forms-workspace",
"component": "patientClinicalFormsWorkspace",
"title": "clinicalForms",
"type": "ward-patient-clinical-forms",
"hasOwnSidebar": true,
"sidebarFamily": "ward-patient",
"width": "wider",
"groups": ["ward-patient"]
},
{
"name": "cancel-admission-request-workspace",
"component": "cancelAdmissionRequestWorkspace",
"title": "cancelAdmissionRequest",
"type": "cancel-admission-request",
"sidebarFamily": "ward-patient"
"type": "cancel-admission-request"
}
]
}
4 changes: 3 additions & 1 deletion packages/esm-ward-app/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export interface Bed {

export interface BedDetail {
bedId: number;
bedNumber: number;
bedNumber: string;
bedType: BedType;
physicalLocation: Location;
patients: Array<Patient>;
Expand Down Expand Up @@ -152,6 +152,8 @@ export interface InpatientAdmission {

// the current in patient request
currentInpatientRequest: InpatientRequest;

currentInpatientLocation: Location;
}

export interface MotherAndChild {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Button, InlineNotification } from '@carbon/react';
import { Movement } from '@carbon/react/icons';
import { ArrowRightIcon, isDesktop, launchWorkspace, useAppContext, useLayoutType } from '@openmrs/esm-framework';
import { ArrowRightIcon, isDesktop, launchWorkspace, launchWorkspaceGroup, useAppContext, useLayoutType } from '@openmrs/esm-framework';
import React, { type ReactNode } from 'react';
import { useTranslation } from 'react-i18next';
import { type WardViewContext } from '../types';
Expand Down Expand Up @@ -41,7 +41,14 @@ const AdmissionRequestsBar: React.FC<AdmissionRequestsBarProps> = ({ wardPending
})}
</span>
<Button
onClick={() => launchWorkspace('admission-requests-workspace', { wardPendingPatients })}
onClick={() => {
launchWorkspaceGroup('ward-patient-admission-requests', {
state: {wardPendingPatients},
workspaceToLaunch: {
name: 'admission-requests-workspace',
},
});
}}
renderIcon={ArrowRightIcon}
kind="ghost"
size={isDesktop(layout) ? 'sm' : 'lg'}>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { launchWorkspace, useAppContext } from '@openmrs/esm-framework';
import { launchWorkspaceGroup, useAppContext } from '@openmrs/esm-framework';
import { screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import React from 'react';
Expand All @@ -8,14 +8,14 @@ import { type WardViewContext } from '../types';
import AdmissionRequestsBar from './admission-requests-bar.component';

jest.mocked(useAppContext<WardViewContext>).mockReturnValue(mockWardViewContext);

const mockUseConfig = jest.mocked(launchWorkspaceGroup);
describe('Admission Requests Button', () => {
it('should launch workspace when clicked on manage button', async () => {
const user = userEvent.setup();
renderWithSwr(<AdmissionRequestsBar wardPendingPatients={[]} />);

await user.click(screen.getByRole('button', { name: /manage/i }));
expect(launchWorkspace).toHaveBeenCalled();
expect(mockUseConfig).toHaveBeenCalled();
});

it('should have one admission request', () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,8 @@ import { launchWorkspace, useAppContext, useLayoutType } from '@openmrs/esm-fram
import React, { useCallback, useContext } from 'react';
import { useTranslation } from 'react-i18next';
import type { WardPatientCardType, WardPatientWorkspaceProps, WardViewContext } from '../../types';
import { useAdmitPatient } from '../../ward.resource';
import { AdmissionRequestsWorkspaceContext } from '../admission-request-workspace/admission-requests.workspace';
import AdmissionPatientButton from '../admit-patient-button.component';
import AdmitPatientButton from '../admit-patient-button.component';
import styles from './admission-request-card.scss';

const AdmissionRequestCardActions: WardPatientCardType = ({ wardPatient }) => {
Expand Down Expand Up @@ -37,8 +36,9 @@ const AdmissionRequestCardActions: WardPatientCardType = ({ wardPatient }) => {
<Button kind="ghost" size={responsiveSize} onClick={launchCancelAdmissionForm}>
{t('cancel', 'Cancel')}
</Button>
<AdmissionPatientButton
<AdmitPatientButton
wardPatient={wardPatient}
dispositionType={wardPatient.inpatientRequest.dispositionType}
onAdmitPatientSuccess={() => closeWorkspaceWithSavedChanges()}
/>
</div>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { ActionMenuButton, launchWorkspace, UserAvatarIcon } from '@openmrs/esm-framework';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { type AdmissionRequestsWorkspaceProps } from './admission-requests.workspace';

export default function AdmissionRequestsActionButton() {
const { t } = useTranslation();

return (
<ActionMenuButton
getIcon={(props) => <UserAvatarIcon {...props} />}
label={t('pendingAdmissions', 'Pending admissions')}
iconDescription={t('pendingAdmissions', 'Pending admissions')}
handler={() => launchWorkspace<AdmissionRequestsWorkspaceProps>('admission-requests-workspace')}
type={'pending-admission-requests'}
/>
);
}
Loading

0 comments on commit 3ff36ca

Please sign in to comment.