Skip to content

Commit

Permalink
(feat) - O3-3216 - Ward App - show admitted newborn baby / mother in …
Browse files Browse the repository at this point in the history
…ward patient card (#1303)

* feat-metrics

* (fix) O3-3709 - ward app - handle pagination

* update metrics

* mother child stash

* what a mess

* stash

* good to go

* (fix) O3-3709 - ward app - handle pagination

* remove orig files

* (fix) O3-3709 - ward app - handle pagination

* fix tests

---------

Co-authored-by: Bhargav Kodali <kodalibhargav019@gmail.com>
  • Loading branch information
chibongho and kb019 authored Sep 6, 2024
1 parent e2a91e8 commit 07d0432
Show file tree
Hide file tree
Showing 33 changed files with 983 additions and 125 deletions.
10 changes: 4 additions & 6 deletions __mocks__/inpatient-request.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
import { mockPatientAlice } from './patient.mock';
import { mockLocationInpatientWard } from './locations.mock';
import { type InpatientRequest } from '@openmrs/esm-ward-app/src/types';
import { mockPastVisit } from './visits.mock';
import { mockAddress } from './address.mock';
import { mockLocationInpatientWard } from './locations.mock';
import { mockPatientAlice } from './patient.mock';

export const mockInpatientRequest: InpatientRequest = {
export const mockInpatientRequest: InpatientRequest[] = [{
patient: mockPatientAlice,
visit: {
uuid: 'e5727d7e-8e1e-4615-bc3a-abd69e63234a',
Expand Down Expand Up @@ -107,4 +105,4 @@ export const mockInpatientRequest: InpatientRequest = {
display: 'Admit to hospital',
encounterDatetime: '2021-09-28T11:00:00.000Z',
},
};
}];
14 changes: 11 additions & 3 deletions __mocks__/wards.mock.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
import { type AdmissionLocationFetchResponse, type BedType } from '../packages/esm-ward-app/src/types';
import {
type InpatientAdmission,
type InpatientRequest,
type AdmissionLocationFetchResponse,
type BedType,
} from '../packages/esm-ward-app/src/types';
import { mockLocationInpatientWard } from './locations.mock';
import { mockPatientAlice, mockPatientBrian } from './patient.mock';

Expand Down Expand Up @@ -66,9 +71,12 @@ export const mockAdmissionLocation: AdmissionLocationFetchResponse = {
],
};

export const mockInpatientAdmissions = [
export const mockInpatientAdmissions: InpatientAdmission[] = [
{
patient: mockPatientBrian,
visit: null,
encounterAssigningToCurrentInpatientLocation: null,
currentInpatientRequest: null,
firstAdmissionOrTransferEncounter: null,
},
];
];
13 changes: 11 additions & 2 deletions packages/esm-bed-management-app/src/__mocks__/react-i18next.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/** At present, this entire mock is boilerplate. */
import 'react';
import 'react-i18next';
const reactI18next = require('react-i18next');

const hasChildren = (node) => node && (node.children || (node.props && node.props.children));

Expand Down Expand Up @@ -31,7 +31,16 @@ const renderNodes = (reactNodes) => {
};

const useMock = [(k) => k, {}];
useMock.t = (k, o) => (o && o.defaultValue) || (typeof o === 'string' ? o : k);
useMock.t = (key, defaultValue, options = {}) => {
let translatedString = defaultValue;
Object.keys(options).forEach((key) => {
if (key != 'interpolation') {
translatedString = defaultValue.replace(`{{${key}}}`, `${options[key]}`);
}
});

return translatedString ?? key;
};
useMock.i18n = {};

module.exports = {
Expand Down
Binary file added packages/esm-ward-app/.fetch.swp
Binary file not shown.
3 changes: 2 additions & 1 deletion packages/esm-ward-app/src/beds/empty-bed-skeleton.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { SkeletonIcon } from '@carbon/react';
import React from 'react';
import styles from './empty-bed.scss';
import WardPatientSkeletonText from '../ward-patient-card/row-elements/ward-pateint-skeleton-text';

const EmptyBedSkeleton = () => {
return (
<div className={styles.container + ' ' + styles.skeleton}>
<SkeletonIcon />
<SkeletonIcon className={styles.skeletonText} />
<WardPatientSkeletonText />
</div>
);
};
Expand Down
4 changes: 0 additions & 4 deletions packages/esm-ward-app/src/beds/empty-bed.scss
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,3 @@
@include type.type-style('heading-compact-01');
color: $text-02;
}

.skeletonText {
width: 6.25rem;
}
26 changes: 26 additions & 0 deletions packages/esm-ward-app/src/config-schema-mother-child-row.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { Type } from '@openmrs/esm-framework';

export const motherChildRowConfigSchema = {
maternalLocations: {
_type: Type.Array,
_description: 'Defines obs display elements that can be included in the card header or footer.',
_default: [],
_elements: {
id: {
_type: Type.UUID,
_description: 'The unique identifier for this ward location',
},
}
},
childLocations: {
_type: Type.Array,
_description: 'Defines obs display elements that can be included in the card header or footer.',
_default: [],
_elements: {
id: {
_type: Type.UUID,
_description: 'The unique identifier for this ward location',
},
}
}
};
2 changes: 1 addition & 1 deletion packages/esm-ward-app/src/config-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ export const defaultWardPatientCard: WardPatientCardDefinition = {
appliedTo: null,
};

export const builtInPatientCardElements = ['patient-age', 'time-on-ward', 'time-since-admission'];
export const builtInPatientCardElements = ['patient-age', 'time-on-ward', 'time-since-admission', 'patient-location'];

export const addressFields = [
'cityVillage',
Expand Down
52 changes: 52 additions & 0 deletions packages/esm-ward-app/src/hooks/useMotherAndChildren.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import {
makeUrl,
restBaseUrl,
useOpenmrsFetchAll,
useOpenmrsInfinite,
useOpenmrsPagination,
} from '@openmrs/esm-framework';
import { type MotherAndChildren } from '../types';

export interface MothersAndChildrenSearchCriteria {
mothers?: Array<string>;
children?: Array<string>;
requireMotherHasActiveVisit?: boolean;
requireChildHasActiveVisit?: boolean;
requireChildBornDuringMothersActiveVisit?: boolean;
}

export function useMotherAndChildren(
criteria: MothersAndChildrenSearchCriteria,
fetch: boolean = true,
rep: string = null,
) {
const url = makeUrlUrl(`${restBaseUrl}/emrapi/maternal/mothersAndChildren`);
const {
mothers,
children,
requireChildBornDuringMothersActiveVisit,
requireChildHasActiveVisit,
requireMotherHasActiveVisit,
} = criteria;

for (const m of mothers ?? []) {
url.searchParams.append('mother', m);
}

for (const c of children ?? []) {
url.searchParams.append('child', c);
}

url.searchParams.append('requireMotherHasActiveVisit', requireMotherHasActiveVisit?.toString() ?? 'false');
url.searchParams.append('requireChildHasActiveVisit', requireChildHasActiveVisit?.toString() ?? 'false');
url.searchParams.append(
'requireChildBornDuringMothersActiveVisit',
requireChildBornDuringMothersActiveVisit?.toString() ?? 'false',
);
rep && url.searchParams.append('v', rep);
return useOpenmrsPagination<MotherAndChildren>(fetch ? url : null, 50);
}

function makeUrlUrl(path: string) {
return new URL(makeUrl(path), window.location.toString());
}
31 changes: 13 additions & 18 deletions packages/esm-ward-app/src/hooks/useWardPatientGrouping.ts
Original file line number Diff line number Diff line change
@@ -1,35 +1,30 @@
import { useMemo } from 'react';
import { createAndGetWardPatientGrouping, getInpatientAdmissionsUuidMap } from '../ward-view/ward-view.resource';
import { createAndGetWardPatientGrouping } from '../ward-view/ward-view.resource';
import { useAdmissionLocation } from './useAdmissionLocation';
import { useInpatientAdmission } from './useInpatientAdmission';
import { useInpatientRequest } from './useInpatientRequest';

export function useWardPatientGrouping() {
const admissionLocationResponse = useAdmissionLocation();
const inpatientAdmissionResponse = useInpatientAdmission();
const inpatientRequestResponse = useInpatientRequest();

const { data: inpatientAdmissions } = inpatientAdmissionResponse;
const { admissionLocation } = admissionLocationResponse;
const inpatientAdmissionsByPatientUuid = useMemo(() => {
return getInpatientAdmissionsUuidMap(inpatientAdmissions);
}, [inpatientAdmissions]);
const { inpatientRequests } = inpatientRequestResponse;

const {
wardAdmittedPatientsWithBed,
wardUnadmittedPatientsWithBed,
wardPatientPendingCount,
bedLayouts,
wardUnassignedPatientsList,
} = useMemo(() => {
return createAndGetWardPatientGrouping(inpatientAdmissions, admissionLocation, inpatientAdmissionsByPatientUuid);
}, [inpatientAdmissionsByPatientUuid, admissionLocation, inpatientAdmissions]);
const groupings = useMemo(() => {
if(!admissionLocationResponse.isLoading && !inpatientAdmissionResponse.isLoading && !inpatientRequestResponse.isLoading) {
return createAndGetWardPatientGrouping(inpatientAdmissions, admissionLocation, inpatientRequests);
} else {
return {};
}
}, [admissionLocation, inpatientAdmissions]) as ReturnType<typeof createAndGetWardPatientGrouping>;

return {
wardAdmittedPatientsWithBed,
wardUnadmittedPatientsWithBed,
wardUnassignedPatientsList,
wardPatientPendingCount,
...groupings,
admissionLocationResponse,
inpatientAdmissionResponse,
bedLayouts,
inpatientRequestResponse,
};
}
7 changes: 7 additions & 0 deletions packages/esm-ward-app/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { coloredObsTagsCardRowConfigSchema } from './config-schema-extension-col
import { moduleName } from './constant';
import { createDashboardLink } from './createDashboardLink.component';
import rootComponent from './root.component';
import { motherChildRowConfigSchema } from './config-schema-mother-child-row';
import { pendingOrdersExtensionConfigSchema } from './config-schema-pending-orders-extension';

export const importTranslation = require.context('../translations', false, /.json$/, 'lazy');
Expand Down Expand Up @@ -68,6 +69,11 @@ export const admissionRequestNoteRowExtension = getAsyncLifecycle(
options,
);

export const motherChildRowExtension = getAsyncLifecycle(
() => import('./ward-patient-card/card-rows/mother-child-row.extension'),
options,
);

export const pendingOrdersExtension = getAsyncLifecycle(
() => import('./ward-patient-card/card-rows/pending-orders.extension'),
options,
Expand Down Expand Up @@ -106,6 +112,7 @@ export function startupApp() {
defineConfigSchema(moduleName, configSchema);
defineExtensionConfigSchema('colored-obs-tags-card-row', coloredObsTagsCardRowConfigSchema);
defineExtensionConfigSchema('admission-request-note-card-row', admissionRequestNoteRowConfigSchema);
defineExtensionConfigSchema('mother-child-card-row', motherChildRowConfigSchema);
defineExtensionConfigSchema('ward-patient-pending-orders', pendingOrdersExtensionConfigSchema);

registerFeatureFlag(
Expand Down
5 changes: 5 additions & 0 deletions packages/esm-ward-app/src/routes.json
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,11 @@
"name": "admission-request-note-card-row",
"slot": "ward-patient-card-slot"
},
{
"component": "motherChildRowExtension",
"name": "mother-child-card-row",
"slot": "ward-patient-card-slot"
},
{
"component": "pendingOrdersExtension",
"name": "ward-patient-pending-orders",
Expand Down
21 changes: 21 additions & 0 deletions packages/esm-ward-app/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ export type WardPatient = {
export interface WardPatientWorkspaceProps extends DefaultWorkspaceProps {
wardPatient: WardPatient;
}
export interface MotherAndChildrenRelationships {
motherByChildUuid: Map<string, Patient>;
childrenByMotherUuid: Map<string, Array<Patient>>;
}

// server-side types defined in openmrs-module-bedmanagement:

Expand Down Expand Up @@ -140,6 +144,16 @@ export interface InpatientAdmission {
// the current in patient request
currentInpatientRequest: InpatientRequest;
}
export interface WardAppContext {
allPatientsByPatientUuid: Map<string, Patient>;
}

export interface MotherAndChild {
mother: Patient;
child: Patient;
motherAdmission: InpatientAdmission;
childAdmission: InpatientAdmission;
}

// TODO: Move these types to esm-core
export interface Observation extends OpenmrsResourceStrict {
Expand Down Expand Up @@ -214,4 +228,11 @@ export interface ObsPayload {
groupMembers?: Array<ObsPayload>;
}

export interface MotherAndChildren {
childAdmission: InpatientAdmission;
child: Patient;
motherAdmission: InpatientAdmission;
mother: Patient;
}

export type WardPatientGroupDetails = ReturnType<typeof useWardPatientGrouping>;
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { type ObsElementDefinition } from '../../config-schema';
import { type WardPatientCard } from '../../types';
import WardPatientObs from '../row-elements/ward-patient-obs';
import { useConfig } from '@openmrs/esm-framework';
import styles from '../ward-patient-card.scss';

const AdmissionRequestNoteRowExtension: WardPatientCard = ({ patient, visit, inpatientAdmission }) => {
const { conceptUuid } = useConfig<ObsElementDefinition>();
Expand All @@ -18,9 +19,13 @@ const AdmissionRequestNoteRowExtension: WardPatientCard = ({ patient, visit, inp
// only show if the patient has not been admitted yet
const admitted = inpatientAdmission != null;
if (admitted) {
return <></>;
return null;
} else {
return <WardPatientObs config={config} patient={patient} visit={visit} />;
return (
<div className={styles.wardPatientCardRow}>
<WardPatientObs config={config} patient={patient} visit={visit} />
</div>
);
}
};

Expand Down
Loading

0 comments on commit 07d0432

Please sign in to comment.