@kenyaemr/esm-ward-app 7.0.3-pre.89 → 8.0.0
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.
- package/.turbo/turbo-build.log +24 -16
- package/dist/130.js +1 -1
- package/dist/130.js.map +1 -1
- package/dist/169.js +1 -0
- package/dist/169.js.map +1 -0
- package/dist/269.js +1 -0
- package/dist/269.js.map +1 -0
- package/dist/346.js +1 -0
- package/dist/346.js.map +1 -0
- package/dist/348.js +1 -0
- package/dist/348.js.map +1 -0
- package/dist/466.js +1 -0
- package/dist/466.js.map +1 -0
- package/dist/501.js +1 -0
- package/dist/501.js.map +1 -0
- package/dist/574.js +1 -1
- package/dist/577.js +1 -0
- package/dist/577.js.map +1 -0
- package/dist/659.js +1 -0
- package/dist/659.js.map +1 -0
- package/dist/749.js +1 -0
- package/dist/749.js.map +1 -0
- package/dist/76.js +1 -0
- package/dist/76.js.map +1 -0
- package/dist/767.js +1 -0
- package/dist/767.js.map +1 -0
- package/dist/793.js +2 -0
- package/dist/793.js.map +1 -0
- package/dist/803.js +1 -0
- package/dist/803.js.map +1 -0
- package/dist/940.js +1 -0
- package/dist/940.js.map +1 -0
- package/dist/960.js +1 -0
- package/dist/960.js.map +1 -0
- package/dist/kenyaemr-esm-ward-app.js +1 -1
- package/dist/kenyaemr-esm-ward-app.js.buildmanifest.json +330 -42
- package/dist/kenyaemr-esm-ward-app.js.map +1 -1
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +3 -4
- package/src/action-menu-buttons/transfer-workspace-siderail.component.tsx +27 -0
- package/src/beds/empty-bed.component.tsx +1 -1
- package/src/beds/empty-bed.scss +6 -6
- package/src/beds/occupied-bed.component.tsx +5 -5
- package/src/beds/occupied-bed.scss +2 -3
- package/src/beds/occupied-bed.test.tsx +37 -21
- package/src/beds/unassigned-patient.component.tsx +20 -0
- package/src/beds/unassigned-patient.scss +6 -0
- package/src/config-schema-admission-request-note.ts +9 -0
- package/src/config-schema-extension-colored-obs-tags.ts +91 -0
- package/src/config-schema.ts +165 -231
- package/src/createDashboardLink.component.tsx +42 -0
- package/src/hooks/useAdmissionLocation.ts +12 -7
- package/src/hooks/useCurrentWardCardConfig.ts +32 -0
- package/src/hooks/useEmrConfiguration.ts +112 -0
- package/src/hooks/useInpatientAdmission.ts +28 -0
- package/src/hooks/useInpatientRequest.ts +39 -9
- package/src/hooks/useLocation.test.ts +38 -0
- package/src/hooks/useLocation.ts +9 -0
- package/src/hooks/useLocations.ts +54 -0
- package/src/hooks/useMostRecentObs.ts +27 -0
- package/src/hooks/useRestPatient.ts +18 -0
- package/src/hooks/useWardLocation.test.ts +108 -0
- package/src/hooks/useWardLocation.ts +26 -0
- package/src/index.ts +71 -4
- package/src/location-selector/location-selector.component.tsx +118 -0
- package/src/location-selector/location-selector.scss +48 -0
- package/src/root.component.tsx +2 -1
- package/src/routes.json +79 -12
- package/src/types/index.ts +87 -46
- package/src/ward-patient-card/card-rows/admission-request-note.extension.tsx +27 -0
- package/src/ward-patient-card/card-rows/colored-obs-tags-card-row.extension.tsx +13 -0
- package/src/ward-patient-card/row-elements/ward-patient-age.tsx +7 -13
- package/src/ward-patient-card/row-elements/ward-patient-bed-number.tsx +2 -2
- package/src/ward-patient-card/row-elements/ward-patient-coded-obs-tags.tsx +51 -50
- package/src/ward-patient-card/row-elements/ward-patient-gender.component.tsx +27 -0
- package/src/ward-patient-card/row-elements/ward-patient-header-address.tsx +16 -15
- package/src/ward-patient-card/row-elements/ward-patient-identifier.tsx +53 -0
- package/src/ward-patient-card/row-elements/ward-patient-name.tsx +7 -7
- package/src/ward-patient-card/row-elements/ward-patient-obs.resource.ts +4 -4
- package/src/ward-patient-card/row-elements/ward-patient-obs.tsx +45 -44
- package/src/ward-patient-card/row-elements/ward-patient-time-on-ward.tsx +22 -0
- package/src/ward-patient-card/row-elements/ward-patient-time-since-admission.tsx +22 -0
- package/src/ward-patient-card/ward-patient-card-element.component.tsx +65 -0
- package/src/ward-patient-card/ward-patient-card.component.tsx +64 -0
- package/src/ward-patient-card/ward-patient-card.scss +61 -12
- package/src/ward-patient-workspace/ward-patient-action-button.extension.tsx +18 -0
- package/src/ward-patient-workspace/ward-patient.style.scss +11 -0
- package/src/ward-patient-workspace/ward-patient.workspace.tsx +51 -0
- package/src/ward-view/ward-bed.component.tsx +0 -1
- package/src/ward-view/ward-view.component.tsx +114 -76
- package/src/ward-view/ward-view.resource.ts +2 -2
- package/src/ward-view/ward-view.scss +4 -4
- package/src/ward-view/ward-view.test.tsx +76 -49
- package/src/ward-view-header/admission-requests-bar.component.tsx +29 -28
- package/src/ward-view-header/admission-requests-bar.test.tsx +11 -15
- package/src/ward-view-header/admission-requests.scss +20 -25
- package/src/ward-view-header/ward-view-header.component.tsx +7 -7
- package/src/ward-view-header/ward-view-header.scss +2 -2
- package/src/ward-workspace/admission-request-card/admission-request-card-actions.component.tsx +29 -0
- package/src/ward-workspace/admission-request-card/admission-request-card-header.component.tsx +51 -0
- package/src/ward-workspace/admission-request-card/admission-request-card.component.tsx +16 -0
- package/src/ward-workspace/admission-request-card/admission-request-card.scss +49 -0
- package/src/ward-workspace/admission-request-workspace/admission-requests-workspace.scss +12 -0
- package/src/ward-workspace/admission-request-workspace/admission-requests-workspace.test.tsx +48 -0
- package/src/ward-workspace/admission-request-workspace/admission-requests.workspace.tsx +61 -0
- package/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.scss +35 -0
- package/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.test.tsx +341 -0
- package/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.workspace.tsx +267 -0
- package/src/ward-workspace/admit-patient-form-workspace/types.ts +7 -0
- package/src/ward-workspace/patient-banner/patient-banner.component.tsx +29 -0
- package/src/ward-workspace/patient-banner/style.scss +23 -0
- package/src/ward-workspace/patient-transfer-bed-swap/patient-bed-swap-form.component.tsx +210 -0
- package/src/ward-workspace/patient-transfer-bed-swap/patient-transfer-request-form.component.tsx +238 -0
- package/src/ward-workspace/patient-transfer-bed-swap/patient-transfer-swap.scss +73 -0
- package/src/ward-workspace/patient-transfer-bed-swap/patient-transfer-swap.workspace.tsx +44 -0
- package/src/ward-workspace/ward-patient-notes/form/notes-form.component.tsx +180 -0
- package/src/ward-workspace/ward-patient-notes/form/notes-form.scss +30 -0
- package/src/ward-workspace/ward-patient-notes/form/notes-form.test.tsx +116 -0
- package/src/ward-workspace/ward-patient-notes/history/note.component.tsx +53 -0
- package/src/ward-workspace/ward-patient-notes/history/notes-container.component.tsx +55 -0
- package/src/ward-workspace/ward-patient-notes/history/notes-container.test.tsx +84 -0
- package/src/ward-workspace/ward-patient-notes/history/styles.scss +61 -0
- package/src/ward-workspace/ward-patient-notes/notes-action-button.extension.tsx +18 -0
- package/src/ward-workspace/ward-patient-notes/notes.resource.ts +71 -0
- package/src/ward-workspace/ward-patient-notes/notes.workspace.tsx +25 -0
- package/src/ward-workspace/ward-patient-notes/types.ts +44 -0
- package/src/ward.resource.ts +25 -0
- package/translations/en.json +63 -2
- package/dist/443.js +0 -1
- package/dist/443.js.map +0 -1
- package/dist/589.js +0 -1
- package/dist/589.js.map +0 -1
- package/dist/695.js +0 -2
- package/dist/695.js.map +0 -1
- package/src/hooks/useAdmittedPatients.ts +0 -13
- package/src/ward-patient-card/row-elements/row-elements.scss +0 -16
- package/src/ward-patient-card/ward-patient-card-row.resources.tsx +0 -92
- package/src/ward-patient-card/ward-patient-card.tsx +0 -20
- package/src/ward-workspace/admission-request-card.component.tsx +0 -23
- package/src/ward-workspace/admission-request-card.scss +0 -34
- package/src/ward-workspace/admission-request-workspace.test.tsx +0 -38
- package/src/ward-workspace/admission-requests-workspace.component.tsx +0 -21
- package/src/ward-workspace/admission-requests-workspace.scss +0 -13
- /package/dist/{695.js.LICENSE.txt → 793.js.LICENSE.txt} +0 -0
package/src/routes.json
CHANGED
|
@@ -4,28 +4,95 @@
|
|
|
4
4
|
"webservices.rest": "^2.2.0",
|
|
5
5
|
"emrapi": "^2.0.0 || 2.0.0-SNAPSHOT"
|
|
6
6
|
},
|
|
7
|
-
"optionalBackendDependencies":{
|
|
8
|
-
|
|
7
|
+
"optionalBackendDependencies": {
|
|
8
|
+
"bedmanagement": {
|
|
9
9
|
"version": "^6.0.0 || 6.0.0-SNAPSHOT",
|
|
10
10
|
"feature": {
|
|
11
11
|
"flagName": "bedmanagement-module",
|
|
12
|
-
"label":"Ward App Patient Service",
|
|
12
|
+
"label": "Ward App Patient Service",
|
|
13
13
|
"description": "This module, if installed, provides services for managing patients admitted to the ward."
|
|
14
14
|
}
|
|
15
|
-
|
|
15
|
+
}
|
|
16
16
|
},
|
|
17
|
-
|
|
17
|
+
"extensions": [
|
|
18
18
|
{
|
|
19
|
-
"name":"
|
|
20
|
-
"component": "
|
|
21
|
-
"
|
|
22
|
-
|
|
19
|
+
"name": "ward-dashboard-link",
|
|
20
|
+
"component": "wardDashboardLink",
|
|
21
|
+
"slot": "homepage-dashboard-slot",
|
|
22
|
+
"meta": {
|
|
23
|
+
"name": "ward",
|
|
24
|
+
"slot": "ward-dashboard-slot",
|
|
25
|
+
"title": "Wards"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
"component": "root",
|
|
30
|
+
"name": "ward-dashboard",
|
|
31
|
+
"slot": "ward-dashboard-slot"
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
"component": "wardPatientActionButtonExtension",
|
|
35
|
+
"name": "ward-patient-action-button",
|
|
36
|
+
"slot": "action-menu-ward-patient-items-slot"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"component": "wardPatientNotesActionButtonExtension",
|
|
40
|
+
"name": "ward-inpatient-notes-form-action-button",
|
|
41
|
+
"slot": "action-menu-ward-patient-items-slot"
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"component": "coloredObsTagCardRowExtension",
|
|
45
|
+
"name": "colored-obs-tags-card-row",
|
|
46
|
+
"slot": "ward-patient-card-slot"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"name": "transfer-swap-patient-siderail-button",
|
|
50
|
+
"slot": "action-menu-ward-patient-items-slot",
|
|
51
|
+
"component": "patientTransferAndSwapWorkspaceSiderailIcon"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"component": "admissionRequestNoteRowExtension",
|
|
55
|
+
"name": "admission-request-note-card-row",
|
|
56
|
+
"slot": "ward-patient-card-slot"
|
|
23
57
|
}
|
|
24
58
|
],
|
|
25
|
-
"
|
|
59
|
+
"workspaces": [
|
|
26
60
|
{
|
|
27
|
-
"
|
|
28
|
-
"
|
|
61
|
+
"name": "admission-requests-workspace",
|
|
62
|
+
"component": "admissionRequestWorkspace",
|
|
63
|
+
"title": "admissionRequests",
|
|
64
|
+
"type": "admission-requests"
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
"name": "ward-patient-notes-workspace",
|
|
68
|
+
"component": "wardPatientNotesWorkspace",
|
|
69
|
+
"type": "ward-patient-notes",
|
|
70
|
+
"title": "inpatientNotesWorkspaceTitle",
|
|
71
|
+
"sidebarFamily": "ward-patient",
|
|
72
|
+
"hasOwnSidebar": true
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"name": "admit-patient-form-workspace",
|
|
76
|
+
"component": "admitPatientFormWorkspace",
|
|
77
|
+
"title": "admissionRequests",
|
|
78
|
+
"type": "admission-requests"
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
"name": "ward-patient-workspace",
|
|
82
|
+
"component": "wardPatientWorkspace",
|
|
83
|
+
"type": "ward",
|
|
84
|
+
"title": "Ward Patient",
|
|
85
|
+
"width": "extra-wide",
|
|
86
|
+
"hasOwnSidebar": true,
|
|
87
|
+
"sidebarFamily": "ward-patient"
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"name": "patient-transfer-swap-workspace",
|
|
91
|
+
"component": "patientTransferAndSwapWorkspace",
|
|
92
|
+
"title": "transfers",
|
|
93
|
+
"type": "transfer-swap-bed-form",
|
|
94
|
+
"hasOwnSidebar": true,
|
|
95
|
+
"sidebarFamily": "ward-patient"
|
|
29
96
|
}
|
|
30
97
|
]
|
|
31
98
|
}
|
package/src/types/index.ts
CHANGED
|
@@ -1,45 +1,59 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
import type {
|
|
2
|
+
Concept,
|
|
3
|
+
DefaultWorkspaceProps,
|
|
4
|
+
Location,
|
|
5
|
+
OpenmrsResource,
|
|
6
|
+
OpenmrsResourceStrict,
|
|
7
|
+
Patient,
|
|
8
|
+
Person,
|
|
9
|
+
Visit,
|
|
8
10
|
} from '@openmrs/esm-framework';
|
|
9
11
|
import type React from 'react';
|
|
10
12
|
|
|
11
|
-
export
|
|
13
|
+
export type WardPatientCard = React.FC<WardPatient>;
|
|
14
|
+
|
|
15
|
+
// WardPatient is a patient admitted to a ward, and/or in a bed on a ward
|
|
16
|
+
export type WardPatient = {
|
|
17
|
+
/**
|
|
18
|
+
* The patient and their current visit. These values are taken either
|
|
19
|
+
* from either the inpatientAdmission object, the inpatientRequest object
|
|
20
|
+
* or the admissionLocation object (which contains the bed)
|
|
21
|
+
*/
|
|
12
22
|
patient: Patient;
|
|
13
23
|
visit: Visit;
|
|
14
|
-
bed: Bed;
|
|
15
|
-
}
|
|
16
24
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
'patient-name',
|
|
23
|
-
'patient-age',
|
|
24
|
-
'patient-address',
|
|
25
|
-
'patient-obs',
|
|
26
|
-
'patient-coded-obs-tags',
|
|
27
|
-
'admission-time',
|
|
28
|
-
] as const;
|
|
29
|
-
export type PatientCardElementType = (typeof patientCardElementTypes)[number];
|
|
25
|
+
/**
|
|
26
|
+
* the bed assigned to the patient. This object is only set if the patient
|
|
27
|
+
* has a bed assigned
|
|
28
|
+
*/
|
|
29
|
+
bed: Bed;
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
/**
|
|
32
|
+
* The admission detail. This object is only set if the patient has been
|
|
33
|
+
* admitted to the ward
|
|
34
|
+
*/
|
|
35
|
+
inpatientAdmission: InpatientAdmission;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The admission request. The object is only set if the patient has a
|
|
39
|
+
* pending admission / transfer request.
|
|
40
|
+
*/
|
|
41
|
+
inpatientRequest: InpatientRequest;
|
|
42
|
+
};
|
|
43
|
+
export interface WardPatientWorkspaceProps extends DefaultWorkspaceProps {
|
|
44
|
+
wardPatient: WardPatient;
|
|
45
|
+
}
|
|
34
46
|
|
|
35
47
|
// server-side types defined in openmrs-module-bedmanagement:
|
|
36
48
|
|
|
37
|
-
|
|
49
|
+
// note "AdmissionLocationResponse" isn't the clearest name, but it matches the endpoint; endpoint fetches bed information (including info about patients in those beds) for a location (as provided by the bed management module)
|
|
50
|
+
export interface AdmissionLocationFetchResponse {
|
|
38
51
|
totalBeds: number;
|
|
39
52
|
occupiedBeds: number;
|
|
40
53
|
ward: Location;
|
|
41
54
|
bedLayouts: Array<BedLayout>;
|
|
42
55
|
}
|
|
56
|
+
|
|
43
57
|
export interface Bed {
|
|
44
58
|
id: number;
|
|
45
59
|
uuid: string;
|
|
@@ -83,34 +97,43 @@ interface BedTagMap {
|
|
|
83
97
|
|
|
84
98
|
export type BedStatus = 'AVAILABLE' | 'OCCUPIED';
|
|
85
99
|
|
|
86
|
-
//
|
|
87
|
-
|
|
88
|
-
|
|
100
|
+
// GET /rest/emrapi/inpatient/request
|
|
101
|
+
export interface InpatientRequestFetchResponse {
|
|
102
|
+
results: InpatientRequest[];
|
|
103
|
+
}
|
|
89
104
|
|
|
90
|
-
// InpatientRequest[] returned by:
|
|
91
|
-
// GET /rest/emrapi/inpatient/admissionRequests
|
|
92
|
-
// GET /rest/emrapi/inpatient/transferRequests
|
|
93
|
-
// GET /rest/emrapi/inpatient/admissionAndTransferRequests
|
|
94
105
|
export interface InpatientRequest {
|
|
95
106
|
patient: Patient;
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
encounter?: Encounter;
|
|
101
|
-
dispositionObs?: Observation;
|
|
107
|
+
dispositionType: DispositionType;
|
|
108
|
+
disposition: Concept;
|
|
109
|
+
dispositionEncounter?: Encounter;
|
|
110
|
+
dispositionObsGroup?: Observation;
|
|
102
111
|
dispositionLocation?: Location;
|
|
103
|
-
|
|
112
|
+
visit: Visit;
|
|
104
113
|
}
|
|
105
114
|
|
|
106
|
-
|
|
115
|
+
export type DispositionType = 'ADMIT' | 'TRANSFER' | 'DISCHARGE';
|
|
116
|
+
|
|
107
117
|
// GET /rest/emrapi/inpatient/visits
|
|
108
|
-
export interface
|
|
118
|
+
export interface InpatientAdmissionFetchResponse {
|
|
119
|
+
results: InpatientAdmission[];
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export interface InpatientAdmission {
|
|
109
123
|
patient: Patient;
|
|
110
124
|
visit: Visit;
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
125
|
+
|
|
126
|
+
// the encounter of type "Admission" or "Transfer" that is responsible
|
|
127
|
+
// for assigning the patient to the current inpatient location. For example,
|
|
128
|
+
// if the patient has been admitted /transferred to multiple locations as follows:
|
|
129
|
+
// A -> B -> A
|
|
130
|
+
// then encounterAssigningToCurrentInpatientLocation
|
|
131
|
+
// would be the transfer encounter that lands the patient back to A.
|
|
132
|
+
encounterAssigningToCurrentInpatientLocation: Encounter;
|
|
133
|
+
|
|
134
|
+
// the first encounter of the visit that is of encounterType "Admission" or "Transfer",
|
|
135
|
+
// regardless of the admission location
|
|
136
|
+
firstAdmissionOrTransferEncounter: Encounter;
|
|
114
137
|
}
|
|
115
138
|
|
|
116
139
|
// TODO: Move these types to esm-core
|
|
@@ -161,3 +184,21 @@ export interface EncounterRole extends OpenmrsResourceStrict {
|
|
|
161
184
|
description?: string;
|
|
162
185
|
retired?: boolean;
|
|
163
186
|
}
|
|
187
|
+
|
|
188
|
+
export interface EncounterPayload {
|
|
189
|
+
encounterDatetime?: string;
|
|
190
|
+
encounterType: string;
|
|
191
|
+
patient: string;
|
|
192
|
+
location: string;
|
|
193
|
+
encounterProviders?: Array<{ encounterRole: string; provider: string }>;
|
|
194
|
+
obs: Array<ObsPayload>;
|
|
195
|
+
form?: string;
|
|
196
|
+
orders?: Array<any>;
|
|
197
|
+
visit?: string;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export interface ObsPayload {
|
|
201
|
+
concept: Concept | string;
|
|
202
|
+
value?: string;
|
|
203
|
+
groupMembers?: Array<ObsPayload>;
|
|
204
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type ObsElementDefinition } from '../../config-schema';
|
|
3
|
+
import { type WardPatientCard } from '../../types';
|
|
4
|
+
import WardPatientObs from '../row-elements/ward-patient-obs';
|
|
5
|
+
import { useConfig } from '@openmrs/esm-framework';
|
|
6
|
+
|
|
7
|
+
const AdmissionRequestNoteRowExtension: WardPatientCard = ({ patient, visit, inpatientAdmission }) => {
|
|
8
|
+
const { conceptUuid } = useConfig<ObsElementDefinition>();
|
|
9
|
+
const config: ObsElementDefinition = {
|
|
10
|
+
conceptUuid,
|
|
11
|
+
limit: 0,
|
|
12
|
+
id: 'admission-note',
|
|
13
|
+
onlyWithinCurrentVisit: true,
|
|
14
|
+
orderBy: 'ascending',
|
|
15
|
+
label: 'Admission Note',
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// only show if the patient has not been admitted yet
|
|
19
|
+
const admitted = inpatientAdmission != null;
|
|
20
|
+
if (admitted) {
|
|
21
|
+
return <></>;
|
|
22
|
+
} else {
|
|
23
|
+
return <WardPatientObs config={config} patient={patient} visit={visit} />;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export default AdmissionRequestNoteRowExtension;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useConfig } from '@openmrs/esm-framework';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { type ColoredObsTagsCardRowConfigObject } from '../../config-schema-extension-colored-obs-tags';
|
|
4
|
+
import { type WardPatientCard } from '../../types';
|
|
5
|
+
import WardPatientCodedObsTags from '../row-elements/ward-patient-coded-obs-tags';
|
|
6
|
+
|
|
7
|
+
const ColoredObsTagsCardRowExtension: WardPatientCard = ({ patient, visit }) => {
|
|
8
|
+
const config = useConfig<ColoredObsTagsCardRowConfigObject>();
|
|
9
|
+
|
|
10
|
+
return <WardPatientCodedObsTags config={config} patient={patient} visit={visit} />;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export default ColoredObsTagsCardRowExtension;
|
|
@@ -1,18 +1,12 @@
|
|
|
1
|
+
import { age, type Patient } from '@openmrs/esm-framework';
|
|
1
2
|
import React from 'react';
|
|
2
|
-
import { useTranslation } from 'react-i18next';
|
|
3
|
-
import { type WardPatientCardElement } from '../../types';
|
|
4
3
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
{t('yearsOld', '{{age}} yrs', {
|
|
12
|
-
age: patient?.person?.age,
|
|
13
|
-
})}
|
|
14
|
-
</div>
|
|
15
|
-
);
|
|
4
|
+
export interface WardPatientAgeProps {
|
|
5
|
+
patient: Patient;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const WardPatientAge: React.FC<WardPatientAgeProps> = ({ patient }) => {
|
|
9
|
+
return <div>{age(patient.person?.birthdate)}</div>;
|
|
16
10
|
};
|
|
17
11
|
|
|
18
12
|
export default WardPatientAge;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
+
import { type Bed } from '../../types';
|
|
2
3
|
import styles from '../ward-patient-card.scss';
|
|
3
|
-
import { type WardPatientCardElement } from '../../types';
|
|
4
4
|
|
|
5
|
-
const WardPatientBedNumber:
|
|
5
|
+
const WardPatientBedNumber: React.FC<{ bed: Bed }> = ({ bed }) => {
|
|
6
6
|
if (!bed) {
|
|
7
7
|
return <></>;
|
|
8
8
|
}
|
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import { SkeletonText, Tag } from '@carbon/react';
|
|
2
|
-
import { translateFrom, type OpenmrsResource } from '@openmrs/esm-framework';
|
|
2
|
+
import { type Patient, translateFrom, type Visit, type OpenmrsResource } from '@openmrs/esm-framework';
|
|
3
3
|
import React from 'react';
|
|
4
4
|
import { useTranslation } from 'react-i18next';
|
|
5
|
-
import { type PatientCodedObsTagsElementConfig } from '../../config-schema';
|
|
6
5
|
import { moduleName } from '../../constant';
|
|
7
6
|
import { useObs } from '../../hooks/useObs';
|
|
8
|
-
import { type WardPatientCardElement } from '../../types';
|
|
9
7
|
import styles from '../ward-patient-card.scss';
|
|
10
8
|
import { obsCustomRepresentation, useConceptToTagColorMap } from './ward-patient-obs.resource';
|
|
9
|
+
import { type ColoredObsTagsCardRowConfigObject } from '../../config-schema-extension-colored-obs-tags';
|
|
10
|
+
|
|
11
|
+
interface WardPatientCodedObsTagsProps {
|
|
12
|
+
config: ColoredObsTagsCardRowConfigObject;
|
|
13
|
+
patient: Patient;
|
|
14
|
+
visit: Visit;
|
|
15
|
+
}
|
|
11
16
|
|
|
12
17
|
/**
|
|
13
18
|
* The WardPatientCodedObsTags displays observations of coded values of a particular concept in the active visit as tags.
|
|
@@ -19,62 +24,58 @@ import { obsCustomRepresentation, useConceptToTagColorMap } from './ward-patient
|
|
|
19
24
|
* @param config
|
|
20
25
|
* @returns
|
|
21
26
|
*/
|
|
22
|
-
const
|
|
23
|
-
const
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
const { data: conceptToTagColorMap } = useConceptToTagColorMap(config);
|
|
28
|
-
|
|
29
|
-
if (isLoading) {
|
|
30
|
-
return <SkeletonText />;
|
|
31
|
-
} else {
|
|
32
|
-
const obsToDisplay = data?.data?.results?.filter((o) => {
|
|
33
|
-
const matchVisit = o.encounter.visit?.uuid == visit?.uuid;
|
|
34
|
-
return matchVisit || visit == null; // TODO: remove visit == null hack when server API supports returning visit
|
|
35
|
-
});
|
|
27
|
+
const WardPatientCodedObsTags: React.FC<WardPatientCodedObsTagsProps> = ({ config, patient, visit }) => {
|
|
28
|
+
const { conceptUuid, summaryLabel, summaryLabelColor, summaryLabelI18nModule } = config;
|
|
29
|
+
const { data, isLoading } = useObs({ patient: patient.uuid, concept: conceptUuid }, obsCustomRepresentation);
|
|
30
|
+
const { t } = useTranslation();
|
|
31
|
+
const { data: conceptToTagColorMap } = useConceptToTagColorMap(config.tags);
|
|
36
32
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
33
|
+
if (isLoading) {
|
|
34
|
+
return <SkeletonText />;
|
|
35
|
+
} else {
|
|
36
|
+
const obsToDisplay = data?.data?.results?.filter((o) => {
|
|
37
|
+
const matchVisit = o.encounter.visit?.uuid == visit?.uuid;
|
|
38
|
+
return matchVisit || visit == null; // TODO: remove visit == null hack when server API supports returning visit
|
|
39
|
+
});
|
|
41
40
|
|
|
42
|
-
|
|
43
|
-
|
|
41
|
+
const summaryLabelToDisplay =
|
|
42
|
+
summaryLabel != null
|
|
43
|
+
? translateFrom(summaryLabelI18nModule ?? moduleName, summaryLabel)
|
|
44
|
+
: obsToDisplay?.[0]?.concept?.display;
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
return (
|
|
48
|
-
<Tag type={color} key={uuid}>
|
|
49
|
-
{display}
|
|
50
|
-
</Tag>
|
|
51
|
-
);
|
|
52
|
-
} else {
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
});
|
|
46
|
+
const obsNodes = obsToDisplay?.map((o) => {
|
|
47
|
+
const { display, uuid } = o.value as OpenmrsResource;
|
|
56
48
|
|
|
57
|
-
const
|
|
58
|
-
if (
|
|
49
|
+
const color = conceptToTagColorMap?.get(uuid);
|
|
50
|
+
if (color) {
|
|
59
51
|
return (
|
|
60
|
-
<
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
{obsWithNoTagCount > 0 ? (
|
|
64
|
-
<Tag type={summaryLabelColor}>
|
|
65
|
-
{t('countItems', '{{count}} {{item}}', { count: obsWithNoTagCount, item: summaryLabelToDisplay })}
|
|
66
|
-
</Tag>
|
|
67
|
-
) : null}
|
|
68
|
-
</span>
|
|
69
|
-
</div>
|
|
52
|
+
<Tag type={color} key={`ward-coded-obs-tag-${o.uuid}`}>
|
|
53
|
+
{display}
|
|
54
|
+
</Tag>
|
|
70
55
|
);
|
|
71
56
|
} else {
|
|
72
57
|
return null;
|
|
73
58
|
}
|
|
74
|
-
}
|
|
75
|
-
};
|
|
59
|
+
});
|
|
76
60
|
|
|
77
|
-
|
|
61
|
+
const obsWithNoTagCount = obsNodes.filter((o) => o == null).length;
|
|
62
|
+
if (obsNodes?.length > 0 || obsWithNoTagCount > 0) {
|
|
63
|
+
return (
|
|
64
|
+
<div>
|
|
65
|
+
<span className={styles.wardPatientObsLabel}>
|
|
66
|
+
{obsNodes}
|
|
67
|
+
{obsWithNoTagCount > 0 ? (
|
|
68
|
+
<Tag type={summaryLabelColor}>
|
|
69
|
+
{t('countItems', '{{count}} {{item}}', { count: obsWithNoTagCount, item: summaryLabelToDisplay })}
|
|
70
|
+
</Tag>
|
|
71
|
+
) : null}
|
|
72
|
+
</span>
|
|
73
|
+
</div>
|
|
74
|
+
);
|
|
75
|
+
} else {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
78
79
|
};
|
|
79
80
|
|
|
80
|
-
export default
|
|
81
|
+
export default WardPatientCodedObsTags;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type Patient } from '@openmrs/esm-framework';
|
|
2
|
+
import { type TFunction } from 'i18next';
|
|
3
|
+
import React from 'react';
|
|
4
|
+
import { useTranslation } from 'react-i18next';
|
|
5
|
+
|
|
6
|
+
const WardPatientGender: React.FC<{ patient: Patient }> = ({ patient }) => {
|
|
7
|
+
const { t } = useTranslation();
|
|
8
|
+
|
|
9
|
+
return <div>{getGender(t, patient?.person?.gender)}</div>;
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
export const getGender = (t: TFunction, gender: string): string => {
|
|
13
|
+
switch (gender) {
|
|
14
|
+
case 'M':
|
|
15
|
+
return t('male', 'Male');
|
|
16
|
+
case 'F':
|
|
17
|
+
return t('female', 'Female');
|
|
18
|
+
case 'O':
|
|
19
|
+
return t('other', 'Other');
|
|
20
|
+
case 'unknown':
|
|
21
|
+
return t('unknown', 'Unknown');
|
|
22
|
+
default:
|
|
23
|
+
return gender;
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export default WardPatientGender;
|
|
@@ -1,22 +1,23 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { type WardPatientCardElement } from '../../types';
|
|
3
2
|
import styles from '../ward-patient-card.scss';
|
|
4
|
-
import { type
|
|
3
|
+
import { type Patient } from '@openmrs/esm-framework';
|
|
4
|
+
import { type AddressElementDefinition } from '../../config-schema';
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
export interface WardPatientAddressProps {
|
|
7
|
+
patient: Patient;
|
|
8
|
+
config: AddressElementDefinition;
|
|
9
|
+
}
|
|
9
10
|
|
|
10
|
-
|
|
11
|
+
const WardPatientAddress: React.FC<WardPatientAddressProps> = ({ patient, config }) => {
|
|
12
|
+
const preferredAddress = patient?.person?.preferredAddress;
|
|
11
13
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return wardPatientAddress;
|
|
14
|
+
return (
|
|
15
|
+
<>
|
|
16
|
+
{config.fields?.map((field, i) =>
|
|
17
|
+
preferredAddress?.[field] ? <div key={i}>{preferredAddress?.[field] as string}</div> : <div key={i}></div>,
|
|
18
|
+
)}
|
|
19
|
+
</>
|
|
20
|
+
);
|
|
20
21
|
};
|
|
21
22
|
|
|
22
|
-
export default
|
|
23
|
+
export default WardPatientAddress;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { type IdentifierElementDefinition } from '../../config-schema';
|
|
3
|
+
import { Tag } from '@carbon/react';
|
|
4
|
+
import { type Patient, translateFrom, type PatientIdentifier } from '@openmrs/esm-framework';
|
|
5
|
+
import { moduleName } from '../../constant';
|
|
6
|
+
import { useTranslation } from 'react-i18next';
|
|
7
|
+
|
|
8
|
+
/** Sort the identifiers by preferred first. The identifier with value of true
|
|
9
|
+
* takes precedence over false. If both identifiers have same preferred value,
|
|
10
|
+
* sort them by most recently created or changed. */
|
|
11
|
+
const identifierCompareFunction = (pi1: PatientIdentifier, pi2: PatientIdentifier) => {
|
|
12
|
+
let comp = (pi2.preferred ? 1 : 0) - (pi1.preferred ? 1 : 0);
|
|
13
|
+
|
|
14
|
+
if (comp == 0) {
|
|
15
|
+
const date1 = pi1.auditInfo.dateChanged ?? pi1.auditInfo.dateCreated;
|
|
16
|
+
const date2 = pi2.auditInfo.dateChanged ?? pi2.auditInfo.dateCreated;
|
|
17
|
+
comp = date2.localeCompare(date1);
|
|
18
|
+
}
|
|
19
|
+
return comp;
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export interface WardPatientIdentifierProps {
|
|
23
|
+
patient: Patient;
|
|
24
|
+
/** If the config is not passed, this will be the default identifier element, which uses the preferred identifier type. */
|
|
25
|
+
config?: IdentifierElementDefinition;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const defaultConfig: IdentifierElementDefinition = {
|
|
29
|
+
id: 'patient-identifier',
|
|
30
|
+
identifierTypeUuid: null,
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const WardPatientIdentifier: React.FC<WardPatientIdentifierProps> = ({ config: configProp, patient }) => {
|
|
34
|
+
const { t } = useTranslation();
|
|
35
|
+
const config = configProp ?? defaultConfig;
|
|
36
|
+
const { identifierTypeUuid, labelI18nModule: labelModule, label } = config;
|
|
37
|
+
const patientIdentifiers = patient.identifiers.filter(
|
|
38
|
+
(patientIdentifier: PatientIdentifier) =>
|
|
39
|
+
identifierTypeUuid == null || patientIdentifier.identifierType?.uuid === identifierTypeUuid,
|
|
40
|
+
);
|
|
41
|
+
patientIdentifiers.sort(identifierCompareFunction);
|
|
42
|
+
const patientIdentifier = patientIdentifiers[0];
|
|
43
|
+
const labelToDisplay =
|
|
44
|
+
label != null ? translateFrom(labelModule ?? moduleName, label) : patientIdentifier?.identifierType?.name;
|
|
45
|
+
return (
|
|
46
|
+
<div>
|
|
47
|
+
{labelToDisplay ? <Tag>{t('identifierTypelabel', '{{label}}:', { label: labelToDisplay })}</Tag> : <></>}
|
|
48
|
+
<span>{patientIdentifier?.identifier}</span>
|
|
49
|
+
</div>
|
|
50
|
+
);
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
export default WardPatientIdentifier;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
|
-
import { type
|
|
2
|
+
import { type Patient } from '@openmrs/esm-framework';
|
|
3
3
|
import styles from '../ward-patient-card.scss';
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
return <div className={styles.wardPatientName}>{
|
|
5
|
+
export interface WardPatientNameProps {
|
|
6
|
+
patient: Patient;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const WardPatientName: React.FC<WardPatientNameProps> = ({ patient }) => {
|
|
10
|
+
return <div className={styles.wardPatientName}>{patient?.person?.preferredName?.display}</div>;
|
|
11
11
|
};
|
|
12
12
|
|
|
13
13
|
export default WardPatientName;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { openmrsFetch, restBaseUrl, type Concept } from '@openmrs/esm-framework';
|
|
2
2
|
import useSWRImmutable from 'swr/immutable';
|
|
3
|
-
import { type
|
|
3
|
+
import { type TagConfigObject } from '../../config-schema-extension-colored-obs-tags';
|
|
4
4
|
|
|
5
5
|
// prettier-ignore
|
|
6
6
|
export const obsCustomRepresentation =
|
|
@@ -12,13 +12,13 @@ export const obsCustomRepresentation =
|
|
|
12
12
|
// get the setMembers of a concept set
|
|
13
13
|
const conceptSetCustomRepresentation = 'custom:(uuid,setMembers:(uuid))';
|
|
14
14
|
|
|
15
|
-
export function useConceptToTagColorMap(
|
|
15
|
+
export function useConceptToTagColorMap(tags: Array<TagConfigObject>) {
|
|
16
16
|
// fetch the members of the concept sets and process the data
|
|
17
17
|
// to return conceptToTagColorMap (wrapped in a promise).
|
|
18
18
|
// Let swr cache the result of this function.
|
|
19
19
|
const fetchAndMap = (url: string) => {
|
|
20
20
|
const conceptSetToTagColorMap = new Map<string, string>();
|
|
21
|
-
for (const tag of
|
|
21
|
+
for (const tag of tags) {
|
|
22
22
|
const { color, appliedToConceptSets } = tag;
|
|
23
23
|
for (const answer of appliedToConceptSets ?? []) {
|
|
24
24
|
if (!conceptSetToTagColorMap.has(answer)) {
|
|
@@ -44,7 +44,7 @@ export function useConceptToTagColorMap(codedObsTagsConfig: PatientCodedObsTagsE
|
|
|
44
44
|
});
|
|
45
45
|
};
|
|
46
46
|
|
|
47
|
-
const conceptSetUuids =
|
|
47
|
+
const conceptSetUuids = tags.flatMap((tag) => tag.appliedToConceptSets);
|
|
48
48
|
const apiUrl = `${restBaseUrl}/concept?references=${conceptSetUuids.join()}&v=${conceptSetCustomRepresentation}`;
|
|
49
49
|
const conceptToTagColorMap = useSWRImmutable(apiUrl, fetchAndMap);
|
|
50
50
|
|