@kenyaemr/esm-ward-app 8.1.1-pre.121 → 8.1.1-pre.124

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 (54) hide show
  1. package/.turbo/turbo-build.log +8 -8
  2. package/dist/124.js +1 -1
  3. package/dist/124.js.map +1 -1
  4. package/dist/125.js +1 -1
  5. package/dist/125.js.map +1 -1
  6. package/dist/130.js +1 -1
  7. package/dist/130.js.map +1 -1
  8. package/dist/153.js +1 -1
  9. package/dist/153.js.map +1 -1
  10. package/dist/303.js +2 -0
  11. package/dist/303.js.map +1 -0
  12. package/dist/471.js +1 -1
  13. package/dist/471.js.map +1 -1
  14. package/dist/53.js +1 -1
  15. package/dist/53.js.map +1 -1
  16. package/dist/574.js +1 -1
  17. package/dist/577.js +1 -1
  18. package/dist/577.js.map +1 -1
  19. package/dist/662.js +1 -1
  20. package/dist/662.js.map +1 -1
  21. package/dist/921.js +1 -1
  22. package/dist/921.js.map +1 -1
  23. package/dist/922.js +1 -1
  24. package/dist/922.js.map +1 -1
  25. package/dist/kenyaemr-esm-ward-app.js +1 -1
  26. package/dist/kenyaemr-esm-ward-app.js.buildmanifest.json +60 -60
  27. package/dist/kenyaemr-esm-ward-app.js.map +1 -1
  28. package/dist/main.js +1 -1
  29. package/dist/main.js.map +1 -1
  30. package/dist/routes.json +1 -1
  31. package/package.json +1 -1
  32. package/src/beds/ward-bed.test.tsx +16 -5
  33. package/src/hooks/useWardPatientGrouping.ts +5 -0
  34. package/src/ward-view/materal-ward/maternal-ward-patient-card.test.tsx +11 -0
  35. package/src/ward-view/ward-view.scss +1 -1
  36. package/src/ward-view/ward-view.test.tsx +8 -0
  37. package/src/ward-view-header/ward-metric.scss +5 -5
  38. package/src/ward-view-header/ward-metrics.scss +1 -2
  39. package/src/ward-workspace/admission-request-card/admission-request-card-actions.component.tsx +59 -6
  40. package/src/ward-workspace/admission-request-workspace/admission-requests-workspace.test.tsx +45 -1
  41. package/src/ward-workspace/admission-request-workspace/admission-requests.workspace.tsx +26 -5
  42. package/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.test.tsx +30 -94
  43. package/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.workspace.tsx +101 -189
  44. package/src/ward-workspace/bed-selector.component.tsx +119 -0
  45. package/src/ward-workspace/bed-selector.scss +15 -0
  46. package/src/ward-workspace/patient-discharge/patient-discharge.workspace.tsx +4 -11
  47. package/src/ward-workspace/patient-transfer-bed-swap/patient-bed-swap-form.component.tsx +56 -77
  48. package/src/ward-workspace/patient-transfer-bed-swap/patient-transfer-request-form.component.tsx +3 -10
  49. package/src/ward-workspace/patient-transfer-bed-swap/patient-transfer-swap.workspace.tsx +10 -6
  50. package/src/ward.resource.ts +32 -2
  51. package/translations/en.json +6 -5
  52. package/dist/67.js +0 -2
  53. package/dist/67.js.map +0 -1
  54. /package/dist/{67.js.LICENSE.txt → 303.js.LICENSE.txt} +0 -0
@@ -1,12 +1,4 @@
1
- import {
2
- Button,
3
- ButtonSet,
4
- Form,
5
- InlineNotification,
6
- RadioButton,
7
- RadioButtonGroup,
8
- RadioButtonSkeleton,
9
- } from '@carbon/react';
1
+ import { Button, ButtonSet, Form, InlineNotification } from '@carbon/react';
10
2
  import { zodResolver } from '@hookform/resolvers/zod';
11
3
  import { showSnackbar, useAppContext, useSession } from '@openmrs/esm-framework';
12
4
  import classNames from 'classnames';
@@ -16,8 +8,9 @@ import { useTranslation } from 'react-i18next';
16
8
  import { z } from 'zod';
17
9
  import useEmrConfiguration from '../../hooks/useEmrConfiguration';
18
10
  import useWardLocation from '../../hooks/useWardLocation';
19
- import type { BedLayout, WardViewContext, WardPatientWorkspaceProps } from '../../types';
20
- import { assignPatientToBed, createEncounter } from '../../ward.resource';
11
+ import type { BedLayout, WardPatientWorkspaceProps, WardViewContext } from '../../types';
12
+ import { assignPatientToBed, createEncounter, removePatientFromBed } from '../../ward.resource';
13
+ import BedSelector from '../bed-selector.component';
21
14
  import styles from './patient-transfer-swap.scss';
22
15
 
23
16
  export default function PatientBedSwapForm({
@@ -32,10 +25,8 @@ export default function PatientBedSwapForm({
32
25
  const [isSubmitting, setIsSubmitting] = useState(false);
33
26
  const { currentProvider } = useSession();
34
27
  const { location } = useWardLocation();
35
- const {wardPatientGroupDetails} = useAppContext<WardViewContext>('ward-view-context') ?? {};
36
- const { isLoading, mutate: mutateAdmissionLocation } = wardPatientGroupDetails?.admissionLocationResponse ?? {};
37
- const { mutate: mutateInpatientRequest } = wardPatientGroupDetails?.inpatientRequestResponse ?? {};
38
- const { mutate: mutateInpatientAdmission } = wardPatientGroupDetails?.inpatientAdmissionResponse ?? {};
28
+ const { wardPatientGroupDetails } = useAppContext<WardViewContext>('ward-view-context') ?? {};
29
+ const { isLoading } = wardPatientGroupDetails?.admissionLocationResponse ?? {};
39
30
 
40
31
  const zodSchema = useMemo(
41
32
  () =>
@@ -71,14 +62,6 @@ export default function PatientBedSwapForm({
71
62
  );
72
63
 
73
64
  const beds = wardPatientGroupDetails?.bedLayouts ?? [];
74
- const bedDetails = useMemo(
75
- () =>
76
- beds.map((bed) => {
77
- const isPatientAssignedToBed = bed.patients.find((bedPatient) => bedPatient.uuid === patient.uuid);
78
- return { id: bed.bedId, label: getBedInformation(bed), isPatientAssignedToBed };
79
- }),
80
- [beds, getBedInformation],
81
- );
82
65
 
83
66
  const onSubmit = useCallback(
84
67
  (values: FormValues) => {
@@ -98,47 +81,58 @@ export default function PatientBedSwapForm({
98
81
  })
99
82
  .then(async (response) => {
100
83
  if (response.ok) {
101
- 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
+ }
102
98
  }
103
99
  })
104
100
  .then((response) => {
105
101
  if (response && response?.ok) {
106
- showSnackbar({
107
- kind: 'success',
108
- title: t('patientAssignedNewbed', 'Patient assigned to new bed'),
109
- subtitle: t('patientAssignedToBed', '{{patientName}} assigned to bed {{bedNumber}}', {
110
- patientName: patient.person.preferredName.display,
111
- bedNumber: bedSelected.bedNumber,
112
- }),
113
- });
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
+ }
114
120
  }
115
121
  })
116
122
  .catch((error: Error) => {
117
123
  showSnackbar({
118
124
  kind: 'error',
119
- title: t('errorAssigningBedToPatient', 'Error assigning bed to patient'),
125
+ title: t('errorChangingPatientBedAssignment', 'Error changing patient bed assignment'),
120
126
  subtitle: error?.message,
121
127
  });
122
128
  })
123
129
  .finally(() => {
124
130
  setIsSubmitting(false);
125
- mutateAdmissionLocation();
126
- mutateInpatientRequest();
127
- mutateInpatientAdmission();
131
+ wardPatientGroupDetails.mutate();
128
132
  closeWorkspaceWithSavedChanges();
129
133
  });
130
134
  },
131
- [
132
- setShowErrorNotifications,
133
- patient,
134
- emrConfiguration,
135
- currentProvider,
136
- location,
137
- beds,
138
- mutateAdmissionLocation,
139
- mutateInpatientRequest,
140
- mutateInpatientAdmission,
141
- ],
135
+ [setShowErrorNotifications, patient, emrConfiguration, currentProvider, location, beds, wardPatientGroupDetails],
142
136
  );
143
137
 
144
138
  const onError = useCallback(() => {
@@ -166,36 +160,21 @@ export default function PatientBedSwapForm({
166
160
  </div>
167
161
  )}
168
162
  <h2 className={styles.productiveHeading02}>{t('selectABed', 'Select a bed')}</h2>
169
- {isLoading ? (
170
- <RadioButtonGroup className={styles.radioButtonGroup} name="bedId">
171
- <RadioButtonSkeleton />
172
- <RadioButtonSkeleton />
173
- <RadioButtonSkeleton />
174
- </RadioButtonGroup>
175
- ) : (
176
- <Controller
177
- name="bedId"
178
- control={control}
179
- render={({ field: { onChange, value } }) => (
180
- <RadioButtonGroup
181
- className={styles.radioButtonGroup}
182
- onChange={onChange}
183
- invalid={!!errors?.bedId?.message}
184
- invalidText={errors?.bedId?.message}>
185
- {bedDetails.map(({ id, label, isPatientAssignedToBed }) => (
186
- <RadioButton
187
- key={id}
188
- labelText={label}
189
- control={control}
190
- value={id}
191
- checked={id === value}
192
- disabled={isPatientAssignedToBed}
193
- />
194
- ))}
195
- </RadioButtonGroup>
196
- )}
197
- />
198
- )}
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
+ />
199
178
  {showErrorNotifications && (
200
179
  <div className={styles.notifications}>
201
180
  {Object.values(errors).map((error) => (
@@ -9,7 +9,7 @@ import { z } from 'zod';
9
9
  import useEmrConfiguration from '../../hooks/useEmrConfiguration';
10
10
  import useWardLocation from '../../hooks/useWardLocation';
11
11
  import LocationSelector from '../../location-selector/location-selector.component';
12
- import type { ObsPayload, WardViewContext, WardPatientWorkspaceProps } from '../../types';
12
+ import type { ObsPayload, WardPatientWorkspaceProps, WardViewContext } from '../../types';
13
13
  import { createEncounter } from '../../ward.resource';
14
14
  import styles from './patient-transfer-swap.scss';
15
15
 
@@ -30,9 +30,6 @@ export default function PatientTransferForm({
30
30
  [emrConfiguration],
31
31
  );
32
32
  const { wardPatientGroupDetails } = useAppContext<WardViewContext>('ward-view-context') ?? {};
33
- const { mutate: mutateAdmissionLocation } = wardPatientGroupDetails?.admissionLocationResponse ?? {};
34
- const { mutate: mutateInpatientAdmission } = wardPatientGroupDetails?.inpatientAdmissionResponse ?? {};
35
- const { mutate: mutateInpatientRequest } = wardPatientGroupDetails?.inpatientRequestResponse ?? {};
36
33
 
37
34
  const zodSchema = useMemo(
38
35
  () =>
@@ -134,9 +131,7 @@ export default function PatientTransferForm({
134
131
  .finally(() => {
135
132
  setIsSubmitting(false);
136
133
  closeWorkspaceWithSavedChanges();
137
- mutateAdmissionLocation();
138
- mutateInpatientAdmission();
139
- mutateInpatientRequest();
134
+ wardPatientGroupDetails.mutate();
140
135
  });
141
136
  },
142
137
  [
@@ -146,9 +141,7 @@ export default function PatientTransferForm({
146
141
  emrConfiguration,
147
142
  patient?.uuid,
148
143
  dispositionsWithTypeTransfer,
149
- mutateAdmissionLocation,
150
- mutateInpatientAdmission,
151
- mutateInpatientRequest,
144
+ wardPatientGroupDetails,
152
145
  ],
153
146
  );
154
147
 
@@ -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',
@@ -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
  );
@@ -1,5 +1,7 @@
1
- import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
2
- import type { Encounter, EncounterPayload } from './types';
1
+ import { openmrsFetch, type Patient, restBaseUrl, useSession } from '@openmrs/esm-framework';
2
+ import type { DispositionType, Encounter, EncounterPayload } from './types';
3
+ import useEmrConfiguration from './hooks/useEmrConfiguration';
4
+ import useWardLocation from './hooks/useWardLocation';
3
5
 
4
6
  export function createEncounter(encounterPayload: EncounterPayload) {
5
7
  return openmrsFetch<Encounter>(`${restBaseUrl}/encounter`, {
@@ -11,6 +13,34 @@ export function createEncounter(encounterPayload: EncounterPayload) {
11
13
  });
12
14
  }
13
15
 
16
+ export function useAdmitPatient() {
17
+ const { location } = useWardLocation();
18
+ const { currentProvider } = useSession();
19
+ const { emrConfiguration, isLoadingEmrConfiguration, errorFetchingEmrConfiguration } = useEmrConfiguration();
20
+
21
+ const admitPatient = (patient: Patient, dispositionType: DispositionType) => {
22
+ return createEncounter({
23
+ patient: patient.uuid,
24
+ encounterType:
25
+ dispositionType === 'ADMIT'
26
+ ? emrConfiguration.admissionEncounterType.uuid
27
+ : dispositionType === 'TRANSFER'
28
+ ? emrConfiguration.transferWithinHospitalEncounterType.uuid
29
+ : null,
30
+ location: location?.uuid,
31
+ encounterProviders: [
32
+ {
33
+ provider: currentProvider?.uuid,
34
+ encounterRole: emrConfiguration.clinicianEncounterRole.uuid,
35
+ },
36
+ ],
37
+ obs: [],
38
+ });
39
+ };
40
+
41
+ return { admitPatient, isLoadingEmrConfiguration, errorFetchingEmrConfiguration };
42
+ }
43
+
14
44
  export function assignPatientToBed(bedUuid: number, patientUuid: string, encounterUuid: string) {
15
45
  return openmrsFetch(`${restBaseUrl}/beds/${bedUuid}`, {
16
46
  method: 'POST',
@@ -5,7 +5,6 @@
5
5
  "admit": "Admit",
6
6
  "admitPatient": "Admit patient",
7
7
  "admitting": "Admitting...",
8
- "bedManagementModuleNotInstalled": "Bed management module is not present to allow bed selection",
9
8
  "bedShare": "Bed share",
10
9
  "bedSwap": "Bed swap",
11
10
  "cancel": "Cancel",
@@ -21,7 +20,7 @@
21
20
  "emptyBed": "Empty bed",
22
21
  "emptyText": "Empty",
23
22
  "encounterDisplay": "{{encounterType}} {{encounterDate}}",
24
- "errorAssigningBedToPatient": "Error assigning bed to patient",
23
+ "errorChangingPatientBedAssignment": "Error changing patient bed assignment",
25
24
  "errorConfiguringPatientCard": "Error configuring patient card",
26
25
  "errorConfiguringPatientCardMessage": "Unable to find configuration for {{elementType}}, id: {{id}}",
27
26
  "errorCreatingEncounter": "Failed to admit patient",
@@ -45,6 +44,7 @@
45
44
  "manage": "Manage",
46
45
  "motherChildBedShare": "Mother / Child",
47
46
  "nextPage": "Next page",
47
+ "noBed": "No bed",
48
48
  "noBedsConfigured": "No beds configured for this location",
49
49
  "noBedsConfiguredForLocation": "No beds configured for {{location}} location",
50
50
  "noLocationsFound": "No locations found",
@@ -56,14 +56,16 @@
56
56
  "patientAdmittedSuccessfully": "Patient admitted successfully",
57
57
  "patientAdmittedSuccessfullySubtitle": "{{patientName}} has been successfully admitted and assigned to bed {{bedNumber}}",
58
58
  "patientAdmittedWoBed": "Patient admitted successfully to {{location}}",
59
- "patientAssignedNewbed": "Patient assigned to new bed",
60
- "patientAssignedToBed": "{{patientName}} assigned to bed {{bedNumber}}",
59
+ "patientAssignedNewBed": "Patient assigned to new bed",
60
+ "patientAssignedNewBedDetail": "{{patientName}} assigned to bed {{bedNumber}}",
61
61
  "patientNoteNowVisible": "It should be now visible in the notes history",
62
62
  "patientNoteSaveError": "Error saving patient note",
63
63
  "patientNotesDidntLoad": "Patient notes didn't load",
64
64
  "patients": "Patients",
65
65
  "patientsMetricValue": "{{ metricValue }}",
66
66
  "patientTransferRequestCreated": "Patient transfer request created",
67
+ "patientUnassignedFromBed": "Patient unassigned from bed",
68
+ "patientUnassignedFromBedDetail": "{{patientName}} is now unassigned from bed",
67
69
  "patientWasDischarged": "Patient was discharged",
68
70
  "pendingOut": "Pending out",
69
71
  "pendingOutMetricValue": "{{ metricValue }}",
@@ -87,7 +89,6 @@
87
89
  "transfers": "Transfers",
88
90
  "transferType": "Transfer type",
89
91
  "typeOfTransfer": "Type of transfer",
90
- "unableToSelectBeds": "Unable to select beds",
91
92
  "unknown": "Unknown",
92
93
  "visitNoteSaved": "Patient note saved",
93
94
  "wardClinicalNotePlaceholder": "Write any notes here",