Skip to content

Commit

Permalink
feat create patient notes workspace
Browse files Browse the repository at this point in the history
  • Loading branch information
usamaidrsk committed Jul 18, 2024
1 parent 20499f9 commit f4522e8
Show file tree
Hide file tree
Showing 12 changed files with 231 additions and 39 deletions.
13 changes: 12 additions & 1 deletion packages/esm-ward-app/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { configSchema } from './config-schema';
import rootComponent from './root.component';
import { moduleName } from './constant';
import WardPatientActionButton from './ward-patient-workspace/ward-patient-action-button.extension';
import WardPatientNotesActionButton from './ward-workspace/ward-patient-notes/notes-action-button.extension';

export const importTranslation = require.context('../translations', false, /.json$/, 'lazy');

Expand All @@ -24,10 +25,20 @@ export const admissionRequestWorkspace = getAsyncLifecycle(
options,
);

export const wardPatientWorkspace = getAsyncLifecycle(() => import('./ward-patient-workspace/ward-patient.workspace'), options);
export const wardPatientWorkspace = getAsyncLifecycle(
() => import('./ward-patient-workspace/ward-patient.workspace'),
options,
);

export const wardPatientNotesWorkspace = getAsyncLifecycle(
() => import('./ward-workspace/ward-patient-notes/notes.workspace'),
options,
);

export const wardPatientActionButtonExtension = getSyncLifecycle(WardPatientActionButton, options);

export const wardPatientNotesActionButtonExtension = getSyncLifecycle(WardPatientNotesActionButton, options);

export function startupApp() {
registerBreadcrumbs([]);
defineConfigSchema(moduleName, configSchema);
Expand Down
57 changes: 37 additions & 20 deletions packages/esm-ward-app/src/routes.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,58 @@
"webservices.rest": "^2.2.0",
"emrapi": "^2.0.0 || 2.0.0-SNAPSHOT"
},
"optionalBackendDependencies":{
"bedmanagement":{
"optionalBackendDependencies": {
"bedmanagement": {
"version": "^6.0.0 || 6.0.0-SNAPSHOT",
"feature": {
"flagName": "bedmanagement-module",
"label":"Ward App Patient Service",
"label": "Ward App Patient Service",
"description": "This module, if installed, provides services for managing patients admitted to the ward."
}
}
}
},
"pages": [
{
"component": "root",
"route": "ward"
}
],
"extensions": [{
"component": "wardPatientActionButtonExtension",
"name": "ward-patient-action-button",
"slot": "action-menu-ward-patient-items-slot"
}],
"extensions": [
{
"component": "wardPatientActionButtonExtension",
"name": "ward-patient-action-button",
"slot": "action-menu-ward-patient-items-slot"
},
{
"component": "wardPatientNotesActionButtonExtension",
"name": "ward-in-patient-notes-action-button",
"slot": "action-menu-ward-patient-items-slot"
}
],
"workspaces": [
{
"name":"admission-requests-workspace",
"name": "admission-requests-workspace",
"component": "admissionRequestWorkspace",
"title":"admissionRequests",
"type":"admission-requests"
"title": "admissionRequests",
"type": "admission-requests"
},
{
"name": "ward-patient-notes-workspace",
"component": "wardPatientNotesWorkspace",
"type": "ward-patient-notes",
"title": "In-patient notes",
"sidebarFamily": "ward-patient",
"width": "wider",
"hasOwnSidebar": true
},
{
"name": "ward-patient-workspace",
"component": "wardPatientWorkspace",
"type": "ward",
"title": "Ward Patient",
"width": "extra-wide",
"hasOwnSidebar": true,
"sidebarFamily": "ward-patient"
}]
"name": "ward-patient-workspace",
"component": "wardPatientWorkspace",
"type": "ward",
"title": "Ward Patient",
"width": "extra-wide",
"hasOwnSidebar": true,
"sidebarFamily": "ward-patient"
}
]
}
18 changes: 18 additions & 0 deletions packages/esm-ward-app/src/store.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import { createGlobalStore, getGlobalStore } from '@openmrs/esm-framework';
import { type WardPatientCardProps } from './types';

type ActiveBedSelection = WardPatientCardProps;

interface WardStoreState {
activeBedSelection: ActiveBedSelection | null;
}

const initialState: WardStoreState = {
activeBedSelection: null,
};

export const wardStore = createGlobalStore('ward', initialState);

export function getWardStore() {
return getGlobalStore<WardStoreState>('ward', initialState);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { useConfig } from '@openmrs/esm-framework';
import { useMemo } from 'react';
import React, { useMemo } from 'react';
import {
builtInPatientCardElements,
defaultPatientCardElementConfig,
Expand All @@ -11,7 +11,6 @@ import WardPatientAge from './row-elements/ward-patient-age';
import WardPatientBedNumber from './row-elements/ward-patient-bed-number';
import wardPatientAddress from './row-elements/ward-patient-header-address';
import WardPatientName from './row-elements/ward-patient-name';
import React from 'react';
import styles from './ward-patient-card.scss';
import wardPatientObs from './row-elements/ward-patient-obs';
import wardPatientCodedObsTags from './row-elements/ward-patient-coded-obs-tags';
Expand Down Expand Up @@ -68,7 +67,7 @@ export function usePatientCardRows(location: string) {
return patientCardRows;
}

function getPatientCardElementFromDefinition(
export function getPatientCardElementFromDefinition(
patientCardElementDef: PatientCardElementDefinition,
): WardPatientCardElement {
const { elementType, config } = patientCardElementDef;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@
}
}

.activeWardPatientCardButton {
outline: 2px solid $interactive-01;
}

.wardPatientCardRow {
width: 100%;
padding: spacing.$spacing-04;
Expand All @@ -62,6 +66,7 @@
.wardPatientName {
@include type.type-style('heading-compact-02');
color: $text-02;

&::before {
content: '' !important;
}
Expand All @@ -78,6 +83,7 @@
display: flex;
justify-content: center;
align-items: center;

&.empty {
background-color: $color-blue-10;
color: $color-blue-60-2;
Expand Down Expand Up @@ -106,6 +112,7 @@
> div:not(div:first-of-type) {
display: flex;
align-items: center;

&::before {
content: '·';
padding: 0 spacing.$spacing-02;
Expand Down
19 changes: 14 additions & 5 deletions packages/esm-ward-app/src/ward-patient-card/ward-patient-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,14 @@ import { type WardPatientCardProps } from '../types';
import { usePatientCardRows } from './ward-patient-card-row.resources';
import styles from './ward-patient-card.scss';
import { getPatientName, launchWorkspace } from '@openmrs/esm-framework';
import { type WardPatientWorkspaceProps } from '../ward-patient-workspace/ward-patient.workspace';
import { getWardStore } from '../store';
import classNames from 'classnames';

const spaRoot = window['getOpenmrsSpaBase'];

const WardPatientCard: React.FC<WardPatientCardProps> = (props) => {
const wardStore = getWardStore();
const activeBedSelection = wardStore.getState().activeBedSelection;
const { locationUuid } = useParams();
const patientCardRows = usePatientCardRows(locationUuid);

Expand All @@ -16,10 +21,14 @@ const WardPatientCard: React.FC<WardPatientCardProps> = (props) => {
<WardPatientCardRow key={i} {...props} />
))}
<button
className={styles.wardPatientCardButton}
onClick={() =>
launchWorkspace<WardPatientWorkspaceProps>('ward-patient-workspace', { patientUuid: props.patient.uuid })
}>
className={classNames(styles.wardPatientCardButton, {
// []: activeBedSelection?.bed.uuid !== props.bed.uuid,
[styles.activeWardPatientCard]: activeBedSelection?.bed.uuid === props.bed.uuid,
})}
onClick={() => {
wardStore.setState({ activeBedSelection: { ...props } });
launchWorkspace('ward-patient-workspace');
}}>
{/* Name will not be displayed; just there for a11y */}
{getPatientName(props.patient.person)}
</button>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
import React, { useEffect, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { InlineNotification } from '@carbon/react';
import { InlineLoading } from '@carbon/react';
import { InlineLoading, InlineNotification } from '@carbon/react';
import {
age,
attach,
type DefaultWorkspaceProps,
ExtensionSlot,
attach,
getPatientName,
usePatient,
age,
} from '@openmrs/esm-framework';
import styles from './ward-patient.style.scss';
import { getWardStore } from '../store';

attach('ward-patient-workspace-header-slot', 'patient-vitals-info');

export interface WardPatientWorkspaceProps extends DefaultWorkspaceProps {
patientUuid: string;
}

export default function WardPatientWorkspace({ patientUuid, setTitle }: WardPatientWorkspaceProps) {
export default function WardPatientWorkspace({ setTitle, setOnCloseCallback }: DefaultWorkspaceProps) {
const { t } = useTranslation();
const { patient, isLoading, error } = usePatient(patientUuid);
const wardStore = getWardStore();
const {
patient: { uuid },
} = wardStore.getState().activeBedSelection;
const { patient, isLoading, error } = usePatient(uuid);

setOnCloseCallback(() => {
wardStore.setState({ activeBedSelection: null });
});

useEffect(() => {
if (isLoading) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import React, { useMemo } from 'react';
import { useConfig } from '@openmrs/esm-framework';
import { defaultPatientCardElementConfig, type WardConfigObject } from '../../config-schema';
import { useParams } from 'react-router-dom';
import { getPatientCardElementFromDefinition } from '../../ward-patient-card/ward-patient-card-row.resources';
import type { PatientCardElementType, WardPatientCardProps } from '../../types';
import styles from './style.scss';

const WardPatientWorkspaceBanner = ({ bed, patient, visit }: WardPatientCardProps) => {
const { locationUuid } = useParams();
const { wardPatientCards } = useConfig<WardConfigObject>();
const { cardDefinitions } = wardPatientCards;

// extract configured elements for the patient card header to use for the banner section
const bannerElements = useMemo(() => {
const cardDefinition = cardDefinitions.find((cardDef) => {
const appliedTo = cardDef.appliedTo;

return appliedTo == null || appliedTo.some((criteria) => criteria.location == locationUuid);
});

const headerRow = cardDefinition.rows.find((cardDef) => cardDef.rowType === 'header');

return headerRow.elements.map((elementType: PatientCardElementType) =>
getPatientCardElementFromDefinition({
id: elementType,
elementType,
config: defaultPatientCardElementConfig,
}),
);
}, [cardDefinitions]);

return (
<div className={styles.patientBanner}>
{bannerElements.map((BannerElement) => (
<BannerElement patient={patient} visit={visit} bed={bed} />
))}
</div>
);
};

export default WardPatientWorkspaceBanner;
23 changes: 23 additions & 0 deletions packages/esm-ward-app/src/ward-workspace/patient-banner/style.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
@use '@carbon/styles/scss/spacing';
@import '~@openmrs/esm-styleguide/src/vars';

.patientBanner {
@extend .dotSeparatedChildren;
display: flex;
flex-wrap: wrap;
width: 100%;
padding: spacing.$spacing-04;
background: $ui-01;
}

.dotSeparatedChildren {
> div:not(div:first-of-type) {
display: flex;
align-items: center;

&::before {
content: '·';
padding: 0 spacing.$spacing-02;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import React from 'react';
import { useTranslation } from 'react-i18next';
import { ActionMenuButton, launchWorkspace, StickyNoteAddIcon } from '@openmrs/esm-framework';

export default function WardPatientNotesActionButton({ workspaceProps }: { workspaceProps: any }) {
const { t } = useTranslation();

return (
<ActionMenuButton
getIcon={(props) => <StickyNoteAddIcon {...props} size={16} />}
label={t('PatientNote', 'Patient Note')}
iconDescription={t('PatientNote', 'Patient Note')}
handler={() => launchWorkspace('ward-patient-notes-workspace')}
type={'ward-patient-notes'}
/>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
@import '~@openmrs/esm-styleguide/src/vars';

.workspaceContainer {
width: calc(100% - 2rem);
min-height: var(--desktop-workspace-window-height);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React, { useMemo } from 'react';
import styles from './notes.style.scss';
import { type DefaultWorkspaceProps, ExtensionSlot } from '@openmrs/esm-framework';
import { getWardStore } from '../../store';
import WardPatientWorkspaceBanner from '../patient-banner/patient-banner.component';

const WardPatientNotesWorkspace = ({
promptBeforeClosing,
closeWorkspace,
closeWorkspaceWithSavedChanges,
setOnCloseCallback,
}: DefaultWorkspaceProps) => {
const wardStore = getWardStore();
const { patient, visit, bed } = wardStore.getState().activeBedSelection;

setOnCloseCallback(() => {
wardStore.setState({ activeBedSelection: null });
});

const notesFormExtensionState = useMemo(
() => ({
patient,
patientUuid: patient.uuid,
promptBeforeClosing,
closeWorkspace,
closeWorkspaceWithSavedChanges,
}),
[patient, closeWorkspace, closeWorkspaceWithSavedChanges, promptBeforeClosing],
);

return (
<div className={styles.workspaceContainer}>
<WardPatientWorkspaceBanner patient={patient} bed={bed} visit={visit} />
<ExtensionSlot name="ward-patient-notes-workspace-slot" state={notesFormExtensionState} />
</div>
);
};

export default WardPatientNotesWorkspace;

0 comments on commit f4522e8

Please sign in to comment.