-
Notifications
You must be signed in to change notification settings - Fork 211
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
(feat) O3-3211: Ward App - display metrics for admission status and bed occupancy #1213
Changes from all commits
e710137
f56fc3a
5104ac5
321625b
801177b
dd661e0
c870aba
7c947c8
82b6817
9a4671a
7bb7ed3
6a7e24d
82d3d9d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { mockBedType } from './wards.mock'; | ||
|
||
export const mockWardBeds = [ | ||
{ | ||
id: 1, | ||
uuid: '0000-bed1', | ||
bedNumber: 'bed1', | ||
bedType: mockBedType, | ||
row: 1, | ||
column: 2, | ||
status: 'OCCUPIED' as const, | ||
}, | ||
{ | ||
id: 2, | ||
uuid: '0000-bed2', | ||
bedNumber: 'bed2', | ||
bedType: mockBedType, | ||
row: 1, | ||
column: 2, | ||
status: 'AVAILABLE' as const, | ||
}, | ||
{ | ||
id: 1, | ||
uuid: '0000-bed3', | ||
bedNumber: 'bed3', | ||
bedType: mockBedType, | ||
row: 1, | ||
column: 3, | ||
status: 'AVAILABLE' as const, | ||
}, | ||
]; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { useMemo } from 'react'; | ||
import { createAndGetWardPatientGrouping, getInpatientAdmissionsUuidMap } from '../ward-view/ward-view.resource'; | ||
import { useAdmissionLocation } from './useAdmissionLocation'; | ||
import { useInpatientAdmission } from './useInpatientAdmission'; | ||
|
||
export function useWardPatientGrouping() { | ||
const admissionLocationResponse = useAdmissionLocation(); | ||
const inpatientAdmissionResponse = useInpatientAdmission(); | ||
|
||
const { inpatientAdmissions } = inpatientAdmissionResponse; | ||
const { admissionLocation } = admissionLocationResponse; | ||
const inpatientAdmissionsByPatientUuid = useMemo(() => { | ||
return getInpatientAdmissionsUuidMap(inpatientAdmissions); | ||
}, [inpatientAdmissions]); | ||
|
||
const { | ||
wardAdmittedPatientsWithBed, | ||
wardUnadmittedPatientsWithBed, | ||
wardPatientPendingCount, | ||
bedLayouts, | ||
wardUnassignedPatientsList, | ||
} = useMemo(() => { | ||
return createAndGetWardPatientGrouping(inpatientAdmissions, admissionLocation, inpatientAdmissionsByPatientUuid); | ||
}, [inpatientAdmissionsByPatientUuid, admissionLocation, inpatientAdmissions]); | ||
|
||
return { | ||
wardAdmittedPatientsWithBed, | ||
wardUnadmittedPatientsWithBed, | ||
wardUnassignedPatientsList, | ||
wardPatientPendingCount, | ||
admissionLocationResponse, | ||
inpatientAdmissionResponse, | ||
bedLayouts, | ||
}; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import React from 'react'; | ||
import styles from './ward-metric.scss'; | ||
import { SkeletonPlaceholder } from '@carbon/react'; | ||
|
||
interface WardMetricProps { | ||
metricName: string; | ||
metricValue: string; | ||
isLoading: boolean; | ||
} | ||
const WardMetric: React.FC<WardMetricProps> = ({ metricName, metricValue, isLoading }) => { | ||
return ( | ||
<div className={styles.metric}> | ||
<span className={styles.metricName}>{metricName}</span> | ||
{isLoading ? ( | ||
<SkeletonPlaceholder className={styles.skeleton} /> | ||
) : ( | ||
<span className={styles.metricValue}>{metricValue}</span> | ||
)} | ||
</div> | ||
); | ||
}; | ||
|
||
export default WardMetric; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
@use '@carbon/styles/scss/spacing'; | ||
@use '@carbon/type'; | ||
@import '~@openmrs/esm-styleguide/src/vars'; | ||
|
||
.metric { | ||
margin-left: spacing.$spacing-05; | ||
display: flex; | ||
align-items: end; | ||
gap: 5px; | ||
} | ||
|
||
.metricName { | ||
@include type.type-style('helper-text-01'); | ||
color: $color-gray-70; | ||
} | ||
|
||
.metricValue { | ||
@include type.type-style('heading-03'); | ||
line-height: revert; | ||
} | ||
|
||
.skeleton { | ||
height: 15px; | ||
width: 15px; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import React from 'react'; | ||
import styles from './ward-metrics.scss'; | ||
import { useBeds } from '../hooks/useBeds'; | ||
import { showNotification, useAppContext, useFeatureFlag } from '@openmrs/esm-framework'; | ||
import { useTranslation } from 'react-i18next'; | ||
import { getWardMetrics } from '../ward-view/ward-view.resource'; | ||
import WardMetric from './ward-metric.component'; | ||
import type { WardPatientGroupDetails } from '../types'; | ||
import useWardLocation from '../hooks/useWardLocation'; | ||
|
||
const wardMetrics = [ | ||
{ name: 'Patients', key: 'patients' }, | ||
{ name: 'Free beds', key: 'freeBeds' }, | ||
{ name: 'Capacity', key: 'capacity' }, | ||
]; | ||
|
||
const WardMetrics = () => { | ||
const { location } = useWardLocation(); | ||
const { beds, isLoading, error } = useBeds({ locationUuid: location.uuid }); | ||
const { t } = useTranslation(); | ||
const isBedManagementModuleInstalled = useFeatureFlag('bedmanagement-module'); | ||
const wardPatientGroup = useAppContext<WardPatientGroupDetails>('ward-patients-group'); | ||
|
||
if (error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will depend on how the useBeds() hook works, but per the ticket description, we do not want to fail if the bedmanagment module is not installed, so this needs to be aware of the bed management feature flag. @chibongho should be able to advise here. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hi @mseaton , if bedmanagement module,is not installed,this component is not rendered and there will be a empty page.Is this the desired behaviour or should the metrics be displayed having "--" as value. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kb019 - see the ticket description: https://openmrs.atlassian.net/browse/O3-3211 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks @mseaton . Will look into it. |
||
showNotification({ | ||
kind: 'error', | ||
title: t('errorLoadingBedDetails', 'Error Loading Bed Details'), | ||
description: error.message, | ||
}); | ||
} | ||
const wardMetricValues = getWardMetrics(beds); | ||
return ( | ||
<div className={styles.metricsContainer}> | ||
{isBedManagementModuleInstalled ? ( | ||
wardMetrics.map((wardMetric) => { | ||
return ( | ||
<WardMetric | ||
metricName={wardMetric.name} | ||
metricValue={wardMetricValues[wardMetric.key]} | ||
isLoading={!!isLoading} | ||
key={wardMetric.key} | ||
/> | ||
); | ||
}) | ||
) : ( | ||
<WardMetric metricName={'Patients'} metricValue={'--'} isLoading={false} key={'patients'} /> | ||
)} | ||
{isBedManagementModuleInstalled && ( | ||
<WardMetric | ||
metricName="Pending out" | ||
metricValue={error ? '--' : wardPatientGroup?.wardPatientPendingCount.toString() ?? '--'} | ||
isLoading={!wardPatientGroup} | ||
key="pending" | ||
/> | ||
)} | ||
</div> | ||
); | ||
}; | ||
|
||
export default WardMetrics; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
@use '@carbon/styles/scss/spacing'; | ||
@import '~@openmrs/esm-styleguide/src/vars'; | ||
|
||
.metricsContainer { | ||
display: flex; | ||
align-items: end; | ||
margin-left: auto; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import React from 'react'; | ||
import WardMetrics from './ward-metrics.component'; | ||
import { renderWithSwr } from '../../../../tools/test-utils'; | ||
import { useBeds } from '../hooks/useBeds'; | ||
import { mockWardBeds } from '../../../../__mocks__/wardBeds.mock'; | ||
import { getWardMetrics } from '../ward-view/ward-view.resource'; | ||
import { useAdmissionLocation } from '../hooks/useAdmissionLocation'; | ||
import { mockAdmissionLocation, mockInpatientAdmissions } from '__mocks__'; | ||
import { useInpatientAdmission } from '../hooks/useInpatientAdmission'; | ||
import useWardLocation from '../hooks/useWardLocation'; | ||
import { screen } from '@testing-library/react'; | ||
|
||
jest.mock('react-router-dom', () => ({ | ||
...jest.requireActual('react-router-dom'), | ||
useParams: jest.fn().mockReturnValue({}), | ||
})); | ||
|
||
jest.mock('../hooks/useWardLocation', () => | ||
jest.fn().mockReturnValue({ | ||
location: { uuid: 'abcd', display: 'mock location' }, | ||
isLoadingLocation: false, | ||
errorFetchingLocation: null, | ||
invalidLocation: false, | ||
}), | ||
); | ||
|
||
const mockUseWardLocation = jest.mocked(useWardLocation); | ||
|
||
jest.mock('../hooks/useBeds', () => ({ | ||
useBeds: jest.fn(), | ||
})); | ||
|
||
jest.mock('../hooks/useAdmissionLocation', () => ({ | ||
useAdmissionLocation: jest.fn(), | ||
})); | ||
jest.mock('../hooks/useInpatientAdmission', () => ({ | ||
useInpatientAdmission: jest.fn(), | ||
})); | ||
|
||
jest.mocked(useBeds).mockReturnValue({ | ||
error: undefined, | ||
mutate: jest.fn(), | ||
isValidating: false, | ||
isLoading: false, | ||
beds: mockWardBeds, | ||
}); | ||
|
||
jest.mocked(useAdmissionLocation).mockReturnValue({ | ||
error: undefined, | ||
mutate: jest.fn(), | ||
isValidating: false, | ||
isLoading: false, | ||
admissionLocation: mockAdmissionLocation, | ||
}); | ||
jest.mocked(useInpatientAdmission).mockReturnValue({ | ||
error: undefined, | ||
mutate: jest.fn(), | ||
isValidating: false, | ||
isLoading: false, | ||
inpatientAdmissions: mockInpatientAdmissions, | ||
}); | ||
|
||
describe('Ward Metrics', () => { | ||
it('Should display metrics of in the ward ', () => { | ||
mockUseWardLocation.mockReturnValueOnce({ | ||
location: null, | ||
isLoadingLocation: false, | ||
errorFetchingLocation: null, | ||
invalidLocation: true, | ||
}); | ||
const bedMetrics = getWardMetrics(mockWardBeds); | ||
renderWithSwr(<WardMetrics />); | ||
for (let [key, value] of Object.entries(bedMetrics)) { | ||
expect(screen.getByText(value)).toBeInTheDocument(); | ||
} | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,5 +4,4 @@ | |
margin: layout.$spacing-05 0; | ||
display: flex; | ||
align-items: center; | ||
justify-content: space-between; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How does this relate to the existing
mockAdmissionLocation
mock in the wards.mock file? Can we just use that other existing mock?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Couple of properties such as id and uuid are named differently in
mockAdmissionLocation
for beds with some additional properties,so i just created a separate mock for beds.