@kenyaemr/esm-ward-app 8.0.1-pre.99 → 8.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (196) hide show
  1. package/.turbo/turbo-build.log +20 -24
  2. package/dist/109.js +1 -0
  3. package/dist/109.js.map +1 -0
  4. package/dist/124.js +1 -0
  5. package/dist/124.js.map +1 -0
  6. package/dist/125.js +1 -0
  7. package/dist/125.js.map +1 -0
  8. package/dist/130.js +1 -1
  9. package/dist/130.js.LICENSE.txt +2 -0
  10. package/dist/130.js.map +1 -1
  11. package/dist/146.js +1 -0
  12. package/dist/146.js.map +1 -0
  13. package/dist/15.js +1 -0
  14. package/dist/15.js.map +1 -0
  15. package/dist/153.js +1 -0
  16. package/dist/153.js.map +1 -0
  17. package/dist/303.js +2 -1
  18. package/dist/303.js.map +1 -1
  19. package/dist/325.js +1 -0
  20. package/dist/325.js.map +1 -0
  21. package/dist/372.js +2 -0
  22. package/dist/372.js.map +1 -0
  23. package/dist/471.js +1 -0
  24. package/dist/471.js.map +1 -0
  25. package/dist/481.js +1 -0
  26. package/dist/481.js.map +1 -0
  27. package/dist/53.js +1 -0
  28. package/dist/53.js.map +1 -0
  29. package/dist/{960.js → 559.js} +1 -1
  30. package/dist/559.js.map +1 -0
  31. package/dist/574.js +1 -1
  32. package/dist/576.js +1 -0
  33. package/dist/576.js.map +1 -0
  34. package/dist/577.js +1 -1
  35. package/dist/577.js.map +1 -1
  36. package/dist/{255.js → 649.js} +2 -2
  37. package/dist/649.js.LICENSE.txt +9 -0
  38. package/dist/649.js.map +1 -0
  39. package/dist/{659.js → 662.js} +1 -1
  40. package/dist/662.js.map +1 -0
  41. package/dist/920.js +1 -0
  42. package/dist/920.js.map +1 -0
  43. package/dist/921.js +1 -0
  44. package/dist/921.js.map +1 -0
  45. package/dist/922.js +1 -0
  46. package/dist/922.js.map +1 -0
  47. package/dist/969.js +1 -0
  48. package/dist/969.js.map +1 -0
  49. package/dist/kenyaemr-esm-ward-app.js +1 -1
  50. package/dist/kenyaemr-esm-ward-app.js.buildmanifest.json +304 -128
  51. package/dist/kenyaemr-esm-ward-app.js.map +1 -1
  52. package/dist/main.js +1 -1
  53. package/dist/main.js.LICENSE.txt +0 -10
  54. package/dist/main.js.map +1 -1
  55. package/dist/routes.json +1 -1
  56. package/mock.tsx +62 -0
  57. package/package-lock.json +5001 -0
  58. package/package.json +2 -3
  59. package/src/action-menu-buttons/clinical-forms-workspace-siderail.component.tsx +37 -0
  60. package/src/action-menu-buttons/discharge-workspace-siderail.component.tsx +20 -0
  61. package/src/beds/empty-bed-skeleton.tsx +4 -3
  62. package/src/beds/empty-bed.component.tsx +3 -3
  63. package/src/beds/ward-bed.component.tsx +41 -0
  64. package/src/beds/ward-bed.scss +45 -0
  65. package/src/beds/{occupied-bed.test.tsx → ward-bed.test.tsx} +42 -20
  66. package/src/config-schema.ts +203 -84
  67. package/src/constant.ts +1 -1
  68. package/src/hooks/useAdmissionLocation.ts +22 -4
  69. package/src/hooks/useAssignedBedByPatient.ts +9 -0
  70. package/src/hooks/useBeds.ts +3 -4
  71. package/src/hooks/useConcept.ts +3 -4
  72. package/src/hooks/useEmrConfiguration.ts +5 -0
  73. package/src/hooks/useInpatientAdmission.ts +9 -14
  74. package/src/hooks/useInpatientRequest.ts +4 -15
  75. package/src/hooks/useLocations.ts +8 -51
  76. package/src/hooks/useMotherAndChildren.ts +46 -0
  77. package/src/hooks/useObs.ts +3 -7
  78. package/src/hooks/usePatientPendingOrders.ts +16 -0
  79. package/src/hooks/useWardPatientGrouping.ts +32 -0
  80. package/src/index.ts +45 -17
  81. package/src/location-selector/location-selector.component.tsx +18 -21
  82. package/src/root.component.tsx +3 -0
  83. package/src/routes.json +41 -3
  84. package/src/types/index.ts +50 -1
  85. package/src/ward-patient-card/card-rows/admission-request-note-row.component.tsx +38 -0
  86. package/src/ward-patient-card/card-rows/coded-obs-tags-row.component.tsx +108 -0
  87. package/src/ward-patient-card/card-rows/mother-child-row.component.tsx +84 -0
  88. package/src/ward-patient-card/card-rows/mother-child-row.scss +22 -0
  89. package/src/ward-patient-card/card-rows/pending-items-row.component.tsx +54 -0
  90. package/src/ward-patient-card/row-elements/ward-patient-age.tsx +1 -1
  91. package/src/ward-patient-card/row-elements/ward-patient-coded-obs-tags.tsx +62 -39
  92. package/src/ward-patient-card/row-elements/ward-patient-header-address.tsx +5 -5
  93. package/src/ward-patient-card/row-elements/ward-patient-identifier.tsx +18 -41
  94. package/src/ward-patient-card/row-elements/ward-patient-location.tsx +19 -0
  95. package/src/ward-patient-card/row-elements/ward-patient-obs.resource.ts +38 -34
  96. package/src/ward-patient-card/row-elements/ward-patient-obs.tsx +26 -13
  97. package/src/ward-patient-card/row-elements/ward-patient-pending-order.component.tsx +45 -0
  98. package/src/ward-patient-card/row-elements/ward-patient-pending-transfer.tsx +38 -0
  99. package/src/ward-patient-card/row-elements/ward-patient-responsive-tooltip.tsx +32 -0
  100. package/src/ward-patient-card/row-elements/ward-patient-skeleton-text.tsx +9 -0
  101. package/src/ward-patient-card/ward-patient-card.component.tsx +14 -45
  102. package/src/ward-patient-card/ward-patient-card.scss +68 -9
  103. package/src/ward-view/default-ward/default-ward-beds.component.tsx +42 -0
  104. package/src/ward-view/default-ward/default-ward-patient-card-header.component.tsx +32 -0
  105. package/src/ward-view/default-ward/default-ward-patient-card.component.tsx +31 -0
  106. package/src/ward-view/default-ward/default-ward-pending-patients.component.tsx +52 -0
  107. package/src/ward-view/default-ward/default-ward-unassigned-patients.component.tsx +32 -0
  108. package/src/ward-view/default-ward/default-ward-view.component.tsx +31 -0
  109. package/src/ward-view/materal-ward/maternal-ward-beds.component.tsx +65 -0
  110. package/src/ward-view/materal-ward/maternal-ward-patient-card-header.component.tsx +30 -0
  111. package/src/ward-view/materal-ward/maternal-ward-patient-card.component.tsx +93 -0
  112. package/src/{beds/occupied-bed.scss → ward-view/materal-ward/maternal-ward-patient-card.scss} +4 -9
  113. package/src/ward-view/materal-ward/maternal-ward-patient-card.test.tsx +58 -0
  114. package/src/ward-view/materal-ward/maternal-ward-pending-patients.component.tsx +48 -0
  115. package/src/ward-view/materal-ward/maternal-ward-unassigned-patients.component.tsx +33 -0
  116. package/src/ward-view/materal-ward/maternal-ward-view.component.tsx +38 -0
  117. package/src/ward-view/materal-ward/maternal-ward-view.resource.ts +89 -0
  118. package/src/ward-view/ward-view.component.tsx +15 -163
  119. package/src/ward-view/ward-view.resource.ts +193 -1
  120. package/src/ward-view/ward-view.scss +17 -6
  121. package/src/ward-view/ward-view.test.tsx +43 -48
  122. package/src/ward-view/ward.component.tsx +106 -0
  123. package/src/ward-view-header/admission-requests-bar.component.tsx +14 -9
  124. package/src/ward-view-header/admission-requests-bar.test.tsx +11 -23
  125. package/src/ward-view-header/admission-requests.scss +1 -1
  126. package/src/ward-view-header/ward-metric.component.tsx +24 -0
  127. package/src/ward-view-header/ward-metric.scss +25 -0
  128. package/src/ward-view-header/ward-metrics.component.tsx +78 -0
  129. package/src/ward-view-header/ward-metrics.scss +7 -0
  130. package/src/ward-view-header/ward-metrics.test.tsx +37 -0
  131. package/src/ward-view-header/ward-view-header.component.tsx +9 -4
  132. package/src/ward-view-header/ward-view-header.scss +0 -1
  133. package/src/ward-workspace/admission-request-card/admission-request-card-actions.component.tsx +70 -6
  134. package/src/ward-workspace/admission-request-card/admission-request-card-header.component.tsx +10 -23
  135. package/src/ward-workspace/admission-request-card/admission-request-card.component.tsx +9 -3
  136. package/src/ward-workspace/admission-request-card/admission-request-card.scss +13 -4
  137. package/src/ward-workspace/admission-request-workspace/admission-requests-workspace.test.tsx +55 -33
  138. package/src/ward-workspace/admission-request-workspace/admission-requests.workspace.tsx +30 -37
  139. package/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.test.tsx +98 -203
  140. package/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.workspace.tsx +116 -180
  141. package/src/ward-workspace/bed-selector.component.tsx +119 -0
  142. package/src/ward-workspace/bed-selector.scss +15 -0
  143. package/src/ward-workspace/patient-banner/patient-banner.component.tsx +7 -14
  144. package/src/ward-workspace/patient-clinical-forms-workspace/patient-clinical-forms.workspace.tsx +23 -0
  145. package/src/{ward-patient-workspace → ward-workspace/patient-details}/ward-patient-action-button.extension.tsx +2 -1
  146. package/src/{ward-patient-workspace → ward-workspace/patient-details}/ward-patient.workspace.tsx +18 -9
  147. package/src/ward-workspace/patient-discharge/patient-discharge.scss +41 -0
  148. package/src/ward-workspace/patient-discharge/patient-discharge.workspace.tsx +113 -0
  149. package/src/ward-workspace/patient-transfer-bed-swap/patient-bed-swap-form.component.tsx +68 -79
  150. package/src/ward-workspace/patient-transfer-bed-swap/patient-transfer-request-form.component.tsx +24 -24
  151. package/src/ward-workspace/patient-transfer-bed-swap/patient-transfer-swap.scss +12 -2
  152. package/src/ward-workspace/patient-transfer-bed-swap/patient-transfer-swap.workspace.tsx +12 -8
  153. package/src/ward-workspace/patient-transfer-request-workspace/patient-transfer-request.workspace.tsx +11 -0
  154. package/src/ward-workspace/ward-patient-notes/form/notes-form.component.tsx +1 -1
  155. package/src/ward-workspace/ward-patient-notes/form/notes-form.test.tsx +1 -1
  156. package/src/ward-workspace/ward-patient-notes/history/notes-container.component.tsx +2 -2
  157. package/src/ward-workspace/ward-patient-notes/notes.resource.ts +5 -7
  158. package/src/ward-workspace/ward-patient-notes/notes.workspace.tsx +1 -1
  159. package/src/ward-workspace/ward-patient-notes/types.ts +0 -4
  160. package/src/ward.resource.ts +38 -2
  161. package/translations/en.json +31 -7
  162. package/dist/152.js +0 -1
  163. package/dist/152.js.map +0 -1
  164. package/dist/255.js.map +0 -1
  165. package/dist/269.js +0 -1
  166. package/dist/269.js.map +0 -1
  167. package/dist/346.js +0 -1
  168. package/dist/346.js.map +0 -1
  169. package/dist/466.js +0 -1
  170. package/dist/466.js.map +0 -1
  171. package/dist/659.js.map +0 -1
  172. package/dist/729.js +0 -1
  173. package/dist/729.js.map +0 -1
  174. package/dist/749.js +0 -1
  175. package/dist/749.js.map +0 -1
  176. package/dist/76.js +0 -1
  177. package/dist/76.js.map +0 -1
  178. package/dist/793.js +0 -2
  179. package/dist/793.js.map +0 -1
  180. package/dist/803.js +0 -1
  181. package/dist/803.js.map +0 -1
  182. package/dist/960.js.map +0 -1
  183. package/src/beds/empty-bed.scss +0 -28
  184. package/src/beds/occupied-bed.component.tsx +0 -35
  185. package/src/beds/unassigned-patient.component.tsx +0 -20
  186. package/src/beds/unassigned-patient.scss +0 -6
  187. package/src/config-schema-admission-request-note.ts +0 -9
  188. package/src/config-schema-extension-colored-obs-tags.ts +0 -91
  189. package/src/hooks/useCurrentWardCardConfig.ts +0 -32
  190. package/src/ward-patient-card/card-rows/admission-request-note.extension.tsx +0 -27
  191. package/src/ward-patient-card/card-rows/colored-obs-tags-card-row.extension.tsx +0 -13
  192. package/src/ward-patient-card/ward-patient-card-element.component.tsx +0 -65
  193. package/src/ward-view/ward-bed.component.tsx +0 -14
  194. /package/dist/{793.js.LICENSE.txt → 303.js.LICENSE.txt} +0 -0
  195. /package/dist/{255.js.LICENSE.txt → 372.js.LICENSE.txt} +0 -0
  196. /package/src/{ward-patient-workspace → ward-workspace/patient-details}/ward-patient.style.scss +0 -0
@@ -0,0 +1,113 @@
1
+ import { Button, ButtonSet, InlineNotification } from '@carbon/react';
2
+ import { Exit } from '@carbon/react/icons';
3
+ import { ExtensionSlot, showSnackbar, useAppContext, useSession } from '@openmrs/esm-framework';
4
+ import React, { useCallback, useState } from 'react';
5
+ import { useTranslation } from 'react-i18next';
6
+ import useEmrConfiguration from '../../hooks/useEmrConfiguration';
7
+ import useWardLocation from '../../hooks/useWardLocation';
8
+ import { type WardPatientWorkspaceProps, type WardViewContext } from '../../types';
9
+ import { createEncounter, removePatientFromBed } from '../../ward.resource';
10
+ import WardPatientWorkspaceBanner from '../patient-banner/patient-banner.component';
11
+ import styles from './patient-discharge.scss';
12
+
13
+ export default function PatientDischargeWorkspace(props: WardPatientWorkspaceProps) {
14
+ const { wardPatient, closeWorkspaceWithSavedChanges } = props;
15
+ const { t } = useTranslation();
16
+ const [isSubmitting, setIsSubmitting] = useState<boolean>(false);
17
+ const { currentProvider } = useSession();
18
+ const { location } = useWardLocation();
19
+ const { emrConfiguration, isLoadingEmrConfiguration, errorFetchingEmrConfiguration } = useEmrConfiguration();
20
+ const { wardPatientGroupDetails } = useAppContext<WardViewContext>('ward-view-context') ?? {};
21
+
22
+ const submitDischarge = useCallback(() => {
23
+ setIsSubmitting(true);
24
+
25
+ createEncounter({
26
+ patient: wardPatient?.patient?.uuid,
27
+ encounterType: emrConfiguration.exitFromInpatientEncounterType.uuid,
28
+ location: location.uuid,
29
+ encounterProviders: [
30
+ {
31
+ encounterRole: emrConfiguration.clinicianEncounterRole.uuid,
32
+ provider: currentProvider?.uuid,
33
+ },
34
+ ],
35
+ obs: [],
36
+ })
37
+ .then((response) => {
38
+ if (response?.ok) {
39
+ if (wardPatient?.bed?.id) {
40
+ return removePatientFromBed(wardPatient.bed.id, wardPatient?.patient?.uuid);
41
+ }
42
+ return response;
43
+ }
44
+ })
45
+ .then((response) => {
46
+ if (response?.ok) {
47
+ showSnackbar({
48
+ title: t('patientWasDischarged', 'Patient was discharged'),
49
+ kind: 'success',
50
+ });
51
+ }
52
+ })
53
+ .catch((err: Error) => {
54
+ showSnackbar({
55
+ title: t('errorDischargingPatient', 'Error discharging patient'),
56
+ subtitle: err.message,
57
+ kind: 'error',
58
+ });
59
+ })
60
+ .finally(() => {
61
+ setIsSubmitting(false);
62
+ closeWorkspaceWithSavedChanges();
63
+ wardPatientGroupDetails.mutate();
64
+ });
65
+ }, [
66
+ currentProvider,
67
+ location,
68
+ emrConfiguration,
69
+ wardPatient?.patient?.uuid,
70
+ wardPatient?.bed?.uuid,
71
+ wardPatientGroupDetails,
72
+ ]);
73
+
74
+ if (!wardPatientGroupDetails) return <></>;
75
+ return (
76
+ <div className={styles.workspaceContent}>
77
+ <div className={styles.patientWorkspaceBanner}>
78
+ <WardPatientWorkspaceBanner {...props?.wardPatient} />
79
+ </div>
80
+ <div className={styles.workspaceForm}>
81
+ <div>
82
+ {errorFetchingEmrConfiguration && (
83
+ <div className={styles.formError}>
84
+ <InlineNotification
85
+ kind="error"
86
+ title={t('somePartsOfTheFormDidntLoad', "Some parts of the form didn't load")}
87
+ subtitle={t(
88
+ 'fetchingEmrConfigurationFailed',
89
+ 'Fetching EMR configuration failed. Try refreshing the page or contact your system administrator.',
90
+ )}
91
+ lowContrast
92
+ hideCloseButton
93
+ />
94
+ </div>
95
+ )}
96
+ </div>
97
+ <ExtensionSlot name="ward-patient-discharge-slot" />
98
+ <ButtonSet className={styles.buttonSet}>
99
+ <Button
100
+ size="sm"
101
+ kind="ghost"
102
+ renderIcon={(props) => <Exit size={16} {...props} />}
103
+ disabled={
104
+ isLoadingEmrConfiguration || isSubmitting || errorFetchingEmrConfiguration || !wardPatient?.patient
105
+ }
106
+ onClick={submitDischarge}>
107
+ {t('proceedWithPatientDischarge', 'Proceed with patient discharge')}
108
+ </Button>
109
+ </ButtonSet>
110
+ </div>
111
+ </div>
112
+ );
113
+ }
@@ -1,26 +1,17 @@
1
+ import { Button, ButtonSet, Form, InlineNotification } from '@carbon/react';
2
+ import { zodResolver } from '@hookform/resolvers/zod';
3
+ import { showSnackbar, useAppContext, useSession } from '@openmrs/esm-framework';
4
+ import classNames from 'classnames';
1
5
  import React, { useCallback, useEffect, useMemo, useState } from 'react';
2
- import styles from './patient-transfer-swap.scss';
3
- import { useAdmissionLocation } from '../../hooks/useAdmissionLocation';
4
- import { z } from 'zod';
5
- import { useTranslation } from 'react-i18next';
6
6
  import { Controller, useForm } from 'react-hook-form';
7
- import { zodResolver } from '@hookform/resolvers/zod';
8
- import { filterBeds } from '../../ward-view/ward-view.resource';
9
- import type { BedLayout, WardPatientWorkspaceProps } from '../../types';
10
- import { assignPatientToBed, createEncounter } from '../../ward.resource';
7
+ import { useTranslation } from 'react-i18next';
8
+ import { z } from 'zod';
11
9
  import useEmrConfiguration from '../../hooks/useEmrConfiguration';
12
- import { showSnackbar, useSession } from '@openmrs/esm-framework';
13
10
  import useWardLocation from '../../hooks/useWardLocation';
14
- import { useInpatientRequest } from '../../hooks/useInpatientRequest';
15
- import {
16
- Form,
17
- ButtonSet,
18
- Button,
19
- RadioButtonGroup,
20
- RadioButton,
21
- RadioButtonSkeleton,
22
- InlineNotification,
23
- } from '@carbon/react';
11
+ import type { BedLayout, WardPatientWorkspaceProps, WardViewContext } from '../../types';
12
+ import { assignPatientToBed, createEncounter, removePatientFromBed } from '../../ward.resource';
13
+ import BedSelector from '../bed-selector.component';
14
+ import styles from './patient-transfer-swap.scss';
24
15
 
25
16
  export default function PatientBedSwapForm({
26
17
  promptBeforeClosing,
@@ -30,13 +21,12 @@ export default function PatientBedSwapForm({
30
21
  const { patient } = wardPatient;
31
22
  const { t } = useTranslation();
32
23
  const [showErrorNotifications, setShowErrorNotifications] = useState(false);
33
- const { isLoading, admissionLocation } = useAdmissionLocation();
34
24
  const { emrConfiguration, isLoadingEmrConfiguration, errorFetchingEmrConfiguration } = useEmrConfiguration();
35
25
  const [isSubmitting, setIsSubmitting] = useState(false);
36
26
  const { currentProvider } = useSession();
37
27
  const { location } = useWardLocation();
38
- const { mutate: mutateAdmissionLocation } = useAdmissionLocation();
39
- const { mutate: mutateInpatientRequest } = useInpatientRequest();
28
+ const { wardPatientGroupDetails } = useAppContext<WardViewContext>('ward-view-context') ?? {};
29
+ const { isLoading } = wardPatientGroupDetails?.admissionLocationResponse ?? {};
40
30
 
41
31
  const zodSchema = useMemo(
42
32
  () =>
@@ -71,15 +61,7 @@ export default function PatientBedSwapForm({
71
61
  [t],
72
62
  );
73
63
 
74
- const beds = useMemo(() => (admissionLocation ? filterBeds(admissionLocation) : []), [admissionLocation]);
75
- const bedDetails = useMemo(
76
- () =>
77
- beds.map((bed) => {
78
- const isPatientAssignedToBed = bed.patients.find((bedPatient) => bedPatient.uuid === patient.uuid);
79
- return { id: bed.bedId, label: getBedInformation(bed), isPatientAssignedToBed };
80
- }),
81
- [beds, getBedInformation],
82
- );
64
+ const beds = wardPatientGroupDetails?.bedLayouts ?? [];
83
65
 
84
66
  const onSubmit = useCallback(
85
67
  (values: FormValues) => {
@@ -99,47 +81,69 @@ export default function PatientBedSwapForm({
99
81
  })
100
82
  .then(async (response) => {
101
83
  if (response.ok) {
102
- return assignPatientToBed(values.bedId, patient.uuid, response.data.uuid);
84
+ if (bedSelected) {
85
+ return assignPatientToBed(values.bedId, patient.uuid, response.data.uuid);
86
+ } else {
87
+ // get the bed that the patient is currently assigned to
88
+ const bedAssignedToPatient = beds.find((bed) =>
89
+ bed.patients.some((bedPatient) => bedPatient.uuid == patient.uuid),
90
+ );
91
+ if (bedAssignedToPatient) {
92
+ return removePatientFromBed(bedAssignedToPatient.bedId, patient.uuid);
93
+ } else {
94
+ // no-op
95
+ return Promise.resolve({ ok: true });
96
+ }
97
+ }
103
98
  }
104
99
  })
105
100
  .then((response) => {
106
101
  if (response && response?.ok) {
107
- showSnackbar({
108
- kind: 'success',
109
- title: t('patientAssignedNewbed', 'Patient assigned to new bed'),
110
- subtitle: t('patientAssignedToBed', '{{patientName}} assigned to bed {{bedNumber}}', {
111
- patientName: patient.person.preferredName.display,
112
- bedNumber: bedSelected.bedNumber,
113
- }),
114
- });
115
- mutateAdmissionLocation();
116
- mutateInpatientRequest();
117
- closeWorkspaceWithSavedChanges();
102
+ if (bedSelected) {
103
+ showSnackbar({
104
+ kind: 'success',
105
+ title: t('patientAssignedNewBed', 'Patient assigned to new bed'),
106
+ subtitle: t('patientAssignedNewBedDetail', '{{patientName}} assigned to bed {{bedNumber}}', {
107
+ patientName: patient.person.preferredName.display,
108
+ bedNumber: bedSelected.bedNumber,
109
+ }),
110
+ });
111
+ } else {
112
+ showSnackbar({
113
+ kind: 'success',
114
+ title: t('patientUnassignedFromBed', 'Patient unassigned from bed'),
115
+ subtitle: t('patientUnassignedFromBedDetail', '{{patientName}} is now unassigned from bed', {
116
+ patientName: patient.person.preferredName.display,
117
+ }),
118
+ });
119
+ }
118
120
  }
119
121
  })
120
122
  .catch((error: Error) => {
121
123
  showSnackbar({
122
124
  kind: 'error',
123
- title: t('errorAssigningBedToPatient', 'Error assigning bed to patient'),
125
+ title: t('errorChangingPatientBedAssignment', 'Error changing patient bed assignment'),
124
126
  subtitle: error?.message,
125
127
  });
126
- mutateAdmissionLocation();
127
- mutateInpatientRequest();
128
- closeWorkspaceWithSavedChanges();
129
128
  })
130
129
  .finally(() => {
131
130
  setIsSubmitting(false);
131
+ wardPatientGroupDetails.mutate();
132
+ closeWorkspaceWithSavedChanges();
132
133
  });
133
134
  },
134
- [setShowErrorNotifications, patient, emrConfiguration, currentProvider, location, beds],
135
+ [setShowErrorNotifications, patient, emrConfiguration, currentProvider, location, beds, wardPatientGroupDetails],
135
136
  );
136
137
 
137
138
  const onError = useCallback(() => {
138
139
  setShowErrorNotifications(true);
139
140
  }, []);
140
141
 
142
+ if (!wardPatientGroupDetails) return <></>;
141
143
  return (
142
- <Form onSubmit={handleSubmit(onSubmit, onError)} className={styles.formContainer}>
144
+ <Form
145
+ onSubmit={handleSubmit(onSubmit, onError)}
146
+ className={classNames(styles.formContainer, styles.workspaceContent)}>
143
147
  <div>
144
148
  {errorFetchingEmrConfiguration && (
145
149
  <div className={styles.formError}>
@@ -156,36 +160,21 @@ export default function PatientBedSwapForm({
156
160
  </div>
157
161
  )}
158
162
  <h2 className={styles.productiveHeading02}>{t('selectABed', 'Select a bed')}</h2>
159
- {isLoading ? (
160
- <RadioButtonGroup className={styles.radioButtonGroup} name="bedId">
161
- <RadioButtonSkeleton />
162
- <RadioButtonSkeleton />
163
- <RadioButtonSkeleton />
164
- </RadioButtonGroup>
165
- ) : (
166
- <Controller
167
- name="bedId"
168
- control={control}
169
- render={({ field: { onChange, value } }) => (
170
- <RadioButtonGroup
171
- className={styles.radioButtonGroup}
172
- onChange={onChange}
173
- invalid={!!errors?.bedId?.message}
174
- invalidText={errors?.bedId?.message}>
175
- {bedDetails.map(({ id, label, isPatientAssignedToBed }) => (
176
- <RadioButton
177
- key={id}
178
- labelText={label}
179
- control={control}
180
- value={id}
181
- checked={id === value}
182
- disabled={isPatientAssignedToBed}
183
- />
184
- ))}
185
- </RadioButtonGroup>
186
- )}
187
- />
188
- )}
163
+ <Controller
164
+ name="bedId"
165
+ control={control}
166
+ render={({ field: { onChange, value }, fieldState: { error } }) => (
167
+ <BedSelector
168
+ beds={beds}
169
+ isLoadingBeds={isLoading}
170
+ currentPatient={patient}
171
+ selectedBedId={value}
172
+ error={error}
173
+ control={control}
174
+ onChange={onChange}
175
+ />
176
+ )}
177
+ />
189
178
  {showErrorNotifications && (
190
179
  <div className={styles.notifications}>
191
180
  {Object.values(errors).map((error) => (
@@ -1,18 +1,17 @@
1
+ import { Button, ButtonSet, Form, InlineNotification, RadioButton, RadioButtonGroup, TextArea } from '@carbon/react';
2
+ import { zodResolver } from '@hookform/resolvers/zod';
3
+ import { ResponsiveWrapper, showSnackbar, useAppContext, useSession } from '@openmrs/esm-framework';
4
+ import classNames from 'classnames';
1
5
  import React, { useCallback, useEffect, useMemo, useState } from 'react';
2
- import { ResponsiveWrapper, showSnackbar, useSession } from '@openmrs/esm-framework';
3
- import styles from './patient-transfer-swap.scss';
6
+ import { Controller, useForm } from 'react-hook-form';
4
7
  import { useTranslation } from 'react-i18next';
5
- import { useAdmissionLocation } from '../../hooks/useAdmissionLocation';
6
8
  import { z } from 'zod';
7
- import { Controller, useForm } from 'react-hook-form';
8
- import { zodResolver } from '@hookform/resolvers/zod';
9
- import LocationSelector from '../../location-selector/location-selector.component';
10
9
  import useEmrConfiguration from '../../hooks/useEmrConfiguration';
11
- import { createEncounter } from '../../ward.resource';
12
10
  import useWardLocation from '../../hooks/useWardLocation';
13
- import type { ObsPayload, WardPatientWorkspaceProps } from '../../types';
14
- import { useInpatientRequest } from '../../hooks/useInpatientRequest';
15
- import { Form, ButtonSet, Button, TextArea, InlineNotification, RadioButtonGroup, RadioButton } from '@carbon/react';
11
+ import LocationSelector from '../../location-selector/location-selector.component';
12
+ import type { ObsPayload, WardPatientWorkspaceProps, WardViewContext } from '../../types';
13
+ import { createEncounter } from '../../ward.resource';
14
+ import styles from './patient-transfer-swap.scss';
16
15
 
17
16
  export default function PatientTransferForm({
18
17
  closeWorkspaceWithSavedChanges,
@@ -30,8 +29,7 @@ export default function PatientTransferForm({
30
29
  () => emrConfiguration?.dispositions.filter(({ type }) => type === 'TRANSFER'),
31
30
  [emrConfiguration],
32
31
  );
33
- const { mutate: mutateAdmissionLocation } = useAdmissionLocation();
34
- const { mutate: mutateInpatientRequest } = useInpatientRequest();
32
+ const { wardPatientGroupDetails } = useAppContext<WardViewContext>('ward-view-context') ?? {};
35
33
 
36
34
  const zodSchema = useMemo(
37
35
  () =>
@@ -64,7 +62,6 @@ export default function PatientTransferForm({
64
62
  formState: { errors, isDirty },
65
63
  control,
66
64
  handleSubmit,
67
- getValues,
68
65
  setValue,
69
66
  } = useForm<FormValues>({ resolver: zodResolver(zodSchema), defaultValues: formDefaultValues });
70
67
 
@@ -102,8 +99,8 @@ export default function PatientTransferForm({
102
99
  }
103
100
 
104
101
  createEncounter({
105
- patient: patient.uuid,
106
- encounterType: emrConfiguration.visitNoteEncounterType.uuid,
102
+ patient: patient?.uuid,
103
+ encounterType: emrConfiguration.transferRequestEncounterType.uuid,
107
104
  location: location.uuid,
108
105
  encounterProviders: [
109
106
  {
@@ -123,9 +120,6 @@ export default function PatientTransferForm({
123
120
  title: t('patientTransferRequestCreated', 'Patient transfer request created'),
124
121
  kind: 'success',
125
122
  });
126
- closeWorkspaceWithSavedChanges();
127
- mutateAdmissionLocation();
128
- mutateInpatientRequest();
129
123
  })
130
124
  .catch((err: Error) => {
131
125
  showSnackbar({
@@ -134,17 +128,20 @@ export default function PatientTransferForm({
134
128
  kind: 'error',
135
129
  });
136
130
  })
137
- .finally(() => setIsSubmitting(false));
131
+ .finally(() => {
132
+ setIsSubmitting(false);
133
+ closeWorkspaceWithSavedChanges();
134
+ wardPatientGroupDetails.mutate();
135
+ });
138
136
  },
139
137
  [
140
138
  setShowErrorNotifications,
141
139
  currentProvider,
142
140
  location,
143
141
  emrConfiguration,
144
- patient.uuid,
142
+ patient?.uuid,
145
143
  dispositionsWithTypeTransfer,
146
- mutateAdmissionLocation,
147
- mutateInpatientRequest,
144
+ wardPatientGroupDetails,
148
145
  ],
149
146
  );
150
147
 
@@ -153,8 +150,11 @@ export default function PatientTransferForm({
153
150
  setShowErrorNotifications(true);
154
151
  }, []);
155
152
 
153
+ if (!wardPatientGroupDetails) return <></>;
156
154
  return (
157
- <Form onSubmit={handleSubmit(onSubmit, onError)} className={styles.formContainer}>
155
+ <Form
156
+ onSubmit={handleSubmit(onSubmit, onError)}
157
+ className={classNames(styles.formContainer, styles.workspaceContent)}>
158
158
  <div>
159
159
  {errorFetchingEmrConfiguration && (
160
160
  <div className={styles.formError}>
@@ -229,7 +229,7 @@ export default function PatientTransferForm({
229
229
  <Button
230
230
  type="submit"
231
231
  size="xl"
232
- disabled={isLoadingEmrConfiguration || isSubmitting || errorFetchingEmrConfiguration}>
232
+ disabled={isLoadingEmrConfiguration || isSubmitting || errorFetchingEmrConfiguration || !patient}>
233
233
  {t('save', 'Save')}
234
234
  </Button>
235
235
  </ButtonSet>
@@ -2,6 +2,13 @@
2
2
  @use '@carbon/layout';
3
3
  @use '@openmrs/esm-styleguide/src/vars' as *;
4
4
 
5
+ .flexWrapper {
6
+ height: 100%;
7
+ display: flex;
8
+ flex-direction: column;
9
+ color: #393939;
10
+ }
11
+
5
12
  .workspaceContent {
6
13
  padding: layout.$spacing-05;
7
14
  height: 100%;
@@ -10,8 +17,12 @@
10
17
  color: #393939;
11
18
  }
12
19
 
20
+ .contentSwitcherWrapper {
21
+ padding: 0 layout.$spacing-05;
22
+ }
23
+
13
24
  .patientWorkspaceBanner {
14
- margin: (-(layout.$spacing-05)) (-(layout.$spacing-05)) layout.$spacing-05 (-(layout.$spacing-05));
25
+ margin-bottom: layout.$spacing-05;
15
26
  }
16
27
 
17
28
  .field {
@@ -26,7 +37,6 @@
26
37
  }
27
38
 
28
39
  .workspaceForm {
29
- margin-top: layout.$spacing-05;
30
40
  flex: 1;
31
41
  }
32
42
 
@@ -1,12 +1,12 @@
1
- import React, { useState } from 'react';
2
- import { useFeatureFlag } from '@openmrs/esm-framework';
3
1
  import { ContentSwitcher, Switch } from '@carbon/react';
2
+ import { useFeatureFlag } from '@openmrs/esm-framework';
3
+ import React, { useState } from 'react';
4
4
  import { useTranslation } from 'react-i18next';
5
- import PatientTransferForm from './patient-transfer-request-form.component';
5
+ import type { WardPatientWorkspaceProps } from '../../types';
6
+ import WardPatientWorkspaceBanner from '../patient-banner/patient-banner.component';
6
7
  import PatientBedSwapForm from './patient-bed-swap-form.component';
8
+ import PatientTransferForm from './patient-transfer-request-form.component';
7
9
  import styles from './patient-transfer-swap.scss';
8
- import WardPatientWorkspaceBanner from '../patient-banner/patient-banner.component';
9
- import type { WardPatientWorkspaceProps } from '../../types';
10
10
 
11
11
  const TransferSection = {
12
12
  TRANSFER: 'transfer',
@@ -21,12 +21,12 @@ export default function PatientTransferAndSwapWorkspace(props: WardPatientWorksp
21
21
  const isBedManagementModuleInstalled = useFeatureFlag('bedmanagement-module');
22
22
 
23
23
  return (
24
- <div className={styles.workspaceContent}>
24
+ <div className={styles.flexWrapper}>
25
25
  <div className={styles.patientWorkspaceBanner}>
26
26
  <WardPatientWorkspaceBanner {...props?.wardPatient} />
27
27
  </div>
28
28
  {isBedManagementModuleInstalled && (
29
- <div>
29
+ <div className={styles.contentSwitcherWrapper}>
30
30
  <h2 className={styles.productiveHeading02}>{t('typeOfTransfer', 'Type of transfer')}</h2>
31
31
  <div className={styles.contentSwitcher}>
32
32
  <ContentSwitcher onChange={({ name }) => setSelectedSection(name)}>
@@ -37,7 +37,11 @@ export default function PatientTransferAndSwapWorkspace(props: WardPatientWorksp
37
37
  </div>
38
38
  )}
39
39
  <div className={styles.workspaceForm}>
40
- {selectedSection === 'transfer' ? <PatientTransferForm {...props} /> : <PatientBedSwapForm {...props} />}
40
+ {selectedSection === TransferSection.TRANSFER ? (
41
+ <PatientTransferForm {...props} />
42
+ ) : (
43
+ <PatientBedSwapForm {...props} />
44
+ )}
41
45
  </div>
42
46
  </div>
43
47
  );
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ import PatientTransferForm from '../patient-transfer-bed-swap/patient-transfer-request-form.component';
3
+ import { type WardPatientWorkspaceProps } from '../../types';
4
+
5
+ interface PatientTransferRequestFormProps extends WardPatientWorkspaceProps {}
6
+
7
+ const PatientTransferRequestForm: React.FC<PatientTransferRequestFormProps> = (props) => {
8
+ return <PatientTransferForm {...props} />;
9
+ };
10
+
11
+ export default PatientTransferRequestForm;
@@ -69,7 +69,7 @@ const PatientNotesForm: React.FC<PatientNotesFormProps> = ({
69
69
  const notePayload = {
70
70
  patient: patientUuid,
71
71
  location: locationUuid,
72
- encounterType: emrConfiguration?.visitNoteEncounterType.uuid,
72
+ encounterType: emrConfiguration?.inpatientNoteEncounterType.uuid,
73
73
  encounterProviders: [
74
74
  {
75
75
  encounterRole: emrConfiguration?.clinicianEncounterRole.uuid,
@@ -54,7 +54,7 @@ test('renders a success snackbar upon successfully recording a visit note', asyn
54
54
  provider: undefined,
55
55
  },
56
56
  ]),
57
- encounterType: emrConfigurationMock.visitNoteEncounterType.uuid,
57
+ encounterType: emrConfigurationMock.inpatientNoteEncounterType.uuid,
58
58
  location: undefined,
59
59
  obs: expect.arrayContaining([
60
60
  {
@@ -14,12 +14,12 @@ interface PatientNotesHistoryProps {
14
14
 
15
15
  const PatientNotesHistory: React.FC<PatientNotesHistoryProps> = ({ patientUuid, visitUuid }) => {
16
16
  const { t } = useTranslation();
17
- const { emrConfiguration, isLoadingEmrConfiguration, errorFetchingEmrConfiguration } = useEmrConfiguration();
17
+ const { emrConfiguration, isLoadingEmrConfiguration } = useEmrConfiguration();
18
18
 
19
19
  const { patientNotes, isLoadingPatientNotes, errorFetchingPatientNotes } = usePatientNotes(
20
20
  patientUuid,
21
21
  visitUuid,
22
- emrConfiguration?.visitNoteEncounterType?.uuid,
22
+ emrConfiguration?.inpatientNoteEncounterType?.uuid,
23
23
  emrConfiguration?.consultFreeTextCommentsConcept.uuid,
24
24
  );
25
25
 
@@ -1,8 +1,7 @@
1
- import { type FetchResponse, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
2
- import useSWR from 'swr';
3
- import { type PatientNote, type UsePatientNotes, type VisitEncountersFetchResponse } from './types';
4
- import { type EncounterPayload } from '../../types';
1
+ import { openmrsFetch, restBaseUrl, useOpenmrsFetchAll } from '@openmrs/esm-framework';
5
2
  import { useMemo } from 'react';
3
+ import { type EncounterPayload } from '../../types';
4
+ import { type PatientNote, type RESTPatientNote, type UsePatientNotes } from './types';
6
5
 
7
6
  export function savePatientNote(payload: EncounterPayload, abortController: AbortController = new AbortController()) {
8
7
  return openmrsFetch(`${restBaseUrl}/encounter`, {
@@ -29,15 +28,14 @@ export function usePatientNotes(
29
28
  'diagnoses';
30
29
  const encountersApiUrl = `${restBaseUrl}/encounter?patient=${patientUuid}&encounterType=${encounterType}&visit=${visitUuid}&v=${customRepresentation}`;
31
30
 
32
- const { data, error, isLoading, isValidating, mutate } = useSWR<FetchResponse<VisitEncountersFetchResponse>, Error>(
31
+ const { data, error, isLoading, isValidating, mutate } = useOpenmrsFetchAll<RESTPatientNote>(
33
32
  patientUuid && encounterType ? encountersApiUrl : null,
34
- openmrsFetch,
35
33
  );
36
34
 
37
35
  const patientNotes: Array<PatientNote> | null = useMemo(
38
36
  () =>
39
37
  data
40
- ? data.data.results
38
+ ? data
41
39
  .map((encounter) => {
42
40
  const noteObs = encounter.obs.find((obs) => obs.concept.uuid === conceptUuid);
43
41
 
@@ -17,7 +17,7 @@ const WardPatientNotesWorkspace: React.FC<WardPatientWorkspaceProps> = (props) =
17
17
  <div>
18
18
  <WardPatientWorkspaceBanner {...wardPatient} />
19
19
  <PatientNotesForm {...notesFormState} />
20
- <PatientNotesHistory patientUuid={patientUuid} visitUuid={wardPatient?.visit.uuid} />
20
+ <PatientNotesHistory patientUuid={patientUuid} visitUuid={wardPatient?.visit?.uuid} />
21
21
  </div>
22
22
  );
23
23
  };
@@ -1,9 +1,5 @@
1
1
  import { type Concept, type OpenmrsResource } from '@openmrs/esm-framework';
2
2
 
3
- export interface VisitEncountersFetchResponse {
4
- results: Array<RESTPatientNote>;
5
- }
6
-
7
3
  export interface RESTPatientNote extends OpenmrsResource {
8
4
  uuid: string;
9
5
  display: string;