@kenyaemr/esm-ward-app 7.0.3-pre.88 → 7.0.3-pre.94

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 (146) hide show
  1. package/.turbo/turbo-build.log +24 -16
  2. package/dist/130.js +1 -1
  3. package/dist/130.js.map +1 -1
  4. package/dist/169.js +1 -0
  5. package/dist/169.js.map +1 -0
  6. package/dist/269.js +1 -0
  7. package/dist/269.js.map +1 -0
  8. package/dist/346.js +1 -0
  9. package/dist/346.js.map +1 -0
  10. package/dist/348.js +1 -0
  11. package/dist/348.js.map +1 -0
  12. package/dist/466.js +1 -0
  13. package/dist/466.js.map +1 -0
  14. package/dist/501.js +1 -0
  15. package/dist/501.js.map +1 -0
  16. package/dist/574.js +1 -1
  17. package/dist/577.js +1 -0
  18. package/dist/577.js.map +1 -0
  19. package/dist/659.js +1 -0
  20. package/dist/659.js.map +1 -0
  21. package/dist/749.js +1 -0
  22. package/dist/749.js.map +1 -0
  23. package/dist/76.js +1 -0
  24. package/dist/76.js.map +1 -0
  25. package/dist/767.js +1 -0
  26. package/dist/767.js.map +1 -0
  27. package/dist/793.js +2 -0
  28. package/dist/793.js.map +1 -0
  29. package/dist/803.js +1 -0
  30. package/dist/803.js.map +1 -0
  31. package/dist/940.js +1 -0
  32. package/dist/940.js.map +1 -0
  33. package/dist/960.js +1 -0
  34. package/dist/960.js.map +1 -0
  35. package/dist/kenyaemr-esm-ward-app.js +1 -1
  36. package/dist/kenyaemr-esm-ward-app.js.buildmanifest.json +330 -42
  37. package/dist/kenyaemr-esm-ward-app.js.map +1 -1
  38. package/dist/main.js +1 -1
  39. package/dist/main.js.map +1 -1
  40. package/dist/routes.json +1 -1
  41. package/package.json +2 -2
  42. package/src/action-menu-buttons/transfer-workspace-siderail.component.tsx +27 -0
  43. package/src/beds/empty-bed.component.tsx +1 -1
  44. package/src/beds/empty-bed.scss +6 -6
  45. package/src/beds/occupied-bed.component.tsx +5 -5
  46. package/src/beds/occupied-bed.scss +2 -3
  47. package/src/beds/occupied-bed.test.tsx +37 -21
  48. package/src/beds/unassigned-patient.component.tsx +20 -0
  49. package/src/beds/unassigned-patient.scss +6 -0
  50. package/src/config-schema-admission-request-note.ts +9 -0
  51. package/src/config-schema-extension-colored-obs-tags.ts +91 -0
  52. package/src/config-schema.ts +165 -231
  53. package/src/createDashboardLink.component.tsx +42 -0
  54. package/src/hooks/useAdmissionLocation.ts +12 -7
  55. package/src/hooks/useCurrentWardCardConfig.ts +32 -0
  56. package/src/hooks/useEmrConfiguration.ts +112 -0
  57. package/src/hooks/useInpatientAdmission.ts +28 -0
  58. package/src/hooks/useInpatientRequest.ts +39 -9
  59. package/src/hooks/useLocation.test.ts +38 -0
  60. package/src/hooks/useLocation.ts +9 -0
  61. package/src/hooks/useLocations.ts +54 -0
  62. package/src/hooks/useMostRecentObs.ts +27 -0
  63. package/src/hooks/useRestPatient.ts +18 -0
  64. package/src/hooks/useWardLocation.test.ts +108 -0
  65. package/src/hooks/useWardLocation.ts +26 -0
  66. package/src/index.ts +71 -4
  67. package/src/location-selector/location-selector.component.tsx +118 -0
  68. package/src/location-selector/location-selector.scss +48 -0
  69. package/src/root.component.tsx +2 -1
  70. package/src/routes.json +79 -12
  71. package/src/types/index.ts +87 -46
  72. package/src/ward-patient-card/card-rows/admission-request-note.extension.tsx +27 -0
  73. package/src/ward-patient-card/card-rows/colored-obs-tags-card-row.extension.tsx +13 -0
  74. package/src/ward-patient-card/row-elements/ward-patient-age.tsx +7 -13
  75. package/src/ward-patient-card/row-elements/ward-patient-bed-number.tsx +2 -2
  76. package/src/ward-patient-card/row-elements/ward-patient-coded-obs-tags.tsx +51 -50
  77. package/src/ward-patient-card/row-elements/ward-patient-gender.component.tsx +27 -0
  78. package/src/ward-patient-card/row-elements/ward-patient-header-address.tsx +16 -15
  79. package/src/ward-patient-card/row-elements/ward-patient-identifier.tsx +53 -0
  80. package/src/ward-patient-card/row-elements/ward-patient-name.tsx +7 -7
  81. package/src/ward-patient-card/row-elements/ward-patient-obs.resource.ts +4 -4
  82. package/src/ward-patient-card/row-elements/ward-patient-obs.tsx +45 -44
  83. package/src/ward-patient-card/row-elements/ward-patient-time-on-ward.tsx +22 -0
  84. package/src/ward-patient-card/row-elements/ward-patient-time-since-admission.tsx +22 -0
  85. package/src/ward-patient-card/ward-patient-card-element.component.tsx +65 -0
  86. package/src/ward-patient-card/ward-patient-card.component.tsx +64 -0
  87. package/src/ward-patient-card/ward-patient-card.scss +61 -12
  88. package/src/ward-patient-workspace/ward-patient-action-button.extension.tsx +18 -0
  89. package/src/ward-patient-workspace/ward-patient.style.scss +11 -0
  90. package/src/ward-patient-workspace/ward-patient.workspace.tsx +51 -0
  91. package/src/ward-view/ward-bed.component.tsx +0 -1
  92. package/src/ward-view/ward-view.component.tsx +114 -76
  93. package/src/ward-view/ward-view.resource.ts +2 -2
  94. package/src/ward-view/ward-view.scss +4 -4
  95. package/src/ward-view/ward-view.test.tsx +76 -49
  96. package/src/ward-view-header/admission-requests-bar.component.tsx +29 -28
  97. package/src/ward-view-header/admission-requests-bar.test.tsx +11 -15
  98. package/src/ward-view-header/admission-requests.scss +20 -25
  99. package/src/ward-view-header/ward-view-header.component.tsx +7 -7
  100. package/src/ward-view-header/ward-view-header.scss +2 -2
  101. package/src/ward-workspace/admission-request-card/admission-request-card-actions.component.tsx +29 -0
  102. package/src/ward-workspace/admission-request-card/admission-request-card-header.component.tsx +51 -0
  103. package/src/ward-workspace/admission-request-card/admission-request-card.component.tsx +16 -0
  104. package/src/ward-workspace/admission-request-card/admission-request-card.scss +49 -0
  105. package/src/ward-workspace/admission-request-workspace/admission-requests-workspace.scss +12 -0
  106. package/src/ward-workspace/admission-request-workspace/admission-requests-workspace.test.tsx +48 -0
  107. package/src/ward-workspace/admission-request-workspace/admission-requests.workspace.tsx +61 -0
  108. package/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.scss +35 -0
  109. package/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.test.tsx +341 -0
  110. package/src/ward-workspace/admit-patient-form-workspace/admit-patient-form.workspace.tsx +267 -0
  111. package/src/ward-workspace/admit-patient-form-workspace/types.ts +7 -0
  112. package/src/ward-workspace/patient-banner/patient-banner.component.tsx +29 -0
  113. package/src/ward-workspace/patient-banner/style.scss +23 -0
  114. package/src/ward-workspace/patient-transfer-bed-swap/patient-bed-swap-form.component.tsx +210 -0
  115. package/src/ward-workspace/patient-transfer-bed-swap/patient-transfer-request-form.component.tsx +238 -0
  116. package/src/ward-workspace/patient-transfer-bed-swap/patient-transfer-swap.scss +73 -0
  117. package/src/ward-workspace/patient-transfer-bed-swap/patient-transfer-swap.workspace.tsx +44 -0
  118. package/src/ward-workspace/ward-patient-notes/form/notes-form.component.tsx +180 -0
  119. package/src/ward-workspace/ward-patient-notes/form/notes-form.scss +30 -0
  120. package/src/ward-workspace/ward-patient-notes/form/notes-form.test.tsx +116 -0
  121. package/src/ward-workspace/ward-patient-notes/history/note.component.tsx +53 -0
  122. package/src/ward-workspace/ward-patient-notes/history/notes-container.component.tsx +55 -0
  123. package/src/ward-workspace/ward-patient-notes/history/notes-container.test.tsx +84 -0
  124. package/src/ward-workspace/ward-patient-notes/history/styles.scss +61 -0
  125. package/src/ward-workspace/ward-patient-notes/notes-action-button.extension.tsx +18 -0
  126. package/src/ward-workspace/ward-patient-notes/notes.resource.ts +71 -0
  127. package/src/ward-workspace/ward-patient-notes/notes.workspace.tsx +25 -0
  128. package/src/ward-workspace/ward-patient-notes/types.ts +44 -0
  129. package/src/ward.resource.ts +25 -0
  130. package/translations/en.json +63 -2
  131. package/dist/443.js +0 -1
  132. package/dist/443.js.map +0 -1
  133. package/dist/589.js +0 -1
  134. package/dist/589.js.map +0 -1
  135. package/dist/695.js +0 -2
  136. package/dist/695.js.map +0 -1
  137. package/src/hooks/useAdmittedPatients.ts +0 -13
  138. package/src/ward-patient-card/row-elements/row-elements.scss +0 -16
  139. package/src/ward-patient-card/ward-patient-card-row.resources.tsx +0 -92
  140. package/src/ward-patient-card/ward-patient-card.tsx +0 -20
  141. package/src/ward-workspace/admission-request-card.component.tsx +0 -23
  142. package/src/ward-workspace/admission-request-card.scss +0 -34
  143. package/src/ward-workspace/admission-request-workspace.test.tsx +0 -38
  144. package/src/ward-workspace/admission-requests-workspace.component.tsx +0 -21
  145. package/src/ward-workspace/admission-requests-workspace.scss +0 -13
  146. /package/dist/{695.js.LICENSE.txt → 793.js.LICENSE.txt} +0 -0
@@ -1,136 +1,143 @@
1
- import { Type, validators, type ConfigSchema, type PersonAddress } from '@openmrs/esm-framework';
2
- import { patientCardElementTypes, type PatientCardElementType } from './types';
1
+ import { type ConfigSchema, Type, validators } from '@openmrs/esm-framework';
3
2
 
4
- const defaultWardPatientCard: WardPatientCardDefinition = {
5
- id: 'default-card',
6
- rows: [
7
- {
8
- rowType: 'header',
9
- elements: ['bed-number', 'patient-name', 'patient-age', 'patient-address'],
10
- },
11
- ],
3
+ export const defaultWardPatientCard: WardPatientCardDefinition = {
4
+ id: 'default',
5
+ headerRowElements: ['patient-age', 'patient-address', 'patient-identifier'],
6
+ footerRowElements: [],
12
7
  appliedTo: null,
13
8
  };
14
9
 
15
- const defaultPatientAddressFields: Array<keyof PersonAddress> = ['cityVillage', 'country'];
16
-
17
- export const defaultPatientCardElementConfig: PatientCardElementConfig = {
18
- address: {
19
- addressFields: defaultPatientAddressFields,
20
- },
21
- obs: null,
22
- codedObsTags: null,
23
- };
24
-
25
- export const builtInPatientCardElements: PatientCardElementType[] = [
26
- 'bed-number',
27
- 'patient-name',
28
- 'patient-age',
29
- 'patient-address',
30
- ];
10
+ export const builtInPatientCardElements = ['patient-age', 'time-on-ward', 'time-since-admission'];
11
+
12
+ export const addressFields = [
13
+ 'cityVillage',
14
+ 'stateProvince',
15
+ 'country',
16
+ 'postalCode',
17
+ 'countyDistrict',
18
+ 'latitude',
19
+ 'longitude',
20
+ 'address1',
21
+ 'address2',
22
+ 'address3',
23
+ 'address4',
24
+ 'address5',
25
+ 'address6',
26
+ 'address7',
27
+ 'address8',
28
+ 'address9',
29
+ 'address10',
30
+ 'address11',
31
+ 'address12',
32
+ 'address13',
33
+ 'address14',
34
+ 'address15',
35
+ ] as const;
36
+
37
+ type AddressField = keyof typeof addressFields;
31
38
 
32
39
  export const configSchema: ConfigSchema = {
33
40
  wardPatientCards: {
34
41
  _description: 'Configure the display of ward patient cards',
35
- patientCardElementDefinitions: {
42
+ obsElementDefinitions: {
36
43
  _type: Type.Array,
44
+ _description: 'Defines obs display elements that can be included in the card header or footer.',
37
45
  _default: [],
38
46
  _elements: {
39
47
  id: {
40
48
  _type: Type.String,
41
- _description: 'The unique identifier for this custom patient card element',
49
+ _description: 'The unique identifier for this patient card element',
50
+ },
51
+ conceptUuid: {
52
+ _type: Type.UUID,
53
+ _description: 'Identifies the concept to use to identify the desired observations.',
42
54
  },
43
- elementType: {
55
+ label: {
44
56
  _type: Type.String,
45
- _description: 'The patient card element type',
46
- _validators: [validators.oneOf(patientCardElementTypes)],
57
+ _description:
58
+ "Optional. The custom label or i18n key to the translated label to display. If not provided, defaults to the concept's name. (Note that this can be set to an empty string to not show a label)",
59
+ _default: null,
47
60
  },
48
- config: {
49
- address: {
50
- _description: 'Config for the patientCardElementType "patient-address"',
51
- addressFields: {
52
- _type: Type.Array,
53
- _description: 'defines which address fields to show',
54
- _default: defaultPatientAddressFields,
55
- },
61
+ labelI18nModule: {
62
+ _type: Type.String,
63
+ _description:
64
+ 'Optional. The custom module to use for translation of the label. If not provided, the label will not be translated.',
65
+ _default: null,
66
+ },
67
+ orderBy: {
68
+ _type: Type.String,
69
+ _description:
70
+ "One of 'ascending' or 'descending', specifying whether to display the obs by obsDatetime ascendingly or descendingly.",
71
+ _default: 'descending',
72
+ _validators: [validators.oneOf(['ascending', 'descending'])],
73
+ },
74
+ limit: {
75
+ _type: Type.Number,
76
+ _description:
77
+ 'If set to a number greater than one, this will show multiple obs for this concept, which will appear as a list. Set to 0 for unlimited.',
78
+ _default: 1,
79
+ },
80
+ onlyWithinCurrentVisit: {
81
+ _type: Type.Boolean,
82
+ _description:
83
+ 'Optional. If true, limits display to only observations within current visit. Defaults to false',
84
+ _default: false,
85
+ },
86
+ },
87
+ },
88
+ identifierElementDefinitions: {
89
+ _type: Type.Array,
90
+ _description: `Defines patient identifier elements that can be included in the card header or footer. The default element 'patient-identifier' displays the preferred identifier.`,
91
+ _default: [
92
+ {
93
+ id: 'patient-identifier',
94
+ },
95
+ ],
96
+ _elements: {
97
+ id: {
98
+ _type: Type.String,
99
+ _description: 'The unique identifier for this patient card element',
100
+ },
101
+ identifierTypeUuid: {
102
+ _type: Type.UUID,
103
+ _description:
104
+ 'The UUID of the identifier type to display. If not provided, defaults to the preferred identifier.',
105
+ _default: null,
106
+ },
107
+ label: {
108
+ _type: Type.String,
109
+ _description:
110
+ 'the custom label or i18n key to the translated label to display for patient identifier. If not provided, defaults to the patient-identifier name.',
111
+ _default: null,
112
+ },
113
+ labelI18nModule: {
114
+ _type: Type.String,
115
+ _description: 'Optional. The custom module to use for translation of the label',
116
+ _default: null,
117
+ },
118
+ },
119
+ },
120
+ addressElementDefinitions: {
121
+ _type: Type.Array,
122
+ _description: 'Defines patient address elements that can be included in the card header or footer.',
123
+ _default: [
124
+ {
125
+ id: 'patient-address',
126
+ fields: ['cityVillage', 'country'],
127
+ },
128
+ ],
129
+ _elements: {
130
+ fields: {
131
+ id: {
132
+ _type: Type.String,
133
+ _description: 'The unique identifier for this patient card element',
56
134
  },
57
- obs: {
58
- _description: 'Config for the patientCardElementType "patient-obs"',
59
- conceptUuid: {
60
- _type: Type.UUID,
61
- _description: 'Required. Identifies the concept to use to identify the desired observations.',
62
- _default: null,
63
- },
64
- label: {
65
- _type: Type.String,
66
- _description:
67
- "Optional. The custom label or i18n key to the translated label to display. If not provided, defaults to the concept's name. (Note that this can be set to an empty string to not show a label)",
68
- _default: null,
69
- },
70
- labelI18nModule: {
71
- _type: Type.String,
72
- _description: 'Optional. The custom module to use for translation of the label',
73
- _default: null,
74
- },
75
- orderBy: {
135
+ fields: {
136
+ _type: Type.Array,
137
+ _description: 'The fields of the address to display',
138
+ _elements: {
76
139
  _type: Type.String,
77
- _description:
78
- "Optional. One of 'ascending' or 'descending', specifying whether to display the obs by obsDatetime ascendingly or descendingly. Defaults to ascending.",
79
- _default: 'descending',
80
- _validators: [validators.oneOf(['ascending', 'descending'])],
81
- },
82
- limit: {
83
- _type: Type.Number,
84
- _description: 'Optional. Limits the max number of obs to display. Unlimited by default.',
85
- _default: null,
86
- },
87
- onlyWithinCurrentVisit: {
88
- _type: Type.Boolean,
89
- _description:
90
- 'Optional. If true, limits display to only observations within current visit. Defaults to false',
91
- _default: false,
92
- },
93
- },
94
- codedObsTags: {
95
- _description: 'Config for the patientCardElementType "patient-coded-obs-tags"',
96
- conceptUuid: {
97
- _type: Type.UUID,
98
- _description: 'Required. Identifies the concept to use to identify the desired observations.',
99
- _default: null,
100
- },
101
- summaryLabel: {
102
- _type: Type.String,
103
- _description: `Optional. The custom label or i18n key to the translated label to display for the summary tag. The summary tag shows the count of the number of answers that are present but not configured to show as their own tags. If not provided, defaults to the name of the concept.`,
104
- _default: null,
105
- },
106
- summaryLabelI18nModule: {
107
- _type: Type.String,
108
- _description: 'Optional. The custom module to use for translation of the summary label',
109
- _default: null,
110
- },
111
- summaryLabelColor: {
112
- _type: Type.String,
113
- _description:
114
- 'The color of the summary tag. See https://react.carbondesignsystem.com/?path=/docs/components-tag--overview for a list of supported colors',
115
- _default: null,
116
- },
117
- tags: {
118
- _description: `An array specifying concept sets and color. Observations with coded values that are members of the specified concept sets will be displayed as their own tags with the specified color. Any observation with coded values not belonging to any concept sets specified will be summarized as a count in the summary tag. If a concept set is listed multiple times, the first matching applied-to rule takes precedence.`,
119
- _type: Type.Array,
120
- _elements: {
121
- color: {
122
- _type: Type.String,
123
- _description:
124
- 'Color of the tag. See https://react.carbondesignsystem.com/?path=/docs/components-tag--overview for a list of supported colors.',
125
- },
126
- appliedToConceptSets: {
127
- _type: Type.Array,
128
- _description: `The concept sets which the color applies to. Observations with coded values that are members of the specified concept sets will be displayed as their own tag with the specified color. If an observation's coded value belongs to multiple concept sets, the first matching applied-to rule takes precedence.`,
129
- _elements: {
130
- _type: Type.UUID,
131
- },
132
- },
133
- },
140
+ _validators: [validators.oneOf(addressFields)],
134
141
  },
135
142
  },
136
143
  },
@@ -144,31 +151,36 @@ export const configSchema: ConfigSchema = {
144
151
  _elements: {
145
152
  id: {
146
153
  _type: Type.String,
147
- _description: 'The unique identifier for this card definition. Currently unused, but that might change.',
154
+ _description:
155
+ 'The unique identifier for this card definition. This is used to set the name of the extension slot the card has, where the rows go. The slot name is "ward-patient-card-<id>", unless the id is "default", in which case the slot name is "ward-patient-card".',
156
+ _default: 'default',
148
157
  },
149
- rows: {
158
+ headerRowElements: {
150
159
  _type: Type.Array,
160
+ _description: `IDs of patient card elements to appear in the header row. These can be built-in, or custom ones can be defined in patientCardElementDefinitions. Built-in elements are: '${builtInPatientCardElements.join(
161
+ "', '",
162
+ )}'.`,
151
163
  _elements: {
152
- id: {
153
- _type: Type.String,
154
- _description: 'The unique identifier for this card row. Currently unused, but that might change.',
155
- },
156
- elements: {
157
- _type: Type.Array,
158
- _element: {
159
- _type: Type.String,
160
- _description: 'The ID of the (bulit-in or custom) patient card elements to appear in this card row',
161
- _validators: [validators.oneOf(patientCardElementTypes)],
162
- },
163
- },
164
+ _type: Type.String,
165
+ },
166
+ },
167
+ footerRowElements: {
168
+ _type: Type.Array,
169
+ _description: `IDs of patient card elements to appear in the footer row. These can be built-in, or custom ones can be defined in patientCardElementDefinitions. Built-in elements are: '${builtInPatientCardElements.join(
170
+ "', '",
171
+ )}'.`,
172
+ _elements: {
173
+ _type: Type.String,
164
174
  },
165
175
  },
166
176
  appliedTo: {
167
177
  _type: Type.Array,
178
+ _description:
179
+ 'Conditions under which this card definition should be used. If not provided, the configuration is applied to all wards.',
168
180
  _elements: {
169
181
  location: {
170
182
  _type: Type.UUID,
171
- _description: 'The UUID of the location. If not provided, applies to all queues.',
183
+ _description: 'The UUID of the location. If not provided, applies to all wards.',
172
184
  _default: null,
173
185
  },
174
186
  },
@@ -183,120 +195,42 @@ export interface WardConfigObject {
183
195
  }
184
196
 
185
197
  export interface WardPatientCardsConfig {
186
- patientCardElementDefinitions: Array<PatientCardElementDefinition>;
198
+ obsElementDefinitions: Array<ObsElementDefinition>;
199
+ identifierElementDefinitions: Array<IdentifierElementDefinition>;
200
+ addressElementDefinitions: Array<AddressElementDefinition>;
187
201
  cardDefinitions: Array<WardPatientCardDefinition>;
188
202
  }
189
203
 
190
- export interface WardPatientCardDefinition {
204
+ export interface ObsElementDefinition {
191
205
  id: string;
192
- rows: Array<{
193
- /**
194
- * The type of row. Currently, only "header" is supported
195
- */
196
- rowType: 'header';
197
-
198
- /**
199
- * an array of (either built-in or custom) patient card element ids
200
- */
201
- elements: Array<string>;
202
- }>;
203
- appliedTo?: Array<{
204
- /**
205
- * locationUuid. If given, only applies to patients at the specified ward locations. (If not provided, applies to all locations)
206
- */
207
- location: string;
208
- }>;
206
+ conceptUuid: string;
207
+ onlyWithinCurrentVisit: boolean;
208
+ orderBy: 'ascending' | 'descending';
209
+ limit: number;
210
+ label?: string;
211
+ labelI18nModule?: string;
209
212
  }
210
213
 
211
- export type PatientCardElementDefinition = {
214
+ export interface IdentifierElementDefinition {
212
215
  id: string;
213
- elementType: PatientCardElementType;
214
- config?: PatientCardElementConfig;
215
- };
216
-
217
- export interface PatientAddressElementConfig {
218
- addressFields: Array<keyof PersonAddress>;
219
- }
220
-
221
- export interface PatientObsElementConfig {
222
- /**
223
- * Required. Identifies the concept to use to identify the desired observations.
224
- */
225
- conceptUuid: string;
226
-
227
- /**
228
- * Optional. The custom label or i18n key to the translated label to display. If not provided, defaults to the concept's name.
229
- * (Note that this can be set to an empty string to not show a label)
230
- */
216
+ identifierTypeUuid: string;
231
217
  label?: string;
232
-
233
- /**
234
- * Optional. The custom module to use for translation of the label
235
- */
236
218
  labelI18nModule?: string;
237
-
238
- /**
239
- * Optional. One of 'ascending' or 'descending', specifying whether to display the obs by obsDatetime ascendingly or descendingly. Defaults to descending.
240
- */
241
- orderBy?: 'ascending' | 'descending';
242
-
243
- /**
244
- * Optional. Limits the max number of obs to display. Unlimited by default.
245
- */
246
- limit?: number;
247
-
248
- /**
249
- * Optional. If true, limits display to only observations within current visit
250
- */
251
- onlyWithinCurrentVisit?: boolean;
252
219
  }
253
220
 
254
- export interface PatientCodedObsTagsElementConfig {
255
- /**
256
- * Required. Identifies the concept to use to identify the desired observations.
257
- */
258
- conceptUuid: string;
259
-
260
- /**
261
- * Optional. The custom label or i18n key to the translated label to display for the summary tag. The summary tag
262
- * shows the count of the number of answers that are present but not configured to show as their own tags. If not
263
- * provided, defaults to the name of the concept.
264
- */
265
- summaryLabel?: string;
266
- /**
267
- * Optional. The custom module to use for translation of the summary label
268
- */
269
- summaryLabelI18nModule?: string;
270
-
271
- /**
272
- * The color of the summary tag.
273
- * See https://react.carbondesignsystem.com/?path=/docs/components-tag--overview for a list of supported colors
274
- */
275
- summaryLabelColor?: string;
276
-
277
- /**
278
- * An array specifying concept sets and color. Observations with coded values that are members of the specified concept sets
279
- * will be displayed as their own tags with the specified color. Any observation with coded values not belonging to
280
- * any concept sets specified will be summarized as a count in the summary tag. If a concept set is listed multiple times,
281
- * the first matching applied-to rule takes precedence.
282
- */
283
- tags: Array<{
284
- /**
285
- * Color of the tag. See https://react.carbondesignsystem.com/?path=/docs/components-tag--overview for a list of supported colors.
286
- */
287
- color: string;
221
+ export interface AddressElementDefinition {
222
+ id: string;
223
+ fields: Array<AddressField>;
224
+ }
288
225
 
226
+ export interface WardPatientCardDefinition {
227
+ id: string;
228
+ headerRowElements: Array<string>;
229
+ footerRowElements: Array<string>;
230
+ appliedTo?: Array<{
289
231
  /**
290
- * The concept sets which the color applies to. Observations with coded values that are members of the specified concept sets
291
- * will be displayed as their own tag with the specified color.
292
- * If an observation's coded value belongs to multiple concept sets, the first matching applied-to rule takes precedence.
232
+ * locationUuid. If given, only applies to patients at the specified ward locations. (If not provided, applies to all locations)
293
233
  */
294
- appliedToConceptSets: Array<string>;
234
+ location: string;
295
235
  }>;
296
236
  }
297
-
298
- export type PatientCardElementConfig = {
299
- address: PatientAddressElementConfig;
300
- obs: PatientObsElementConfig;
301
- codedObsTags: PatientCodedObsTagsElementConfig;
302
- };
@@ -0,0 +1,42 @@
1
+ import React, { useMemo } from 'react';
2
+ import classNames from 'classnames';
3
+ import { ConfigurableLink } from '@openmrs/esm-framework';
4
+ import { BrowserRouter, useLocation } from 'react-router-dom';
5
+ import { useTranslation } from 'react-i18next';
6
+
7
+ export interface DashboardLinkConfig {
8
+ name: string;
9
+ title: string;
10
+ }
11
+
12
+ // TODO: extract this out into the esm-framework and all 4 copies of this file in this repo?
13
+
14
+ function DashboardExtension({ dashboardLinkConfig }: { dashboardLinkConfig: DashboardLinkConfig }) {
15
+ //
16
+ const { t } = useTranslation();
17
+ const { name, title } = dashboardLinkConfig;
18
+ const location = useLocation();
19
+ const spaBasePath = `${window.spaBase}/home`;
20
+
21
+ const navLink = useMemo(() => {
22
+ const pathArray = location.pathname.split('/home');
23
+ const lastElement = pathArray[pathArray.length - 1];
24
+ return decodeURIComponent(lastElement);
25
+ }, [location.pathname]);
26
+
27
+ return (
28
+ <ConfigurableLink
29
+ className={classNames('cds--side-nav__link', {
30
+ 'active-left-nav-link': navLink.match(name),
31
+ })}
32
+ to={`${spaBasePath}/${name}`}>
33
+ {t(title)}
34
+ </ConfigurableLink>
35
+ );
36
+ }
37
+
38
+ export const createDashboardLink = (dashboardLinkConfig: DashboardLinkConfig) => () => (
39
+ <BrowserRouter>
40
+ <DashboardExtension dashboardLinkConfig={dashboardLinkConfig} />
41
+ </BrowserRouter>
42
+ );
@@ -1,13 +1,18 @@
1
- import { openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
2
- import useSWR from 'swr';
3
- import { type AdmissionLocation } from '../types/index';
1
+ import { type FetchResponse, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
2
+ import { type AdmissionLocationFetchResponse } from '../types/index';
3
+ import useSWRImmutable from 'swr/immutable';
4
+ import useWardLocation from './useWardLocation';
4
5
 
5
- export function useAdmissionLocation(locationUuid: string, rep: string = 'full') {
6
- const apiUrl = `${restBaseUrl}/admissionLocation/${locationUuid}` + (rep ? `?v=${rep}` : '');
7
- const { data, ...rest } = useSWR<{ data: AdmissionLocation }, Error>(apiUrl, openmrsFetch);
6
+ const requestRep =
7
+ 'custom:(ward,totalBeds,occupiedBeds,bedLayouts:(rowNumber,columnNumber,bedNumber,bedId,bedUuid,status,location,patients:(person:full,identifiers,uuid)))';
8
8
 
9
+ // note "admissionLocation" sn'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)
10
+ export function useAdmissionLocation(rep: string = requestRep) {
11
+ const { location } = useWardLocation();
12
+ const apiUrl = location?.uuid ? `${restBaseUrl}/admissionLocation/${location?.uuid}?v=${rep}` : null;
13
+ const { data, ...rest } = useSWRImmutable<FetchResponse<AdmissionLocationFetchResponse>, Error>(apiUrl, openmrsFetch);
9
14
  return {
10
- admissionLocation: data?.data ?? null,
15
+ admissionLocation: data?.data,
11
16
  ...rest,
12
17
  };
13
18
  }
@@ -0,0 +1,32 @@
1
+ import { useConfig } from '@openmrs/esm-framework';
2
+ import { useMemo } from 'react';
3
+ import { type WardConfigObject, defaultWardPatientCard } from '../config-schema';
4
+ import useWardLocation from './useWardLocation';
5
+
6
+ export function useCurrentWardCardConfig() {
7
+ const { wardPatientCards } = useConfig<WardConfigObject>();
8
+ const {
9
+ location: { uuid: locationUuid },
10
+ } = useWardLocation();
11
+
12
+ const currentWardCardConfig = useMemo(() => {
13
+ const cardDefinition = wardPatientCards.cardDefinitions.find((cardDef) => {
14
+ return (
15
+ cardDef.appliedTo == null ||
16
+ cardDef.appliedTo?.length == 0 ||
17
+ cardDef.appliedTo.some((criteria) => criteria.location == locationUuid)
18
+ );
19
+ });
20
+
21
+ return cardDefinition;
22
+ }, [wardPatientCards, locationUuid]);
23
+
24
+ if (!currentWardCardConfig) {
25
+ console.warn(
26
+ 'No ward card configuration has `appliedTo` criteria that matches the current location. Using the default configuration.',
27
+ );
28
+ return defaultWardPatientCard;
29
+ }
30
+
31
+ return currentWardCardConfig;
32
+ }
@@ -0,0 +1,112 @@
1
+ import { type FetchResponse, openmrsFetch, type OpenmrsResource, restBaseUrl } from '@openmrs/esm-framework';
2
+ import { useMemo } from 'react';
3
+ import useSWRImmutable from 'swr/immutable';
4
+ import type { DispositionType } from '../types';
5
+
6
+ interface LocationTag extends OpenmrsResource {
7
+ name: string;
8
+ }
9
+
10
+ interface EmrApiConfigurationResponse {
11
+ admissionEncounterType: OpenmrsResource;
12
+ clinicianEncounterRole: OpenmrsResource;
13
+ consultFreeTextCommentsConcept: OpenmrsResource;
14
+ visitNoteEncounterType: OpenmrsResource;
15
+ transferWithinHospitalEncounterType: OpenmrsResource;
16
+ supportsTransferLocationTag: LocationTag;
17
+ supportsAdmissionLocationTag: LocationTag;
18
+ supportsLoginLocationTag: LocationTag;
19
+ supportsVisitsLocationTag: LocationTag;
20
+ dispositionDescriptor: {
21
+ admissionLocationConcept: OpenmrsResource;
22
+ dateOfDeathConcept: OpenmrsResource;
23
+ dispositionConcept: OpenmrsResource;
24
+ internalTransferLocationConcept: OpenmrsResource;
25
+ dispositionSetConcept: OpenmrsResource;
26
+ };
27
+ dispositions: Array<{
28
+ encounterTypes: null;
29
+ keepsVisitOpen: null;
30
+ additionalObs: null;
31
+ careSettingTypes: ['OUTPATIENT'];
32
+ name: string;
33
+ conceptCode: string;
34
+ type: DispositionType;
35
+ actions: [];
36
+ excludedEncounterTypes: Array<string>;
37
+ uuid: string;
38
+ }>;
39
+ // There are many more keys to this object, but we only need these for now
40
+ // Add more keys as needed
41
+ }
42
+
43
+ const customRepProps = [
44
+ ['metadataSourceName', 'ref'],
45
+ ['orderingProviderEncounterRole', 'ref'],
46
+ ['supportsTransferLocationTag', '(uuid,display,name,links)'],
47
+ ['unknownLocation', 'ref'],
48
+ ['denyAdmissionConcept', 'ref'],
49
+ ['admissionForm', 'ref'],
50
+ ['exitFromInpatientEncounterType', 'ref'],
51
+ ['extraPatientIdentifierTypes', 'ref'],
52
+ ['consultFreeTextCommentsConcept', 'ref'],
53
+ ['sameAsConceptMapType', 'ref'],
54
+ ['testPatientPersonAttributeType', 'ref'],
55
+ ['admissionDecisionConcept', 'ref'],
56
+ ['supportsAdmissionLocationTag', '(uuid,display,name,links)'],
57
+ ['checkInEncounterType', 'ref'],
58
+ ['transferWithinHospitalEncounterType', 'ref'],
59
+ ['suppressedDiagnosisConcepts', 'ref'],
60
+ ['primaryIdentifierType', 'ref'],
61
+ ['nonDiagnosisConceptSets', 'ref'],
62
+ ['fullPrivilegeLevel', 'ref'],
63
+ ['unknownProvider', 'ref'],
64
+ ['diagnosisSets', 'ref'],
65
+ ['personImageDirectory', 'ref'],
66
+ ['visitNoteEncounterType', 'ref'],
67
+ ['consultEncounterType', 'ref'],
68
+ ['diagnosisMetadata', 'ref'],
69
+ ['narrowerThanConceptMapType', 'ref'],
70
+ ['clinicianEncounterRole', 'ref'],
71
+ ['conceptSourcesForDiagnosisSearch', 'ref'],
72
+ ['patientDiedConcept', 'ref'],
73
+ ['emrApiConceptSource', 'ref'],
74
+ ['lastViewedPatientSizeLimit', 'ref'],
75
+ ['identifierTypesToSearch', 'ref'],
76
+ ['telephoneAttributeType', 'ref'],
77
+ ['checkInClerkEncounterRole', 'ref'],
78
+ ['dischargeForm', 'ref'],
79
+ ['unknownCauseOfDeathConcept', 'ref'],
80
+ ['visitAssignmentHandlerAdjustEncounterTimeOfDayIfNecessary', 'ref'],
81
+ ['atFacilityVisitType', 'ref'],
82
+ ['visitExpireHours', 'ref'],
83
+ ['admissionEncounterType', 'ref'],
84
+ ['motherChildRelationshipType', 'ref'],
85
+ ['dispositions', 'ref'],
86
+ ['dispositionDescriptor', 'ref'],
87
+ ['highPrivilegeLevel', 'ref'],
88
+ ['supportsLoginLocationTag', '(uuid,display,name,links)'],
89
+ ['unknownPatientPersonAttributeType', 'ref'],
90
+ ['supportsVisitsLocationTag', '(uuid,display,name,links)'],
91
+ ['transferForm', 'ref'],
92
+ ];
93
+
94
+ const customRep = `custom:${customRepProps.map((prop) => prop.join(':')).join(',')}`;
95
+
96
+ export default function useEmrConfiguration() {
97
+ const swrData = useSWRImmutable<FetchResponse<EmrApiConfigurationResponse>>(
98
+ `${restBaseUrl}/emrapi/configuration?v=${customRep}`,
99
+ openmrsFetch,
100
+ );
101
+
102
+ const results = useMemo(
103
+ () => ({
104
+ emrConfiguration: swrData.data?.data,
105
+ isLoadingEmrConfiguration: swrData.isLoading,
106
+ mutateEmrConfiguration: swrData.mutate,
107
+ errorFetchingEmrConfiguration: swrData.error,
108
+ }),
109
+ [swrData],
110
+ );
111
+ return results;
112
+ }