-
Notifications
You must be signed in to change notification settings - Fork 347
O3-3210 ward app - configuration system for ward patient cards #1184
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
Merged
Changes from all commits
Commits
Show all changes
24 commits
Select commit
Hold shift + click to select a range
091498f
configure-patient-header
kb019 9260f40
WIP patient header address extension
chibongho 8f7265e
configure-patient-header
kb019 28f952d
Merge remote-tracking branch 'origin/main' into ward
chibongho 5a612cf
WIP
chibongho 895b97c
WIP
chibongho 525258a
restructure config-schema
kb019 bccda94
correct tests
kb019 693f126
added tests for occupied-bed
kb019 577059c
Merge remote-tracking branch 'origin/main' into ward
chibongho 77d3d47
stash
chibongho 4d87d4f
WIP
chibongho 3d3232b
Merge remote-tracking branch 'origin/main' into ward
chibongho 2a316be
remove WIP admission workspace for purpose of PR
chibongho 4d2cae4
ready for PR
chibongho 2bd3210
address a few PR comments
chibongho 1266781
rename bento box to patient card row
chibongho a6b19f9
remove WardPatientStatus type
chibongho a00bacc
provide default value for addressFields
chibongho d3bcb20
update config to allow for multiple rows
chibongho a2f9640
misc
chibongho 92516fe
Update packages/esm-ward-app/src/beds/occupied-bed.test.tsx
chibongho 6bade24
Merge branch 'main' into O3-3210
mogoodrich 9db705c
Merge branch 'main' into O3-3210
jayasanka-sack File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
2 changes: 1 addition & 1 deletion
2
...app/src/empty-beds/empty-bed-skeleton.tsx → ...-ward-app/src/beds/empty-bed-skeleton.tsx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| import React from 'react'; | ||
| import styles from './empty-bed.scss'; | ||
| import wardPatientCardStyles from '../ward-patient-card/ward-patient-card.scss'; | ||
| import { type Bed } from '../types'; | ||
| import { useTranslation } from 'react-i18next'; | ||
|
|
||
| interface EmptyBedProps { | ||
| bed: Bed; | ||
| } | ||
|
|
||
| const EmptyBed: React.FC<EmptyBedProps> = ({ bed }) => { | ||
| const { t } = useTranslation(); | ||
|
|
||
| return ( | ||
| <div className={styles.container}> | ||
| <span className={`${wardPatientCardStyles.wardPatientBedNumber} ${wardPatientCardStyles.empty}`}> | ||
| {bed.bedNumber} | ||
| </span> | ||
| <p className={styles.emptyBed}>{t('emptyBed', 'Empty bed')}</p> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| export default EmptyBed; |
File renamed without changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| import React from 'react'; | ||
| import { type Patient } from '@openmrs/esm-framework'; | ||
| import { type Bed } from '../types'; | ||
| import styles from './occupied-bed.scss'; | ||
| import { Tag } from '@carbon/react'; | ||
| import { useTranslation } from 'react-i18next'; | ||
| import WardPatientCard from '../ward-patient-card/ward-patient-card'; | ||
|
|
||
| export interface OccupiedBedProps { | ||
| patients: Patient[]; | ||
| bed: Bed; | ||
| } | ||
| const OccupiedBed: React.FC<OccupiedBedProps> = ({ patients, bed }) => { | ||
| return ( | ||
| <div className={styles.occupiedBed}> | ||
| {patients.map((patient, index: number) => { | ||
| const last = index === patients.length - 1; | ||
| return ( | ||
| <div key={'occupied-bed-pt-' + patient.uuid}> | ||
| <WardPatientCard patient={patient} bed={bed} /> | ||
| {!last && <BedShareDivider />} | ||
| </div> | ||
| ); | ||
| })} | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| const BedShareDivider = () => { | ||
| const { t } = useTranslation(); | ||
| return ( | ||
| <div className={styles.bedDivider}> | ||
| <div className={styles.bedDividerLine}></div> | ||
| <Tag>{t('bedShare', 'Bed share')}</Tag> | ||
| <div className={styles.bedDividerLine}></div> | ||
| </div> | ||
| ); | ||
| }; | ||
|
|
||
| export default OccupiedBed; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,24 @@ | ||
| @use '@carbon/styles/scss/spacing'; | ||
| @use '@carbon/styles/scss/type'; | ||
| @use '@openmrs/esm-styleguide/src/vars'; | ||
|
|
||
| .occupiedBed { | ||
| display: flex; | ||
| flex-direction: column; | ||
| background-color: vars.$ui-02; | ||
| } | ||
|
|
||
| .bedDivider { | ||
| background-color: vars.$ui-02; | ||
| color: vars.$text-02; | ||
| padding: spacing.$spacing-01; | ||
| display: flex; | ||
| align-items: center; | ||
| justify-content: space-between; | ||
| } | ||
|
|
||
| .bedDividerLine { | ||
| height: 1px; | ||
| background-color: vars.$ui-03; | ||
| width: 30%; | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| import { render, screen } from '@testing-library/react'; | ||
| import OccupiedBed from './occupied-bed.component'; | ||
| import React from 'react'; | ||
| import { mockAdmissionLocation } from '../../../../__mocks__/wards.mock'; | ||
| import { bedLayoutToBed, filterBeds } from '../ward-view/ward-view.resource'; | ||
| import { getDefaultsFromConfigSchema, useConfig } from '@openmrs/esm-framework'; | ||
| import { configSchema, defaultPatientCardElementConfig } from '../config-schema'; | ||
|
|
||
| const defaultConfig = getDefaultsFromConfigSchema(configSchema); | ||
|
|
||
| jest.mocked(useConfig).mockReturnValue(defaultConfig); | ||
|
|
||
| const mockBedLayouts = filterBeds(mockAdmissionLocation); | ||
|
|
||
| const mockBedToUse = mockBedLayouts[0]; | ||
| jest.replaceProperty(mockBedToUse.patient.person, 'preferredName', { | ||
| uuid: '', | ||
| givenName: 'Alice', | ||
| familyName: 'Johnson', | ||
| }); | ||
| const mockPatient = mockBedToUse.patient; | ||
| const mockBed = bedLayoutToBed(mockBedToUse); | ||
|
|
||
| describe('Occupied bed: ', () => { | ||
| it('renders a single bed with patient details', () => { | ||
| render(<OccupiedBed patients={[mockPatient]} bed={mockBed} />); | ||
| const patientName = screen.getByText('Alice Johnson'); | ||
| expect(patientName).toBeInTheDocument(); | ||
| const patientAge = `${mockPatient.person.age} yrs`; | ||
| expect(screen.getByText(patientAge)).toBeInTheDocument(); | ||
| const defaultAddressFields = defaultPatientCardElementConfig.addressFields; | ||
| defaultAddressFields.forEach((addressField) => { | ||
| const addressFieldValue = mockPatient.person.preferredAddress[addressField] as string; | ||
| expect(screen.getByText(addressFieldValue)).toBeInTheDocument(); | ||
| }); | ||
| }); | ||
|
|
||
| it('renders a divider for shared patients', () => { | ||
| render(<OccupiedBed patients={[mockPatient, mockPatient]} bed={mockBed} />); | ||
| const bedShareText = screen.getByTitle('Bed share'); | ||
| expect(bedShareText).toBeInTheDocument(); | ||
| }); | ||
| }); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,136 @@ | ||
| import { type ConfigSchema } from '@openmrs/esm-framework'; | ||
| import { Type, validators, type ConfigSchema, type PersonAddress } from '@openmrs/esm-framework'; | ||
| import { patientCardElementTypes, type PatientCardElementType } from './types'; | ||
|
|
||
| export const configSchema: ConfigSchema = {}; | ||
| const defaultWardPatientCard: WardPatientCardDefinition = { | ||
| id: 'default-card', | ||
| rows: [ | ||
| { | ||
| rowType: 'header', | ||
| elements: ['bed-number', 'patient-name', 'patient-age', 'patient-address'], | ||
| }, | ||
| ], | ||
| appliedTo: null, | ||
| }; | ||
|
|
||
| export interface ConfigObject {} | ||
| const defaultPatientAddressFields: Array<keyof PersonAddress> = ['cityVillage', 'country']; | ||
|
|
||
| export const defaultPatientCardElementConfig: PatientCardElementConfig = { | ||
| addressFields: defaultPatientAddressFields, | ||
| }; | ||
|
|
||
| export const builtInPatientCardElements: PatientCardElementType[] = [ | ||
| 'bed-number', | ||
| 'patient-name', | ||
| 'patient-age', | ||
| 'patient-address', | ||
| 'admission-time', | ||
| ]; | ||
|
|
||
| export const configSchema: ConfigSchema = { | ||
| wardPatientCards: { | ||
| _description: 'Configure the display of ward patient cards', | ||
| patientCardElementDefinitions: { | ||
| _type: Type.Array, | ||
| _default: [], | ||
| _elements: { | ||
| id: { | ||
| _type: Type.String, | ||
| _description: 'The unique identifier for this custom patient card element', | ||
| }, | ||
| elementType: { | ||
| _type: Type.String, | ||
| _description: 'The patient card element type', | ||
| _validators: [validators.oneOf(patientCardElementTypes)], | ||
| }, | ||
| config: { | ||
| addressFields: { | ||
| _type: Type.Array, | ||
| _description: 'For patientCardElementType "patient-address", defining which address fields to show', | ||
| _default: defaultPatientAddressFields, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| cardDefinitions: { | ||
| _type: Type.Array, | ||
| _default: [defaultWardPatientCard], | ||
| _description: `An array of card configuration. A card configuration can be applied to different ward locations. | ||
| If multiple card configurations apply to a location, only the first one is chosen.`, | ||
| _elements: { | ||
| id: { | ||
| _type: Type.String, | ||
| _description: 'The unique identifier for this card definition. Currently unused, but that might change.', | ||
| }, | ||
| rows: { | ||
| _type: Type.Array, | ||
| _elements: { | ||
| id: { | ||
| _type: Type.String, | ||
| _description: 'The unique identifier for this card row. Currently unused, but that might change.', | ||
| }, | ||
| elements: { | ||
| _type: Type.Array, | ||
| _element: { | ||
| _type: Type.String, | ||
| _description: 'The ID of the (bulit-in or custom) patient card elements to appear in this card row', | ||
| _validators: [validators.oneOf(patientCardElementTypes)], | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| appliedTo: { | ||
| _type: Type.Array, | ||
| _elements: { | ||
| location: { | ||
| _type: Type.UUID, | ||
| _description: 'The UUID of the location. If not provided, applies to all queues.', | ||
| _default: null, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }, | ||
| }; | ||
|
|
||
| export interface WardConfigObject { | ||
| wardPatientCards: WardPatientCardsConfig; | ||
| } | ||
|
|
||
| export interface WardPatientCardsConfig { | ||
| patientCardElementDefinitions: Array<PatientCardElementDefinition>; | ||
| cardDefinitions: Array<WardPatientCardDefinition>; | ||
| } | ||
|
|
||
| export interface WardPatientCardDefinition { | ||
| id: string; | ||
| rows: Array<{ | ||
| /** | ||
| * The type of row. Currently, only "header" is supported | ||
| */ | ||
| rowType: 'header'; | ||
|
|
||
| /** | ||
| * an array of (either built-in or custom) patient card element ids | ||
| */ | ||
| elements: Array<string>; | ||
| }>; | ||
| appliedTo?: Array<{ | ||
| /** | ||
| * locationUuid. If given, only applies to patients at the specified ward locations. (If not provided, applies to all locations) | ||
| */ | ||
| location: string; | ||
| }>; | ||
| } | ||
|
|
||
| export type PatientCardElementDefinition = { | ||
| id: string; | ||
| elementType: PatientCardElementType; | ||
| config?: PatientCardElementConfig; | ||
| }; | ||
|
|
||
| export interface PatientAddressElementConfig { | ||
| addressFields: Array<keyof PersonAddress>; | ||
| } | ||
|
|
||
| export type PatientCardElementConfig = {} & PatientAddressElementConfig; | ||
19 changes: 0 additions & 19 deletions
19
packages/esm-ward-app/src/empty-beds/empty-bed.component.tsx
This file was deleted.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
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.
I really don't like this here. A specific type of
patientCardElementshould have a configuration that takes in an address field. Just like other types ofpatientCardElement(like Gravity) might take in aConcept. This should not be baked into a generic config schema.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.
A similar change was made to the queues app. I believe it was to facilitate adding validators on the fields. I'm not sure if it's possible to have a validator work by defining specific configs per element type, but I can play around with it.
@brandones what do you think?
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.
@mseaton This is the way we handle this kind of thing in registration and service queues; it's the best pattern we've figured out for this.
@chibongho You will definitely want to add a
_defaultvalue foraddressFieldsso that it is optional, since indeed it would be very bad if we required implementers to configureaddressFieldsfor an element of typebed-number. We want implementers to be able to provide config like{ "patientCardElementDefinitions": [ { "id": "bednum", "elementType": "bed-number" }, { "id": "addr", "elementType": "patient-address", "config": { "addressFields": ["cityVillage"] } } ] }and then we can add validators that ensure that implementers don't provide an element config that doesn't make sense with the element type.
@mseaton The only way I can imagine supporting what you're talking about would be to have a separate definitions array for each type:
{ "patientCardBedNumberElementDefinitions": [ { "id": "bednum" } ], "patientCardPatientAddressElementDefinitions": [ { "id": "addr", "addressFields": ["cityVillage"] } ] }So that would be our other option here.
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.
Given the frequency with which this pattern appears, I'm definitely open to thinking about whether we can add a schema feature that supports it better. It would be really nice if the config system had some way of knowing what element config schema should be used based on the element type.
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.
Quick first pass. This usually comes up in the context of arrays of elements right? Maybe for arrays we can support something like the following:
That could be typed like: