@digihmis/esm-patient-registration-app 1.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/README.md +4 -0
- package/dist/117.js +1 -0
- package/dist/117.js.map +1 -0
- package/dist/131.js +1 -0
- package/dist/131.js.map +1 -0
- package/dist/132.js +1 -0
- package/dist/132.js.map +1 -0
- package/dist/152.js +1 -0
- package/dist/152.js.map +1 -0
- package/dist/209.js +1 -0
- package/dist/209.js.map +1 -0
- package/dist/247.js +1 -0
- package/dist/247.js.map +1 -0
- package/dist/28.js +1 -0
- package/dist/28.js.map +1 -0
- package/dist/317.js +1 -0
- package/dist/317.js.map +1 -0
- package/dist/349.js +1 -0
- package/dist/349.js.map +1 -0
- package/dist/371.js +1 -0
- package/dist/371.js.map +1 -0
- package/dist/378.js +1 -0
- package/dist/378.js.map +1 -0
- package/dist/442.js +1 -0
- package/dist/442.js.map +1 -0
- package/dist/45.js +1 -0
- package/dist/45.js.map +1 -0
- package/dist/459.js +12 -0
- package/dist/459.js.map +1 -0
- package/dist/466.js +1 -0
- package/dist/466.js.map +1 -0
- package/dist/508.js +1 -0
- package/dist/508.js.map +1 -0
- package/dist/61.js +1 -0
- package/dist/61.js.map +1 -0
- package/dist/640.js +1 -0
- package/dist/640.js.map +1 -0
- package/dist/689.js +1 -0
- package/dist/689.js.map +1 -0
- package/dist/709.js +1 -0
- package/dist/709.js.map +1 -0
- package/dist/712.js +1 -0
- package/dist/712.js.map +1 -0
- package/dist/720.js +1 -0
- package/dist/720.js.map +1 -0
- package/dist/742.js +1 -0
- package/dist/742.js.map +1 -0
- package/dist/771.js +1 -0
- package/dist/771.js.map +1 -0
- package/dist/806.js +1 -0
- package/dist/806.js.map +1 -0
- package/dist/912.js +1 -0
- package/dist/912.js.map +1 -0
- package/dist/913.js +1 -0
- package/dist/913.js.map +1 -0
- package/dist/940.js +1 -0
- package/dist/940.js.map +1 -0
- package/dist/957.js +15 -0
- package/dist/957.js.map +1 -0
- package/dist/digihmis-esm-patient-registration-app.js +6 -0
- package/dist/digihmis-esm-patient-registration-app.js.buildmanifest.json +827 -0
- package/dist/digihmis-esm-patient-registration-app.js.map +1 -0
- package/dist/main.js +17 -0
- package/dist/main.js.map +1 -0
- package/dist/routes.json +1 -0
- package/jest.config.js +26 -0
- package/package.json +51 -0
- package/rspack.config.js +1 -0
- package/src/active-visit-table/active-visit-table.component.tsx +240 -0
- package/src/active-visit-table/active-visits.resource.ts +338 -0
- package/src/active-visit-table/active-visits.scss +211 -0
- package/src/active-visit-table/empty-data-illustration.component.tsx +39 -0
- package/src/checkin-form/base-visit-type.component.tsx +102 -0
- package/src/checkin-form/base-visit-type.scss +75 -0
- package/src/checkin-form/checkin-form.scss +167 -0
- package/src/checkin-form/checkin-form.workspace.tsx +703 -0
- package/src/checkin-form/hooks/useDefaultFacilityLocation.ts +15 -0
- package/src/checkin-form/hooks/useDefaultVisitLocation.ts +24 -0
- package/src/checkin-form/hooks/useOfflineVisitType.ts +18 -0
- package/src/checkin-form/hooks/usePatientHistory.ts +40 -0
- package/src/checkin-form/hooks/useRecommendedVisitTypes.ts +34 -0
- package/src/checkin-form/hooks/useVisitAttributeType.ts +102 -0
- package/src/checkin-form/location-selector.component.tsx +86 -0
- package/src/checkin-form/recommended-visit-type.component.tsx +32 -0
- package/src/checkin-form/visit-attribute-type.component.tsx +256 -0
- package/src/checkin-form/visit-attribute-type.scss +5 -0
- package/src/checkin-form/visit-date-time.component.tsx +201 -0
- package/src/checkin-form/visit-form.resource.ts +308 -0
- package/src/components/custom-card/compact-patient-banner.component.tsx +70 -0
- package/src/components/custom-card/compact-patient-banner.scss +34 -0
- package/src/components/custom-card/custom-loader.component.tsx +22 -0
- package/src/components/custom-card/patient-search-context.ts +30 -0
- package/src/components/custom-input/autosuggest/autosuggest.component.tsx +213 -0
- package/src/components/custom-input/autosuggest/autosuggest.scss +158 -0
- package/src/components/custom-input/autosuggest/autosuggest.test.tsx +168 -0
- package/src/components/custom-patient-banner/custom-patient-banner.component.tsx +39 -0
- package/src/components/custom-patient-banner/custom-patient-banner.resource.ts +32 -0
- package/src/components/custom-patient-banner/custom-patient-banner.scss +26 -0
- package/src/config-schema.ts +511 -0
- package/src/constant.ts +6 -0
- package/src/constants/index.ts +1 -0
- package/src/dashboard.meta.ts +8 -0
- package/src/declarations.d.ts +6 -0
- package/src/empty-svg/empty-svg.component.tsx +565 -0
- package/src/icon-wrapper/icon-wrapper.extension.tsx +17 -0
- package/src/identifier-workspace/identifier-configure-form.workspace.tsx +278 -0
- package/src/identifier-workspace/identifier-configure.scss +106 -0
- package/src/identifier-workspace/identifier.resource.ts +90 -0
- package/src/index.ts +33 -0
- package/src/left-panel/left-panel.component.tsx +54 -0
- package/src/left-panel/left-panel.scss +13 -0
- package/src/page-header/page-header.component.tsx +35 -0
- package/src/page-header/page-header.scss +76 -0
- package/src/patient-tags/deceased-patient-tag.extension.tsx +41 -0
- package/src/patient-tags/deceased-patient-tag.scss +15 -0
- package/src/patient-tags/patient-tags.resource.ts +30 -0
- package/src/patient-tags/visit-tag.extension.tsx +43 -0
- package/src/patient-tags/visit-tag.scss +19 -0
- package/src/registration-form/form-manager.ts +449 -0
- package/src/registration-form/registration-form.component.tsx +1565 -0
- package/src/registration-form/registration-form.resource.ts +264 -0
- package/src/registration-form/registration-form.scss +282 -0
- package/src/routes.json +62 -0
- package/src/search-bar/search-bar.component.tsx +285 -0
- package/src/search-bar/search-bar.resource.ts +274 -0
- package/src/search-bar/search-bar.scss +427 -0
- package/src/search-dashboard/search-dashboard.component.tsx +17 -0
- package/src/search-dashboard/search-dashboard.scss +13 -0
- package/src/setup-tests.ts +1 -0
- package/src/type/index.ts +463 -0
- package/src/utils/fhir-mapper.ts +73 -0
- package/src/visit-summary/visit-summary.component.tsx +32 -0
- package/src/visit-summary/visit-summary.resource.ts +56 -0
- package/src/visit-summary/visit-summary.scss +74 -0
- package/translations/am.json +112 -0
- package/translations/ar.json +120 -0
- package/translations/en.json +112 -0
- package/translations/es.json +114 -0
- package/translations/fr.json +114 -0
- package/translations/he.json +114 -0
- package/translations/km.json +111 -0
- package/translations/pt.json +114 -0
- package/translations/sw.json +112 -0
- package/translations/vi.json +111 -0
- package/translations/zh.json +111 -0
- package/tsconfig.json +5 -0
|
@@ -0,0 +1,463 @@
|
|
|
1
|
+
import { type Visit, type OpenmrsResource, type Session } from '@openmrs/esm-framework';
|
|
2
|
+
import { type RegistrationConfig } from '../config-schema';
|
|
3
|
+
import { SavePatientTransactionManager } from '../registration-form/form-manager';
|
|
4
|
+
|
|
5
|
+
interface NameValue {
|
|
6
|
+
uuid: string;
|
|
7
|
+
preferred: boolean;
|
|
8
|
+
givenName: string;
|
|
9
|
+
middleName: string;
|
|
10
|
+
familyName: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface AttributeValue {
|
|
14
|
+
attributeType: string;
|
|
15
|
+
value: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Patient Identifier data as it is fetched and composed from the APIs.
|
|
20
|
+
*/
|
|
21
|
+
export interface FetchedPatientIdentifierType {
|
|
22
|
+
name: string;
|
|
23
|
+
required: boolean;
|
|
24
|
+
uuid: string;
|
|
25
|
+
fieldName: string;
|
|
26
|
+
format: string;
|
|
27
|
+
formatDescription?: string;
|
|
28
|
+
isPrimary: boolean;
|
|
29
|
+
/** See: https://github.com/openmrs/openmrs-core/blob/e3fb1ac0a052aeff0f957a150731757dd319693b/api/src/main/java/org/openmrs/PatientIdentifierType.java#L41 */
|
|
30
|
+
uniquenessBehavior: undefined | null | 'UNIQUE' | 'NON_UNIQUE' | 'LOCATION';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface PatientIdentifierValue {
|
|
34
|
+
identifierUuid?: string;
|
|
35
|
+
identifierTypeUuid: string;
|
|
36
|
+
initialValue: string;
|
|
37
|
+
identifierValue: string;
|
|
38
|
+
identifierName: string;
|
|
39
|
+
selectedSource: IdentifierSource;
|
|
40
|
+
autoGeneration?: boolean;
|
|
41
|
+
preferred: boolean;
|
|
42
|
+
required: boolean;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Extends the `FetchedPatientIdentifierType` with aggregated data.
|
|
47
|
+
*/
|
|
48
|
+
export interface PatientIdentifierType extends FetchedPatientIdentifierType {
|
|
49
|
+
identifierSources: Array<IdentifierSource>;
|
|
50
|
+
autoGenerationSource?: IdentifierSource;
|
|
51
|
+
checked?: boolean;
|
|
52
|
+
source?: IdentifierSource;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export interface IdentifierSource {
|
|
56
|
+
uuid: string;
|
|
57
|
+
name: string;
|
|
58
|
+
autoGenerationOption?: IdentifierSourceAutoGenerationOption;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export interface IdentifierSourceAutoGenerationOption {
|
|
62
|
+
manualEntryEnabled: boolean;
|
|
63
|
+
automaticGenerationEnabled: boolean;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface PatientIdentifier {
|
|
67
|
+
uuid?: string;
|
|
68
|
+
identifier: string;
|
|
69
|
+
identifierType?: string;
|
|
70
|
+
location?: string;
|
|
71
|
+
preferred?: boolean;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface PatientRegistration {
|
|
75
|
+
id?: number;
|
|
76
|
+
/**
|
|
77
|
+
* The preliminary patient in the FHIR format.
|
|
78
|
+
*/
|
|
79
|
+
fhirPatient: fhir.Patient;
|
|
80
|
+
/**
|
|
81
|
+
* Internal data collected by patient-registration. Required for later syncing and editing.
|
|
82
|
+
* Not supposed to be used outside of this module.
|
|
83
|
+
*/
|
|
84
|
+
_patientRegistrationData: {
|
|
85
|
+
isNewPatient: boolean;
|
|
86
|
+
formValues: FormValues;
|
|
87
|
+
patientUuidMap: PatientUuidMapType;
|
|
88
|
+
initialAddressFieldValues: Record<string, any>;
|
|
89
|
+
capturePhotoProps: CapturePhotoProps;
|
|
90
|
+
currentLocation: string;
|
|
91
|
+
initialIdentifierValues: FormValues['identifiers'];
|
|
92
|
+
currentUser: Session;
|
|
93
|
+
config: RegistrationConfig;
|
|
94
|
+
savePatientTransactionManager: SavePatientTransactionManager;
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export type Relationship = {
|
|
99
|
+
relationshipType: string;
|
|
100
|
+
personA: string;
|
|
101
|
+
personB: string;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
export type Patient = {
|
|
105
|
+
uuid: string;
|
|
106
|
+
identifiers: Array<PatientIdentifier>;
|
|
107
|
+
person: {
|
|
108
|
+
uuid: string;
|
|
109
|
+
names: Array<NameValue>;
|
|
110
|
+
gender: string;
|
|
111
|
+
birthdate: string;
|
|
112
|
+
birthdateEstimated: boolean;
|
|
113
|
+
attributes: Array<AttributeValue>;
|
|
114
|
+
addresses: Array<Record<string, string>>;
|
|
115
|
+
dead: boolean;
|
|
116
|
+
deathDate?: string;
|
|
117
|
+
causeOfDeath?: string;
|
|
118
|
+
};
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
export interface Encounter {
|
|
122
|
+
encounterDatetime?: Date;
|
|
123
|
+
patient: string;
|
|
124
|
+
encounterType: string;
|
|
125
|
+
location: string;
|
|
126
|
+
encounterProviders: Array<{
|
|
127
|
+
provider: string;
|
|
128
|
+
encounterRole: string;
|
|
129
|
+
}>;
|
|
130
|
+
form: string;
|
|
131
|
+
obs: Array<{
|
|
132
|
+
concept: string | OpenmrsResource;
|
|
133
|
+
value: string | number | OpenmrsResource;
|
|
134
|
+
}>;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export interface RelationshipValue {
|
|
138
|
+
relatedPersonName?: string;
|
|
139
|
+
relatedPersonUuid: string;
|
|
140
|
+
relation?: string;
|
|
141
|
+
relationshipType: string;
|
|
142
|
+
/**
|
|
143
|
+
* Defines the action to be taken on the existing relationship
|
|
144
|
+
* @kind ADD -> adds a new relationship
|
|
145
|
+
* @kind UPDATE -> updates an existing relationship
|
|
146
|
+
* @kind DELETE -> deletes an existing relationship
|
|
147
|
+
* @kind undefined -> no operation on the existing relationship
|
|
148
|
+
*/
|
|
149
|
+
action?: 'ADD' | 'UPDATE' | 'DELETE';
|
|
150
|
+
/**
|
|
151
|
+
* Value kept for restoring initial relationshipType value
|
|
152
|
+
*/
|
|
153
|
+
initialrelationshipTypeValue?: string;
|
|
154
|
+
uuid?: string;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export interface FormValues {
|
|
158
|
+
additionalFamilyName: string;
|
|
159
|
+
additionalGivenName: string;
|
|
160
|
+
additionalMiddleName: string;
|
|
161
|
+
addNameInLocalLanguage: boolean;
|
|
162
|
+
address: {
|
|
163
|
+
[addressField: string]: string;
|
|
164
|
+
};
|
|
165
|
+
attributes?: {
|
|
166
|
+
[attributeTypeUuid: string]: string;
|
|
167
|
+
};
|
|
168
|
+
birthdate: Date | string;
|
|
169
|
+
birthdateEstimated: boolean;
|
|
170
|
+
deathCause: string;
|
|
171
|
+
deathDate: string | Date;
|
|
172
|
+
deathTime: string;
|
|
173
|
+
deathTimeFormat: 'AM' | 'PM';
|
|
174
|
+
familyName: string;
|
|
175
|
+
gender: string;
|
|
176
|
+
givenName: string;
|
|
177
|
+
identifiers: {
|
|
178
|
+
[identifierFieldName: string]: PatientIdentifierValue;
|
|
179
|
+
};
|
|
180
|
+
isDead: boolean;
|
|
181
|
+
middleName: string;
|
|
182
|
+
monthsEstimated: number;
|
|
183
|
+
nonCodedCauseOfDeath: string;
|
|
184
|
+
obs?: {
|
|
185
|
+
[conceptUuid: string]: string;
|
|
186
|
+
};
|
|
187
|
+
patientUuid: string;
|
|
188
|
+
relationships: Array<RelationshipValue>;
|
|
189
|
+
telephoneNumber: string;
|
|
190
|
+
yearsEstimated: number;
|
|
191
|
+
observation?: ObsResponse;
|
|
192
|
+
concepts?: Array<ConceptAnswers>;
|
|
193
|
+
token?: string;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
export interface PatientUuidMapType {
|
|
197
|
+
additionalNameUuid?: string;
|
|
198
|
+
preferredNameUuid?: string;
|
|
199
|
+
preferredAddressUuid?: string;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export interface CapturePhotoProps {
|
|
203
|
+
imageData: string;
|
|
204
|
+
dateTime: string;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export interface AddressValidationSchemaType {
|
|
208
|
+
name: string;
|
|
209
|
+
label: string;
|
|
210
|
+
regex: RegExp;
|
|
211
|
+
regexFormat: string;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export interface CodedPersonAttributeConfig {
|
|
215
|
+
personAttributeUuid: string;
|
|
216
|
+
conceptUuid: string;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export interface TextBasedPersonAttributeConfig {
|
|
220
|
+
personAttributeUuid: string;
|
|
221
|
+
validationRegex: string;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export interface PatientIdentifierResponse {
|
|
225
|
+
uuid: string;
|
|
226
|
+
identifier: string;
|
|
227
|
+
preferred: boolean;
|
|
228
|
+
identifierType: {
|
|
229
|
+
uuid: string;
|
|
230
|
+
required: boolean;
|
|
231
|
+
name: string;
|
|
232
|
+
};
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export interface PersonAttributeTypeResponse {
|
|
236
|
+
uuid: string;
|
|
237
|
+
display: string;
|
|
238
|
+
name: string;
|
|
239
|
+
description: string;
|
|
240
|
+
format: string;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
export interface PersonAttributeResponse {
|
|
244
|
+
display: string;
|
|
245
|
+
uuid: string;
|
|
246
|
+
value:
|
|
247
|
+
| string
|
|
248
|
+
| {
|
|
249
|
+
uuid: string;
|
|
250
|
+
display: string;
|
|
251
|
+
};
|
|
252
|
+
attributeType: {
|
|
253
|
+
display: string;
|
|
254
|
+
uuid: string;
|
|
255
|
+
format: 'org.openmrs.Concept' | string;
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
export interface ConceptResponse {
|
|
260
|
+
uuid: string;
|
|
261
|
+
display: string;
|
|
262
|
+
datatype: {
|
|
263
|
+
uuid: string;
|
|
264
|
+
display: string;
|
|
265
|
+
};
|
|
266
|
+
answers: Array<ConceptAnswers>;
|
|
267
|
+
setMembers: Array<ConceptAnswers>;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export interface ConceptAnswers {
|
|
271
|
+
display: string;
|
|
272
|
+
uuid: string;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export type AddressProperties =
|
|
276
|
+
| 'cityVillage'
|
|
277
|
+
| 'stateProvince'
|
|
278
|
+
| 'countyDistrict'
|
|
279
|
+
| 'postalCode'
|
|
280
|
+
| 'country'
|
|
281
|
+
| 'address1'
|
|
282
|
+
| 'address2'
|
|
283
|
+
| 'address3'
|
|
284
|
+
| 'address4'
|
|
285
|
+
| 'address5'
|
|
286
|
+
| 'address6'
|
|
287
|
+
| 'address7'
|
|
288
|
+
| 'address8'
|
|
289
|
+
| 'address9'
|
|
290
|
+
| 'address10'
|
|
291
|
+
| 'address11'
|
|
292
|
+
| 'address12'
|
|
293
|
+
| 'address13'
|
|
294
|
+
| 'address14'
|
|
295
|
+
| 'address15';
|
|
296
|
+
|
|
297
|
+
export type ExtensibleAddressProperties = { [p in AddressProperties]?: string } | null;
|
|
298
|
+
|
|
299
|
+
export interface AddressTemplate {
|
|
300
|
+
displayName: string | null;
|
|
301
|
+
codeName: string | null;
|
|
302
|
+
country: string | null;
|
|
303
|
+
lines: Array<
|
|
304
|
+
Array<{
|
|
305
|
+
isToken: 'IS_NOT_ADDR_TOKEN' | 'IS_ADDR_TOKEN';
|
|
306
|
+
displayText: string;
|
|
307
|
+
codeName?: AddressProperties;
|
|
308
|
+
displaySize?: string;
|
|
309
|
+
}>
|
|
310
|
+
> | null;
|
|
311
|
+
lineByLineFormat: Array<string> | null;
|
|
312
|
+
nameMappings: ExtensibleAddressProperties;
|
|
313
|
+
sizeMappings: ExtensibleAddressProperties;
|
|
314
|
+
elementDefaults: ExtensibleAddressProperties;
|
|
315
|
+
elementRegex: ExtensibleAddressProperties;
|
|
316
|
+
elementRegexFormats: ExtensibleAddressProperties;
|
|
317
|
+
requiredElements: Array<AddressProperties> | null;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// https://rest.openmrs.org/#address-template
|
|
321
|
+
export interface RestAddressTemplate {
|
|
322
|
+
uuid: string;
|
|
323
|
+
description: string;
|
|
324
|
+
property: string;
|
|
325
|
+
display: string;
|
|
326
|
+
value: string;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
export interface ObsResponse {
|
|
330
|
+
results: Array<{ obs: Array<{ uuid: string; display: string; value: OpenmrsResource; concept: OpenmrsResource }> }>;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
export interface SearchedPatient {
|
|
334
|
+
uuid: string;
|
|
335
|
+
identifiers: Array<Identifier>;
|
|
336
|
+
person: {
|
|
337
|
+
addresses: Array<Address>;
|
|
338
|
+
age: number;
|
|
339
|
+
birthdate: string;
|
|
340
|
+
gender: string;
|
|
341
|
+
dead: boolean;
|
|
342
|
+
deathDate: string | null;
|
|
343
|
+
personName: {
|
|
344
|
+
display: string;
|
|
345
|
+
givenName: string;
|
|
346
|
+
familyName: string;
|
|
347
|
+
middleName: string;
|
|
348
|
+
};
|
|
349
|
+
};
|
|
350
|
+
attributes: Array<{
|
|
351
|
+
value: OpenmrsResource | string;
|
|
352
|
+
attributeType: { uuid: string; display: string };
|
|
353
|
+
}>;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export interface Identifier {
|
|
357
|
+
display: string;
|
|
358
|
+
identifier: string;
|
|
359
|
+
identifierType: OpenmrsResource;
|
|
360
|
+
location: OpenmrsResource;
|
|
361
|
+
uuid: string;
|
|
362
|
+
preferred: boolean;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
export interface Address {
|
|
366
|
+
preferred: boolean;
|
|
367
|
+
voided: boolean;
|
|
368
|
+
address1: string;
|
|
369
|
+
cityVillage: string;
|
|
370
|
+
country: string;
|
|
371
|
+
postalCode: string;
|
|
372
|
+
stateProvince: string;
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
export interface User {
|
|
376
|
+
uuid: string;
|
|
377
|
+
userProperties: {
|
|
378
|
+
[x: string]: string;
|
|
379
|
+
patientsVisited: string;
|
|
380
|
+
defaultLocation: string;
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
export interface PatientSearchResponse {
|
|
385
|
+
currentPage: number;
|
|
386
|
+
data?: Array<SearchedPatient>;
|
|
387
|
+
fetchError: Error;
|
|
388
|
+
hasMore: boolean;
|
|
389
|
+
isLoading: boolean;
|
|
390
|
+
isValidating: boolean;
|
|
391
|
+
setPage: (page: number | ((_page: number) => number)) => Promise<unknown[] | undefined>;
|
|
392
|
+
totalResults: number;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
export interface Resources {
|
|
396
|
+
addressTemplate: AddressTemplate;
|
|
397
|
+
currentSession: Session;
|
|
398
|
+
relationshipTypes: any;
|
|
399
|
+
identifierTypes: Array<PatientIdentifierType>;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
export interface OpenMRSPatient {
|
|
403
|
+
person: {
|
|
404
|
+
personName: {
|
|
405
|
+
display: string;
|
|
406
|
+
};
|
|
407
|
+
gender: string;
|
|
408
|
+
dead: boolean;
|
|
409
|
+
birthdate: string;
|
|
410
|
+
};
|
|
411
|
+
patientIdentifier: {
|
|
412
|
+
identifier: string;
|
|
413
|
+
identifierType: {
|
|
414
|
+
display: string;
|
|
415
|
+
};
|
|
416
|
+
};
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
export interface ActiveVisit {
|
|
420
|
+
age: string;
|
|
421
|
+
id: string;
|
|
422
|
+
idNumber: string;
|
|
423
|
+
gender: string;
|
|
424
|
+
location: string;
|
|
425
|
+
name: string;
|
|
426
|
+
patientUuid: string;
|
|
427
|
+
visitStartTime: string;
|
|
428
|
+
visitType: string;
|
|
429
|
+
visitUuid: string;
|
|
430
|
+
observations?: Record<string, Observation[]>;
|
|
431
|
+
[identifier: string]: string | Record<string, Observation[]>;
|
|
432
|
+
}
|
|
433
|
+
export interface Observation {
|
|
434
|
+
uuid: string;
|
|
435
|
+
concept: {
|
|
436
|
+
uuid: string;
|
|
437
|
+
display: string;
|
|
438
|
+
conceptClass: {
|
|
439
|
+
uuid: string;
|
|
440
|
+
display: string;
|
|
441
|
+
};
|
|
442
|
+
};
|
|
443
|
+
display: string;
|
|
444
|
+
groupMembers: null | Array<{
|
|
445
|
+
uuid: string;
|
|
446
|
+
concept: {
|
|
447
|
+
uuid: string;
|
|
448
|
+
display: string;
|
|
449
|
+
};
|
|
450
|
+
value: {
|
|
451
|
+
uuid: string;
|
|
452
|
+
display: string;
|
|
453
|
+
};
|
|
454
|
+
}>;
|
|
455
|
+
value: any;
|
|
456
|
+
obsDatetime: string;
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
export interface VisitResponse {
|
|
460
|
+
results: Array<Visit>;
|
|
461
|
+
links: Array<{ rel: 'prev' | 'next' }>;
|
|
462
|
+
totalCount: number;
|
|
463
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
2
|
+
import { SearchedPatient } from '../type';
|
|
3
|
+
|
|
4
|
+
const getGender = (gender: string) => {
|
|
5
|
+
switch (gender) {
|
|
6
|
+
case 'M':
|
|
7
|
+
return 'male';
|
|
8
|
+
case 'F':
|
|
9
|
+
return 'female';
|
|
10
|
+
case 'O':
|
|
11
|
+
return 'other';
|
|
12
|
+
case 'U':
|
|
13
|
+
return 'unknown';
|
|
14
|
+
default:
|
|
15
|
+
return gender;
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export function mapToFhirPatient(patient: SearchedPatient) {
|
|
20
|
+
const preferredAddress = patient?.person?.addresses?.find((address) => address.preferred);
|
|
21
|
+
const addressId = uuidv4();
|
|
22
|
+
const nameId = uuidv4();
|
|
23
|
+
|
|
24
|
+
return {
|
|
25
|
+
resourceType: 'Patient',
|
|
26
|
+
address: preferredAddress
|
|
27
|
+
? [
|
|
28
|
+
{
|
|
29
|
+
id: addressId,
|
|
30
|
+
city: preferredAddress.cityVillage,
|
|
31
|
+
country: preferredAddress.country,
|
|
32
|
+
postalCode: preferredAddress.postalCode,
|
|
33
|
+
state: preferredAddress.stateProvince,
|
|
34
|
+
use: 'home',
|
|
35
|
+
},
|
|
36
|
+
]
|
|
37
|
+
: [],
|
|
38
|
+
birthDate: patient?.person?.birthdate,
|
|
39
|
+
deceasedBoolean: patient?.person?.dead,
|
|
40
|
+
deceasedDateTime: patient?.person?.deathDate ?? undefined,
|
|
41
|
+
gender: getGender(patient?.person?.gender),
|
|
42
|
+
id: patient?.uuid,
|
|
43
|
+
identifier: patient?.identifiers?.map((identifier) => ({
|
|
44
|
+
id: identifier?.uuid,
|
|
45
|
+
type: {
|
|
46
|
+
coding: [
|
|
47
|
+
{
|
|
48
|
+
code: identifier.identifierType.uuid,
|
|
49
|
+
},
|
|
50
|
+
],
|
|
51
|
+
text: identifier.identifierType.display,
|
|
52
|
+
},
|
|
53
|
+
use: 'official',
|
|
54
|
+
value: identifier.identifier,
|
|
55
|
+
})),
|
|
56
|
+
name: [
|
|
57
|
+
{
|
|
58
|
+
id: nameId,
|
|
59
|
+
given: [patient?.person?.personName.givenName, patient.person?.personName?.middleName],
|
|
60
|
+
family: patient?.person?.personName.familyName,
|
|
61
|
+
text: patient?.person?.personName.display,
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
telecom:
|
|
65
|
+
patient.attributes
|
|
66
|
+
?.filter((attribute) => attribute?.attributeType.display === 'Telephone Number')
|
|
67
|
+
?.map((phone) => ({
|
|
68
|
+
system: 'phone',
|
|
69
|
+
value: phone.value.toString(),
|
|
70
|
+
use: 'mobile',
|
|
71
|
+
})) ?? [],
|
|
72
|
+
};
|
|
73
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { Tile } from '@carbon/react';
|
|
3
|
+
import { useTranslation } from 'react-i18next';
|
|
4
|
+
import styles from './visit-summary.scss';
|
|
5
|
+
import useActiveVisits, { useAppointmentsData, useTotalVisits } from './visit-summary.resource';
|
|
6
|
+
|
|
7
|
+
const VisitSummaryTile: React.FC = () => {
|
|
8
|
+
const { count } = useActiveVisits();
|
|
9
|
+
const { data: visitsData } = useTotalVisits();
|
|
10
|
+
const { data: appointmentsData } = useAppointmentsData();
|
|
11
|
+
|
|
12
|
+
const { t } = useTranslation();
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<Tile className={styles.tileContainer}>
|
|
16
|
+
<div className={styles.displayDetails}>
|
|
17
|
+
<div className={styles.countLabel}>{t('activeVisits', 'Active Visits')}</div>
|
|
18
|
+
<div className={styles.displayData}>{count ?? 0}</div>
|
|
19
|
+
</div>
|
|
20
|
+
<div className={styles.displayDetails}>
|
|
21
|
+
<div className={styles.countLabel}>{t('totalVisits', 'Total Visits Today')}</div>
|
|
22
|
+
<div className={styles.displayData}>{visitsData?.length ?? 0}</div>
|
|
23
|
+
</div>
|
|
24
|
+
<div className={styles.displayDetails}>
|
|
25
|
+
<div className={styles.countLabel}>{t('scheduledForToday', 'Scheduled For Today')}</div>
|
|
26
|
+
<div className={styles.displayData}>{appointmentsData?.length ?? 0}</div>
|
|
27
|
+
</div>
|
|
28
|
+
</Tile>
|
|
29
|
+
);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default VisitSummaryTile;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import useSWR from 'swr';
|
|
2
|
+
import { openmrsFetch, restBaseUrl, useSession, Visit } from '@openmrs/esm-framework';
|
|
3
|
+
import dayjs from 'dayjs';
|
|
4
|
+
|
|
5
|
+
export default function useActiveVisits() {
|
|
6
|
+
const session = useSession();
|
|
7
|
+
const sessionLocation = session?.sessionLocation?.uuid;
|
|
8
|
+
|
|
9
|
+
const customRepresentation = 'custom:(uuid,startDatetime,stopDatetime)';
|
|
10
|
+
|
|
11
|
+
const getUrl = () => {
|
|
12
|
+
let url = `${restBaseUrl}/visit?v=${customRepresentation}&`;
|
|
13
|
+
let urlSearchParams = new URLSearchParams();
|
|
14
|
+
|
|
15
|
+
urlSearchParams.append('includeParentLocations', 'true');
|
|
16
|
+
urlSearchParams.append('includeInactive', 'false');
|
|
17
|
+
urlSearchParams.append('totalCount', 'true');
|
|
18
|
+
urlSearchParams.append('location', `${sessionLocation}`);
|
|
19
|
+
|
|
20
|
+
return url + urlSearchParams.toString();
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const { data, error, isLoading } = useSWR<{ data: { totalCount: number } }>(getUrl, openmrsFetch);
|
|
24
|
+
|
|
25
|
+
return {
|
|
26
|
+
count: data?.data?.totalCount,
|
|
27
|
+
error,
|
|
28
|
+
isLoading,
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const useTotalVisits = () => {
|
|
33
|
+
const customRepresentation = 'custom:(uuid,startDatetime,stopDatetime)';
|
|
34
|
+
|
|
35
|
+
const visitsUrl = `${restBaseUrl}/visit?includeInactive=true&v=${customRepresentation}&fromStartDate=${dayjs().format(
|
|
36
|
+
'YYYY-MM-DD',
|
|
37
|
+
)}`;
|
|
38
|
+
|
|
39
|
+
const { data, error, isLoading } = useSWR<{ data: { results: Array<Visit> } }>(visitsUrl, openmrsFetch);
|
|
40
|
+
|
|
41
|
+
return {
|
|
42
|
+
data: data?.data?.results,
|
|
43
|
+
error,
|
|
44
|
+
isLoading,
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
export const useAppointmentsData = () => {
|
|
49
|
+
const appointmentDate = dayjs(new Date().setHours(0, 0, 0, 0)).toISOString();
|
|
50
|
+
|
|
51
|
+
const url = `${restBaseUrl}/appointment/all?forDate=${appointmentDate}`;
|
|
52
|
+
|
|
53
|
+
const { data, error, isLoading } = useSWR<{ data: Array<any> }>(url, openmrsFetch);
|
|
54
|
+
|
|
55
|
+
return { data: data?.data, error, isLoading };
|
|
56
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
@use '@carbon/layout';
|
|
2
|
+
@use '@carbon/type';
|
|
3
|
+
@use '@carbon/colors';
|
|
4
|
+
@use '@openmrs/esm-styleguide/src/vars' as *;
|
|
5
|
+
|
|
6
|
+
.tileContainer {
|
|
7
|
+
border: 1px solid colors.$cool-gray-30;
|
|
8
|
+
margin-bottom: layout.$spacing-04;
|
|
9
|
+
display: grid;
|
|
10
|
+
grid-template-columns: 1fr 1fr 1fr;
|
|
11
|
+
gap: 0;
|
|
12
|
+
height: 80px;
|
|
13
|
+
|
|
14
|
+
:global(.omrs-breakpoint-lt-desktop) & {
|
|
15
|
+
padding: layout.$spacing-04;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
:global(.omrs-breakpoint-lt-tablet) & {
|
|
19
|
+
padding: layout.$spacing-03;
|
|
20
|
+
grid-template-columns: 1fr;
|
|
21
|
+
grid-template-rows: auto auto auto;
|
|
22
|
+
gap: layout.$spacing-04;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
.tileHeader {
|
|
27
|
+
color: colors.$gray-70;
|
|
28
|
+
margin-bottom: layout.$spacing-03;
|
|
29
|
+
text-transform: none;
|
|
30
|
+
letter-spacing: 0;
|
|
31
|
+
|
|
32
|
+
:global(.omrs-breakpoint-lt-tablet) & {
|
|
33
|
+
margin-bottom: layout.$spacing-02;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
.displayDetails {
|
|
38
|
+
display: flex;
|
|
39
|
+
flex-direction: column;
|
|
40
|
+
align-items: flex-start;
|
|
41
|
+
|
|
42
|
+
:global(.omrs-breakpoint-lt-tablet) & {
|
|
43
|
+
padding: layout.$spacing-03 0;
|
|
44
|
+
border-right: none;
|
|
45
|
+
border-bottom: 1px solid colors.$gray-20;
|
|
46
|
+
padding-bottom: layout.$spacing-04;
|
|
47
|
+
margin-bottom: layout.$spacing-03;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
.countLabel {
|
|
52
|
+
color: colors.$gray-70;
|
|
53
|
+
margin-bottom: layout.$spacing-02;
|
|
54
|
+
@include type.type-style('caption-01');
|
|
55
|
+
font-weight: bold;
|
|
56
|
+
|
|
57
|
+
:global(.omrs-breakpoint-lt-tablet) & {
|
|
58
|
+
@include type.type-style('caption-01');
|
|
59
|
+
font-weight: bold;
|
|
60
|
+
margin-bottom: layout.$spacing-01;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
.displayData {
|
|
65
|
+
color: colors.$gray-100;
|
|
66
|
+
font-weight: bold;
|
|
67
|
+
@include type.type-style('caption-01');
|
|
68
|
+
font-size: 25px;
|
|
69
|
+
|
|
70
|
+
:global(.omrs-breakpoint-lt-tablet) & {
|
|
71
|
+
font-size: 2.625rem;
|
|
72
|
+
line-height: 2.75rem;
|
|
73
|
+
}
|
|
74
|
+
}
|