@olaboot/esm-patient-registration-app 9.2.1 → 10.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/1339.js +1 -0
- package/dist/1339.js.map +1 -0
- package/dist/1480.js +1 -0
- package/dist/1480.js.map +1 -0
- package/dist/1646.js +1 -0
- package/dist/1646.js.map +1 -0
- package/dist/1789.js +1 -0
- package/dist/1789.js.map +1 -0
- package/dist/1869.js +1 -0
- package/dist/1869.js.map +1 -0
- package/dist/1877.js +1 -0
- package/dist/1877.js.map +1 -0
- package/dist/2317.js +1 -0
- package/dist/2317.js.map +1 -0
- package/dist/2416.js +1 -0
- package/dist/2416.js.map +1 -0
- package/dist/2747.js +1 -0
- package/dist/2747.js.map +1 -0
- package/dist/282.js +1 -0
- package/dist/282.js.map +1 -0
- package/dist/2881.js +1 -0
- package/dist/2881.js.map +1 -0
- package/dist/3378.js +1 -0
- package/dist/3378.js.map +1 -0
- package/dist/3720.js +1 -0
- package/dist/3720.js.map +1 -0
- package/dist/3906.js +1 -0
- package/dist/3906.js.map +1 -0
- package/dist/3963.js +1 -0
- package/dist/3963.js.map +1 -0
- package/dist/3989.js +1 -0
- package/dist/3989.js.map +1 -0
- package/dist/4106.js +1 -0
- package/dist/4106.js.map +1 -0
- package/dist/4111.js +1 -0
- package/dist/4111.js.map +1 -0
- package/dist/434.js +1 -0
- package/dist/434.js.map +1 -0
- package/dist/4348.js +1 -0
- package/dist/4348.js.map +1 -0
- package/dist/4383.js +1 -0
- package/dist/4383.js.map +1 -0
- package/dist/4658.js +1 -0
- package/dist/4658.js.map +1 -0
- package/dist/466.js +1 -0
- package/dist/466.js.map +1 -0
- package/dist/4928.js +1 -0
- package/dist/4928.js.map +1 -0
- package/dist/5117.js +1 -0
- package/dist/5117.js.map +1 -0
- package/dist/5132.js +1 -0
- package/dist/5132.js.map +1 -0
- package/dist/5145.js +1 -0
- package/dist/5145.js.map +1 -0
- package/dist/5208.js +43 -0
- package/dist/5208.js.map +1 -0
- package/dist/527.js +1 -0
- package/dist/527.js.map +1 -0
- package/dist/5280.js +1 -0
- package/dist/5280.js.map +1 -0
- package/dist/5338.js +6 -0
- package/dist/5338.js.map +1 -0
- package/dist/5503.js +1 -0
- package/dist/5503.js.map +1 -0
- package/dist/555.js +1 -0
- package/dist/555.js.map +1 -0
- package/dist/556.js +1 -0
- package/dist/556.js.map +1 -0
- package/dist/5644.js +1 -0
- package/dist/5644.js.map +1 -0
- package/dist/5697.js +1 -0
- package/dist/{4024.js.map → 5697.js.map} +1 -1
- package/dist/5940.js +1 -0
- package/dist/5940.js.map +1 -0
- package/dist/6047.js +1 -0
- package/dist/6047.js.map +1 -0
- package/dist/6371.js +1 -0
- package/dist/6371.js.map +1 -0
- package/dist/6377.js +1 -0
- package/dist/6377.js.map +1 -0
- package/dist/6388.js +1 -0
- package/dist/6388.js.map +1 -0
- package/dist/6444.js +1 -0
- package/dist/6444.js.map +1 -0
- package/dist/6508.js +1 -0
- package/dist/6508.js.map +1 -0
- package/dist/6724.js +1 -0
- package/dist/6724.js.map +1 -0
- package/dist/689.js +1 -0
- package/dist/689.js.map +1 -0
- package/dist/6904.js +1 -0
- package/dist/6904.js.map +1 -0
- package/dist/7045.js +1 -0
- package/dist/7045.js.map +1 -0
- package/dist/7175.js +1 -0
- package/dist/7175.js.map +1 -0
- package/dist/7182.js +1 -0
- package/dist/7182.js.map +1 -0
- package/dist/7649.js +1 -0
- package/dist/7649.js.map +1 -0
- package/dist/7742.js +1 -0
- package/dist/7742.js.map +1 -0
- package/dist/7912.js +1 -0
- package/dist/7912.js.map +1 -0
- package/dist/8358.js +1 -0
- package/dist/8358.js.map +1 -0
- package/dist/8359.js +1 -0
- package/dist/8359.js.map +1 -0
- package/dist/8695.js +1 -0
- package/dist/8695.js.map +1 -0
- package/dist/903.js +1 -0
- package/dist/903.js.map +1 -0
- package/dist/9061.js +1 -0
- package/dist/9061.js.map +1 -0
- package/dist/9072.js +1 -0
- package/dist/9072.js.map +1 -0
- package/dist/9397.js +1 -0
- package/dist/9397.js.map +1 -0
- package/dist/9712.js +1 -0
- package/dist/9712.js.map +1 -0
- package/dist/9771.js +1 -0
- package/dist/9771.js.map +1 -0
- package/dist/9806.js +1 -0
- package/dist/9806.js.map +1 -0
- package/dist/9816.js +1 -0
- package/dist/9816.js.map +1 -0
- package/dist/main.js +7 -6
- package/dist/main.js.map +1 -1
- package/dist/openmrs-esm-patient-registration-app.js +6 -0
- package/dist/{olaboot-esm-patient-registration-app.js.buildmanifest.json → openmrs-esm-patient-registration-app.js.buildmanifest.json} +540 -455
- package/dist/openmrs-esm-patient-registration-app.js.map +1 -0
- package/dist/routes.json +1 -1
- package/package.json +8 -9
- package/src/add-patient-link.extension.tsx +3 -2
- package/src/add-patient-link.test.tsx +2 -1
- package/src/config-schema.ts +507 -507
- package/src/constants.ts +1 -1
- package/src/index.ts +2 -24
- package/src/nav-link.test.tsx +1 -0
- package/src/offline.resources.ts +97 -31
- package/src/patient-registration/before-save-prompt.test.tsx +199 -0
- package/src/patient-registration/field/__mocks__/field.resource.ts +8 -7
- package/src/patient-registration/field/address/address-field.component.tsx +183 -186
- package/src/patient-registration/field/address/address-hierarchy-levels.component.tsx +6 -1
- package/src/patient-registration/field/address/address-hierarchy.test.tsx +289 -296
- package/src/patient-registration/field/address/address-search.component.tsx +20 -8
- package/src/patient-registration/field/address/address-search.scss +19 -2
- package/src/patient-registration/field/address/address-search.test.tsx +333 -141
- package/src/patient-registration/field/address/custom-address-field.component.tsx +1 -1
- package/src/patient-registration/field/cause-of-death/cause-of-death.component.tsx +1 -1
- package/src/patient-registration/field/cause-of-death/cause-of-death.test.tsx +251 -0
- package/src/patient-registration/field/custom-field.component.tsx +1 -1
- package/src/patient-registration/field/date-and-time-of-death/date-and-time-of-death.component.tsx +1 -1
- package/src/patient-registration/field/date-and-time-of-death/date-and-time-of-death.test.tsx +144 -0
- package/src/patient-registration/field/dob/dob.component.tsx +2 -2
- package/src/patient-registration/field/dob/dob.test.tsx +370 -54
- package/src/patient-registration/field/field.component.tsx +1 -1
- package/src/patient-registration/field/field.resource.ts +2 -2
- package/src/patient-registration/field/field.test.tsx +333 -330
- package/src/patient-registration/field/gender/gender-field.test.tsx +240 -54
- package/src/patient-registration/field/id/id-field.component.tsx +146 -144
- package/src/patient-registration/field/id/id-field.test.tsx +177 -121
- package/src/patient-registration/field/id/identifier-selection-overlay.component.tsx +200 -200
- package/src/patient-registration/field/id/identifier-selection-overlay.test.tsx +346 -0
- package/src/patient-registration/field/name/name-field.component.tsx +2 -2
- package/src/patient-registration/field/name/name-field.test.tsx +282 -0
- package/src/patient-registration/field/obs/obs-field.test.tsx +294 -118
- package/src/patient-registration/field/person-attributes/coded-person-attribute-field.test.tsx +172 -108
- package/src/patient-registration/field/person-attributes/location-person-attribute-field.component.tsx +3 -3
- package/src/patient-registration/field/person-attributes/location-person-attribute-field.resource.tsx +2 -5
- package/src/patient-registration/field/person-attributes/person-attribute-field.component.tsx +2 -2
- package/src/patient-registration/field/person-attributes/person-attribute-field.test.tsx +249 -131
- package/src/patient-registration/field/person-attributes/person-attributes.resource.ts +1 -1
- package/src/patient-registration/field/person-attributes/text-person-attribute-field.test.tsx +98 -70
- package/src/patient-registration/field/phone/phone-field.test.tsx +100 -0
- package/src/patient-registration/form-manager.test.ts +6 -5
- package/src/patient-registration/form-manager.ts +5 -2
- package/src/patient-registration/input/basic-input/input/input.component.tsx +3 -121
- package/src/patient-registration/input/basic-input/input/input.test.tsx +151 -51
- package/src/patient-registration/input/basic-input/select/select-input.test.tsx +113 -33
- package/src/patient-registration/input/combo-input/combo-input.component.tsx +60 -24
- package/src/patient-registration/input/custom-input/autosuggest/autosuggest.component.tsx +10 -101
- package/src/patient-registration/input/custom-input/autosuggest/autosuggest.test.tsx +144 -108
- package/src/patient-registration/input/custom-input/identifier/identifier-input.test.tsx +241 -177
- package/src/patient-registration/input/custom-input/identifier/utils.test.ts +47 -8
- package/src/patient-registration/input/dummy-data/dummy-data-input.component.tsx +12 -12
- package/src/patient-registration/input/dummy-data/dummy-data-input.test.tsx +52 -20
- package/src/patient-registration/input/input.scss +1 -2
- package/src/patient-registration/patient-registration-context.ts +5 -3
- package/src/patient-registration/patient-registration-hooks.ts +4 -12
- package/src/patient-registration/patient-registration-utils.test.ts +2 -1
- package/src/patient-registration/patient-registration-utils.ts +2 -98
- package/src/patient-registration/patient-registration.component.tsx +50 -46
- package/src/patient-registration/patient-registration.resource.test.tsx +4 -7
- package/src/patient-registration/patient-registration.resource.ts +1 -4
- package/src/patient-registration/patient-registration.scss +16 -3
- package/src/patient-registration/patient-registration.test.tsx +614 -580
- package/src/patient-registration/patient-registration.types.ts +17 -28
- package/src/patient-registration/section/death-info/death-info-section.test.tsx +130 -34
- package/src/patient-registration/section/demographics/demographics-section.test.tsx +122 -68
- package/src/patient-registration/section/patient-relationships/relationships-section.component.tsx +15 -15
- package/src/patient-registration/section/patient-relationships/relationships-section.test.tsx +278 -84
- package/src/patient-registration/section/section-wrapper.component.tsx +1 -1
- package/src/patient-registration/ui-components/overlay/overlay.test.tsx +104 -0
- package/src/patient-registration/validation/patient-registration-validation.test.ts +2 -1
- package/src/patient-registration/validation/patient-registration-validation.ts +9 -3
- package/src/root.component.tsx +2 -5
- package/src/widgets/cancel-patient-edit.test.tsx +48 -11
- package/src/widgets/delete-identifier-confirmation.test.tsx +77 -24
- package/src/widgets/edit-patient-details-button.component.tsx +14 -18
- package/src/widgets/edit-patient-details-button.scss +2 -2
- package/src/widgets/edit-patient-details-button.test.tsx +11 -13
- package/translations/am.json +9 -4
- package/translations/ar.json +9 -4
- package/translations/ar_SY.json +9 -4
- package/translations/bn.json +9 -4
- package/translations/cs.json +9 -4
- package/translations/de.json +120 -115
- package/translations/en.json +10 -4
- package/translations/en_US.json +9 -4
- package/translations/es.json +9 -4
- package/translations/es_MX.json +9 -4
- package/translations/fr.json +9 -4
- package/translations/he.json +9 -4
- package/translations/hi.json +9 -4
- package/translations/hi_IN.json +9 -4
- package/translations/id.json +9 -4
- package/translations/it.json +9 -4
- package/translations/ka.json +9 -4
- package/translations/km.json +9 -4
- package/translations/ku.json +9 -4
- package/translations/ky.json +9 -4
- package/translations/lg.json +9 -4
- package/translations/ne.json +9 -4
- package/translations/pl.json +9 -4
- package/translations/pt.json +9 -4
- package/translations/pt_BR.json +10 -5
- package/translations/qu.json +9 -4
- package/translations/ro_RO.json +9 -4
- package/translations/ru_RU.json +9 -4
- package/translations/si.json +9 -4
- package/translations/sq.json +9 -4
- package/translations/sw.json +9 -4
- package/translations/sw_KE.json +9 -4
- package/translations/tr.json +9 -4
- package/translations/tr_TR.json +9 -4
- package/translations/uk.json +9 -4
- package/translations/uz.json +9 -4
- package/translations/uz@Latn.json +9 -4
- package/translations/uz_UZ.json +9 -4
- package/translations/vi.json +9 -4
- package/translations/zh.json +50 -45
- package/translations/zh_CN.json +9 -4
- package/translations/zh_TW.json +9 -4
- package/vitest.config.ts +4 -0
- package/dist/126.js +0 -1
- package/dist/15.js +0 -1
- package/dist/1564.js +0 -1
- package/dist/1567.js +0 -1
- package/dist/1845.js +0 -1
- package/dist/1953.js +0 -1
- package/dist/200.js +0 -1
- package/dist/200.js.map +0 -1
- package/dist/215.js +0 -1
- package/dist/2178.js +0 -1
- package/dist/250.js +0 -1
- package/dist/250.js.map +0 -1
- package/dist/2523.js +0 -1
- package/dist/2523.js.map +0 -1
- package/dist/2566.js +0 -1
- package/dist/2586.js +0 -1
- package/dist/2586.js.map +0 -1
- package/dist/2716.js +0 -1
- package/dist/2716.js.map +0 -1
- package/dist/2759.js +0 -1
- package/dist/2821.js +0 -6
- package/dist/2821.js.map +0 -1
- package/dist/3089.js +0 -1
- package/dist/3089.js.map +0 -1
- package/dist/3230.js +0 -1
- package/dist/3441.js +0 -1
- package/dist/3565.js +0 -1
- package/dist/3571.js +0 -1
- package/dist/3571.js.map +0 -1
- package/dist/3746.js +0 -1
- package/dist/3925.js +0 -1
- package/dist/3946.js +0 -1
- package/dist/4024.js +0 -1
- package/dist/4744.js +0 -1
- package/dist/4744.js.map +0 -1
- package/dist/4809.js +0 -1
- package/dist/4894.js +0 -1
- package/dist/4970.js +0 -1
- package/dist/4970.js.map +0 -1
- package/dist/5130.js +0 -1
- package/dist/5187.js +0 -1
- package/dist/5491.js +0 -1
- package/dist/5491.js.map +0 -1
- package/dist/5595.js +0 -1
- package/dist/5961.js +0 -1
- package/dist/6133.js +0 -1
- package/dist/634.js +0 -1
- package/dist/634.js.map +0 -1
- package/dist/6456.js +0 -1
- package/dist/6466.js +0 -1
- package/dist/6613.js +0 -1
- package/dist/6783.js +0 -1
- package/dist/7073.js +0 -38
- package/dist/7073.js.map +0 -1
- package/dist/7154.js +0 -1
- package/dist/7154.js.map +0 -1
- package/dist/7348.js +0 -1
- package/dist/7439.js +0 -1
- package/dist/7439.js.map +0 -1
- package/dist/7543.js +0 -1
- package/dist/7607.js +0 -1
- package/dist/772.js +0 -1
- package/dist/7984.js +0 -1
- package/dist/7984.js.map +0 -1
- package/dist/8538.js +0 -1
- package/dist/8538.js.map +0 -1
- package/dist/8599.js +0 -1
- package/dist/8727.js +0 -1
- package/dist/8847.js +0 -1
- package/dist/9015.js +0 -1
- package/dist/906.js +0 -1
- package/dist/9065.js +0 -1
- package/dist/9182.js +0 -1
- package/dist/9339.js +0 -1
- package/dist/9453.js +0 -1
- package/dist/9833.js +0 -1
- package/dist/9833.js.map +0 -1
- package/dist/9856.js +0 -1
- package/dist/9856.js.map +0 -1
- package/dist/9920.js +0 -1
- package/dist/9938.js +0 -1
- package/dist/9943.js +0 -1
- package/dist/9943.js.map +0 -1
- package/dist/olaboot-esm-patient-registration-app.js +0 -5
- package/dist/olaboot-esm-patient-registration-app.js.map +0 -1
- package/jest.config.js +0 -3
- package/src/resource.ts +0 -12
package/src/constants.ts
CHANGED
|
@@ -6,7 +6,7 @@ export const personRelationshipRepresentation =
|
|
|
6
6
|
'personB:(age,display,birthdate,uuid),' +
|
|
7
7
|
'relationshipType:(uuid,display,description,aIsToB,bIsToA))';
|
|
8
8
|
|
|
9
|
-
export const moduleName = '@
|
|
9
|
+
export const moduleName = '@openmrs/esm-patient-registration-app';
|
|
10
10
|
export const patientRegistration = 'patient-registration';
|
|
11
11
|
|
|
12
12
|
export const cacheForOfflineHeaders: OmrsOfflineHttpHeaders = {
|
package/src/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { defineConfigSchema, getAsyncLifecycle, getSyncLifecycle
|
|
1
|
+
import { defineConfigSchema, getAsyncLifecycle, getSyncLifecycle } from '@openmrs/esm-framework';
|
|
2
2
|
import { esmPatientRegistrationSchema } from './config-schema';
|
|
3
|
-
import { moduleName
|
|
3
|
+
import { moduleName } from './constants';
|
|
4
4
|
import { setupOffline } from './offline';
|
|
5
5
|
import addPatientLinkComponent from './add-patient-link.extension';
|
|
6
6
|
|
|
@@ -13,28 +13,6 @@ const options = {
|
|
|
13
13
|
|
|
14
14
|
export function startupApp() {
|
|
15
15
|
defineConfigSchema(moduleName, esmPatientRegistrationSchema);
|
|
16
|
-
|
|
17
|
-
registerBreadcrumbs([
|
|
18
|
-
{
|
|
19
|
-
path: `${window.spaBase}/${patientRegistration}`,
|
|
20
|
-
// t('patientRegistrationBreadcrumb', 'Patient Registration')
|
|
21
|
-
title: () =>
|
|
22
|
-
Promise.resolve(
|
|
23
|
-
window.i18next.t('patientRegistrationBreadcrumb', { defaultValue: 'Patient Registration', ns: moduleName }),
|
|
24
|
-
),
|
|
25
|
-
parent: `${window.spaBase}/home`,
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
path: `${window.spaBase}/patient/:patientUuid/edit`,
|
|
29
|
-
// t('editPatientDetailsBreadcrumb', 'Edit patient details')
|
|
30
|
-
title: () =>
|
|
31
|
-
Promise.resolve(
|
|
32
|
-
window.i18next.t('editPatientDetailsBreadcrumb', { defaultValue: 'Edit patient details', ns: moduleName }),
|
|
33
|
-
),
|
|
34
|
-
parent: `${window.spaBase}/patient/:patientUuid/chart`,
|
|
35
|
-
},
|
|
36
|
-
]);
|
|
37
|
-
|
|
38
16
|
setupOffline();
|
|
39
17
|
}
|
|
40
18
|
|
package/src/nav-link.test.tsx
CHANGED
package/src/offline.resources.ts
CHANGED
|
@@ -1,16 +1,28 @@
|
|
|
1
1
|
import { camelCase, escapeRegExp, find } from 'lodash-es';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
getConfig,
|
|
4
|
+
messageOmrsServiceWorker,
|
|
5
|
+
openmrsFetch,
|
|
6
|
+
restBaseUrl,
|
|
7
|
+
type FetchResponse,
|
|
8
|
+
type Session,
|
|
9
|
+
} from '@openmrs/esm-framework';
|
|
3
10
|
import type {
|
|
4
|
-
PatientIdentifierType,
|
|
5
|
-
FetchedPatientIdentifierType,
|
|
6
11
|
AddressTemplate,
|
|
12
|
+
ConceptResponse,
|
|
13
|
+
FetchedPatientIdentifierType,
|
|
14
|
+
IdentifierSourceAutoGenerationOption,
|
|
15
|
+
PatientIdentifierType,
|
|
16
|
+
PersonAttributeTypeResponse,
|
|
17
|
+
RelationshipTypesResponse,
|
|
7
18
|
} from './patient-registration/patient-registration.types';
|
|
8
19
|
import { cacheForOfflineHeaders, moduleName } from './constants';
|
|
20
|
+
import type { FieldDefinition } from './config-schema';
|
|
9
21
|
|
|
10
22
|
export interface Resources {
|
|
11
23
|
addressTemplate: AddressTemplate;
|
|
12
24
|
currentSession: Session;
|
|
13
|
-
relationshipTypes:
|
|
25
|
+
relationshipTypes: RelationshipTypesResponse;
|
|
14
26
|
identifierTypes: Array<PatientIdentifierType>;
|
|
15
27
|
}
|
|
16
28
|
|
|
@@ -24,8 +36,8 @@ export async function fetchAddressTemplate() {
|
|
|
24
36
|
return data;
|
|
25
37
|
}
|
|
26
38
|
|
|
27
|
-
export async function fetchAllRelationshipTypes() {
|
|
28
|
-
const { data } = await cacheAndFetch(`${restBaseUrl}/relationshiptype?v=default`);
|
|
39
|
+
export async function fetchAllRelationshipTypes(): Promise<RelationshipTypesResponse> {
|
|
40
|
+
const { data } = await cacheAndFetch<RelationshipTypesResponse>(`${restBaseUrl}/relationshiptype?v=default`);
|
|
29
41
|
return data;
|
|
30
42
|
}
|
|
31
43
|
|
|
@@ -50,39 +62,61 @@ export async function fetchAllFieldDefinitionTypes() {
|
|
|
50
62
|
return mergedData;
|
|
51
63
|
}
|
|
52
64
|
|
|
53
|
-
async function fetchFieldDefinitionType(
|
|
65
|
+
async function fetchFieldDefinitionType(
|
|
66
|
+
fieldDefinition: FieldDefinition,
|
|
67
|
+
): Promise<PersonAttributeTypeResponse | undefined> {
|
|
54
68
|
let apiUrl = '';
|
|
55
69
|
|
|
56
70
|
if (fieldDefinition.type === 'person attribute') {
|
|
57
71
|
apiUrl = `${restBaseUrl}/personattributetype/${fieldDefinition.uuid}`;
|
|
58
72
|
}
|
|
59
73
|
|
|
74
|
+
// Pre-fetch the concept for offline caching if answerConceptSetUuid is provided.
|
|
75
|
+
// This ensures the concept answers are available offline even though we don't use the result here.
|
|
76
|
+
// The actual concept data is fetched later via useConceptAnswers hook when rendering the field.
|
|
60
77
|
if (fieldDefinition.answerConceptSetUuid) {
|
|
61
|
-
await cacheAndFetch(`${restBaseUrl}/concept/${fieldDefinition.answerConceptSetUuid}`);
|
|
78
|
+
await cacheAndFetch<ConceptResponse>(`${restBaseUrl}/concept/${fieldDefinition.answerConceptSetUuid}`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (!apiUrl) {
|
|
82
|
+
return undefined;
|
|
62
83
|
}
|
|
63
|
-
|
|
84
|
+
|
|
85
|
+
const { data } = await cacheAndFetch<PersonAttributeTypeResponse>(apiUrl);
|
|
64
86
|
return data;
|
|
65
87
|
}
|
|
66
88
|
|
|
89
|
+
function isValidFetchedIdentifierType(
|
|
90
|
+
type: FetchedPatientIdentifierType | null | undefined,
|
|
91
|
+
): type is FetchedPatientIdentifierType {
|
|
92
|
+
return type !== null && type !== undefined;
|
|
93
|
+
}
|
|
94
|
+
|
|
67
95
|
export async function fetchPatientIdentifierTypesWithSources(): Promise<Array<PatientIdentifierType>> {
|
|
68
96
|
const patientIdentifierTypes = await fetchPatientIdentifierTypes();
|
|
69
97
|
|
|
70
|
-
|
|
71
|
-
|
|
98
|
+
const validIdentifierTypes = patientIdentifierTypes.filter(isValidFetchedIdentifierType);
|
|
99
|
+
// Convert FetchedPatientIdentifierType to PatientIdentifierType
|
|
100
|
+
const identifierTypes: Array<PatientIdentifierType> = validIdentifierTypes.map((type) => ({
|
|
101
|
+
...type,
|
|
102
|
+
identifierSources: [],
|
|
103
|
+
}));
|
|
72
104
|
|
|
73
105
|
const [autoGenOptions, identifierSourcesResponse] = await Promise.all([
|
|
74
106
|
fetchAutoGenerationOptions(),
|
|
75
107
|
fetchIdentifierSources(),
|
|
76
108
|
]);
|
|
77
109
|
|
|
78
|
-
const allIdentifierSources = identifierSourcesResponse.data
|
|
110
|
+
const allIdentifierSources = identifierSourcesResponse.data?.results || [];
|
|
79
111
|
|
|
80
|
-
for (let i = 0; i < identifierTypes
|
|
112
|
+
for (let i = 0; i < identifierTypes.length; i++) {
|
|
81
113
|
identifierTypes[i].identifierSources = allIdentifierSources
|
|
82
114
|
.filter((source) => source.identifierType.uuid === identifierTypes[i].uuid)
|
|
83
115
|
.map((source) => {
|
|
84
|
-
const option = find(autoGenOptions.data
|
|
85
|
-
|
|
116
|
+
const option = find(autoGenOptions.data?.results || [], { source: { uuid: source.uuid } });
|
|
117
|
+
if (option && 'manualEntryEnabled' in option && 'automaticGenerationEnabled' in option) {
|
|
118
|
+
source.autoGenerationOption = option as IdentifierSourceAutoGenerationOption;
|
|
119
|
+
}
|
|
86
120
|
return source;
|
|
87
121
|
});
|
|
88
122
|
}
|
|
@@ -90,28 +124,40 @@ export async function fetchPatientIdentifierTypesWithSources(): Promise<Array<Pa
|
|
|
90
124
|
return identifierTypes;
|
|
91
125
|
}
|
|
92
126
|
|
|
93
|
-
|
|
127
|
+
interface ApiPatientIdentifierType {
|
|
128
|
+
display: string;
|
|
129
|
+
uuid: string;
|
|
130
|
+
name: string;
|
|
131
|
+
format: string;
|
|
132
|
+
formatDescription?: string;
|
|
133
|
+
required: boolean;
|
|
134
|
+
uniquenessBehavior: undefined | null | 'UNIQUE' | 'NON_UNIQUE' | 'LOCATION';
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function fetchPatientIdentifierTypes(): Promise<Array<FetchedPatientIdentifierType | null>> {
|
|
94
138
|
const [patientIdentifierTypesResponse, primaryIdentifierTypeResponse] = await Promise.all([
|
|
95
|
-
cacheAndFetch(
|
|
139
|
+
cacheAndFetch<{ results: Array<ApiPatientIdentifierType> }>(
|
|
96
140
|
`${restBaseUrl}/patientidentifiertype?v=custom:(display,uuid,name,format,formatDescription,required,uniquenessBehavior)`,
|
|
97
141
|
),
|
|
98
|
-
cacheAndFetch
|
|
142
|
+
cacheAndFetch<{ results: Array<{ metadataUuid: string }> }>(
|
|
143
|
+
`${restBaseUrl}/metadatamapping/termmapping?v=full&code=emr.primaryIdentifierType`,
|
|
144
|
+
),
|
|
99
145
|
]);
|
|
100
146
|
|
|
101
147
|
if (patientIdentifierTypesResponse.ok) {
|
|
102
148
|
// Primary identifier type is to be kept at the top of the list.
|
|
103
|
-
const patientIdentifierTypes = patientIdentifierTypesResponse?.data?.results;
|
|
149
|
+
const patientIdentifierTypes = patientIdentifierTypesResponse?.data?.results || [];
|
|
104
150
|
|
|
105
151
|
const primaryIdentifierTypeUuid = primaryIdentifierTypeResponse?.data?.results?.[0]?.metadataUuid;
|
|
106
152
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
153
|
+
const identifierTypes: Array<FetchedPatientIdentifierType | null> = [];
|
|
154
|
+
|
|
155
|
+
if (primaryIdentifierTypeResponse?.ok && primaryIdentifierTypeUuid) {
|
|
156
|
+
const primaryType = patientIdentifierTypes.find((type) => type.uuid === primaryIdentifierTypeUuid);
|
|
157
|
+
if (primaryType) {
|
|
158
|
+
identifierTypes.push(mapPatientIdentifierType(primaryType, true));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
115
161
|
|
|
116
162
|
patientIdentifierTypes.forEach((type) => {
|
|
117
163
|
if (type.uuid !== primaryIdentifierTypeUuid) {
|
|
@@ -124,15 +170,32 @@ async function fetchPatientIdentifierTypes(): Promise<Array<FetchedPatientIdenti
|
|
|
124
170
|
return [];
|
|
125
171
|
}
|
|
126
172
|
|
|
173
|
+
interface IdentifierSourceResponse {
|
|
174
|
+
results: Array<{
|
|
175
|
+
uuid: string;
|
|
176
|
+
name: string;
|
|
177
|
+
identifierType: { uuid: string };
|
|
178
|
+
autoGenerationOption?: IdentifierSourceAutoGenerationOption;
|
|
179
|
+
}>;
|
|
180
|
+
}
|
|
181
|
+
|
|
127
182
|
async function fetchIdentifierSources() {
|
|
128
|
-
return await cacheAndFetch(`${restBaseUrl}/idgen/identifiersource?v=default`);
|
|
183
|
+
return await cacheAndFetch<IdentifierSourceResponse>(`${restBaseUrl}/idgen/identifiersource?v=default`);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
interface AutoGenerationOptionResponse {
|
|
187
|
+
results: Array<IdentifierSourceAutoGenerationOption & { source: { uuid: string } }>;
|
|
129
188
|
}
|
|
130
189
|
|
|
131
190
|
async function fetchAutoGenerationOptions(abortController?: AbortController) {
|
|
132
|
-
return await cacheAndFetch(`${restBaseUrl}/idgen/autogenerationoption?v=full`);
|
|
191
|
+
return await cacheAndFetch<AutoGenerationOptionResponse>(`${restBaseUrl}/idgen/autogenerationoption?v=full`);
|
|
133
192
|
}
|
|
134
193
|
|
|
135
|
-
async function cacheAndFetch<T =
|
|
194
|
+
async function cacheAndFetch<T = unknown>(url?: string): Promise<FetchResponse<T>> {
|
|
195
|
+
if (!url) {
|
|
196
|
+
throw new Error('URL is required for cacheAndFetch');
|
|
197
|
+
}
|
|
198
|
+
|
|
136
199
|
const abortController = new AbortController();
|
|
137
200
|
|
|
138
201
|
await messageOmrsServiceWorker({
|
|
@@ -143,7 +206,10 @@ async function cacheAndFetch<T = any>(url?: string) {
|
|
|
143
206
|
return await openmrsFetch<T>(url, { headers: cacheForOfflineHeaders, signal: abortController?.signal });
|
|
144
207
|
}
|
|
145
208
|
|
|
146
|
-
function mapPatientIdentifierType(
|
|
209
|
+
function mapPatientIdentifierType(
|
|
210
|
+
patientIdentifierType: ApiPatientIdentifierType,
|
|
211
|
+
isPrimary: boolean,
|
|
212
|
+
): FetchedPatientIdentifierType {
|
|
147
213
|
return {
|
|
148
214
|
name: patientIdentifierType.display,
|
|
149
215
|
fieldName: camelCase(patientIdentifierType.name),
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { vi, describe, it, expect, beforeEach, afterEach, type Mock, type MockInstance } from 'vitest';
|
|
3
|
+
import { render, waitFor } from '@testing-library/react';
|
|
4
|
+
import { showModal, navigate } from '@openmrs/esm-framework';
|
|
5
|
+
import BeforeSavePrompt from './before-save-prompt.component';
|
|
6
|
+
|
|
7
|
+
const mockShowModal = vi.mocked(showModal);
|
|
8
|
+
const mockNavigate = vi.mocked(navigate);
|
|
9
|
+
|
|
10
|
+
const mockGetOpenmrsSpaBase = vi.fn(() => '/openmrs/spa');
|
|
11
|
+
window['getOpenmrsSpaBase'] = mockGetOpenmrsSpaBase;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Helper to render BeforeSavePrompt component.
|
|
15
|
+
*/
|
|
16
|
+
function renderBeforeSavePrompt(when: boolean, redirect?: string) {
|
|
17
|
+
return render(<BeforeSavePrompt when={when} redirect={redirect} />);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe('BeforeSavePrompt component', () => {
|
|
21
|
+
let addEventListenerSpy: MockInstance;
|
|
22
|
+
let removeEventListenerSpy: MockInstance;
|
|
23
|
+
let disposeMock: Mock;
|
|
24
|
+
|
|
25
|
+
beforeEach(() => {
|
|
26
|
+
disposeMock = vi.fn();
|
|
27
|
+
mockShowModal.mockReturnValue(disposeMock);
|
|
28
|
+
mockNavigate.mockClear();
|
|
29
|
+
|
|
30
|
+
// Spy on window.addEventListener and removeEventListener
|
|
31
|
+
addEventListenerSpy = vi.spyOn(window, 'addEventListener');
|
|
32
|
+
removeEventListenerSpy = vi.spyOn(window, 'removeEventListener');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
afterEach(() => {
|
|
36
|
+
addEventListenerSpy.mockRestore();
|
|
37
|
+
removeEventListenerSpy.mockRestore();
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
describe('Event listener setup', () => {
|
|
41
|
+
it('does not set up event listeners when when is false', () => {
|
|
42
|
+
renderBeforeSavePrompt(false);
|
|
43
|
+
|
|
44
|
+
expect(addEventListenerSpy).not.toHaveBeenCalled();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('sets up beforeunload and navigation event listeners when when is true', () => {
|
|
48
|
+
renderBeforeSavePrompt(true);
|
|
49
|
+
|
|
50
|
+
expect(addEventListenerSpy).toHaveBeenCalledWith('beforeunload', expect.any(Function));
|
|
51
|
+
expect(addEventListenerSpy).toHaveBeenCalledWith('single-spa:before-routing-event', expect.any(Function));
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('removes event listeners on unmount', () => {
|
|
55
|
+
const { unmount } = renderBeforeSavePrompt(true);
|
|
56
|
+
|
|
57
|
+
unmount();
|
|
58
|
+
|
|
59
|
+
expect(removeEventListenerSpy).toHaveBeenCalledWith('beforeunload', expect.any(Function));
|
|
60
|
+
expect(removeEventListenerSpy).toHaveBeenCalledWith('single-spa:before-routing-event', expect.any(Function));
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('does not set up listeners when redirect is provided', () => {
|
|
64
|
+
renderBeforeSavePrompt(true, '/some/path');
|
|
65
|
+
|
|
66
|
+
expect(addEventListenerSpy).not.toHaveBeenCalled();
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
describe('Beforeunload event handling', () => {
|
|
71
|
+
it('prevents default and sets returnValue on beforeunload event', () => {
|
|
72
|
+
renderBeforeSavePrompt(true);
|
|
73
|
+
|
|
74
|
+
const beforeunloadHandler = addEventListenerSpy.mock.calls.find((call) => call[0] === 'beforeunload')?.[1] as (
|
|
75
|
+
e: BeforeUnloadEvent,
|
|
76
|
+
) => string;
|
|
77
|
+
|
|
78
|
+
const mockEvent = {
|
|
79
|
+
preventDefault: vi.fn(),
|
|
80
|
+
returnValue: '',
|
|
81
|
+
} as unknown as BeforeUnloadEvent;
|
|
82
|
+
|
|
83
|
+
const result = beforeunloadHandler(mockEvent);
|
|
84
|
+
|
|
85
|
+
expect(mockEvent.preventDefault).toHaveBeenCalled();
|
|
86
|
+
expect(mockEvent.returnValue).toBeTruthy();
|
|
87
|
+
expect(result).toBeTruthy();
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('Navigation event handling', () => {
|
|
92
|
+
it('shows modal and cancels navigation on single-spa routing event', () => {
|
|
93
|
+
renderBeforeSavePrompt(true);
|
|
94
|
+
|
|
95
|
+
const navigationHandler = addEventListenerSpy.mock.calls.find(
|
|
96
|
+
(call) => call[0] === 'single-spa:before-routing-event',
|
|
97
|
+
)?.[1] as (e: CustomEvent) => void;
|
|
98
|
+
|
|
99
|
+
const mockCancelNavigation = vi.fn();
|
|
100
|
+
const mockEvent = {
|
|
101
|
+
detail: {
|
|
102
|
+
newUrl: '/openmrs/spa/new-page',
|
|
103
|
+
navigationIsCanceled: false,
|
|
104
|
+
cancelNavigation: mockCancelNavigation,
|
|
105
|
+
},
|
|
106
|
+
} as unknown as CustomEvent;
|
|
107
|
+
|
|
108
|
+
navigationHandler(mockEvent);
|
|
109
|
+
|
|
110
|
+
expect(mockCancelNavigation).toHaveBeenCalled();
|
|
111
|
+
expect(mockShowModal).toHaveBeenCalledWith(
|
|
112
|
+
'cancel-patient-edit-modal',
|
|
113
|
+
expect.objectContaining({
|
|
114
|
+
onConfirm: expect.any(Function),
|
|
115
|
+
}),
|
|
116
|
+
expect.any(Function),
|
|
117
|
+
);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
it('does not show modal if navigation is already canceled', () => {
|
|
121
|
+
renderBeforeSavePrompt(true);
|
|
122
|
+
|
|
123
|
+
const navigationHandler = addEventListenerSpy.mock.calls.find(
|
|
124
|
+
(call) => call[0] === 'single-spa:before-routing-event',
|
|
125
|
+
)?.[1] as (e: CustomEvent) => void;
|
|
126
|
+
|
|
127
|
+
const mockEvent = {
|
|
128
|
+
detail: {
|
|
129
|
+
newUrl: '/openmrs/spa/new-page',
|
|
130
|
+
navigationIsCanceled: true,
|
|
131
|
+
cancelNavigation: vi.fn(),
|
|
132
|
+
},
|
|
133
|
+
} as unknown as CustomEvent;
|
|
134
|
+
|
|
135
|
+
navigationHandler(mockEvent);
|
|
136
|
+
|
|
137
|
+
expect(mockShowModal).not.toHaveBeenCalled();
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('navigates to target URL when modal is confirmed', async () => {
|
|
141
|
+
renderBeforeSavePrompt(true);
|
|
142
|
+
|
|
143
|
+
const navigationHandler = addEventListenerSpy.mock.calls.find(
|
|
144
|
+
(call) => call[0] === 'single-spa:before-routing-event',
|
|
145
|
+
)?.[1] as (e: CustomEvent) => void;
|
|
146
|
+
|
|
147
|
+
const mockEvent = {
|
|
148
|
+
detail: {
|
|
149
|
+
newUrl: '/openmrs/spa/new-page',
|
|
150
|
+
navigationIsCanceled: false,
|
|
151
|
+
cancelNavigation: vi.fn(),
|
|
152
|
+
},
|
|
153
|
+
} as unknown as CustomEvent;
|
|
154
|
+
|
|
155
|
+
// Call navigationHandler directly - it triggers showModal synchronously
|
|
156
|
+
navigationHandler(mockEvent);
|
|
157
|
+
|
|
158
|
+
// Get the onConfirm callback from showModal call
|
|
159
|
+
// showModal is called with: (modalName, props, onClose)
|
|
160
|
+
const modalProps = mockShowModal.mock.calls[0]?.[1] as { onConfirm?: () => void } | undefined;
|
|
161
|
+
const onConfirm = modalProps?.onConfirm;
|
|
162
|
+
|
|
163
|
+
expect(onConfirm).toBeDefined();
|
|
164
|
+
|
|
165
|
+
// Call onConfirm directly - waitFor will handle async updates
|
|
166
|
+
onConfirm?.();
|
|
167
|
+
|
|
168
|
+
await waitFor(() => {
|
|
169
|
+
expect(mockNavigate).toHaveBeenCalledWith({
|
|
170
|
+
to: expect.stringContaining('/new-page'),
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
expect(disposeMock).toHaveBeenCalled();
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
describe('Redirect handling', () => {
|
|
178
|
+
it('navigates to redirect URL when redirect prop is provided', async () => {
|
|
179
|
+
renderBeforeSavePrompt(true, '/openmrs/spa/target-page');
|
|
180
|
+
|
|
181
|
+
await waitFor(() => {
|
|
182
|
+
expect(mockNavigate).toHaveBeenCalledWith({
|
|
183
|
+
to: expect.stringContaining('/target-page'),
|
|
184
|
+
});
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it('handles redirect with full URL', async () => {
|
|
189
|
+
mockGetOpenmrsSpaBase.mockReturnValue('/openmrs/spa');
|
|
190
|
+
renderBeforeSavePrompt(true, '/openmrs/spa/target-page');
|
|
191
|
+
|
|
192
|
+
await waitFor(() => {
|
|
193
|
+
expect(mockNavigate).toHaveBeenCalledWith({
|
|
194
|
+
to: expect.stringContaining('/target-page'),
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
});
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
+
import { vi } from 'vitest';
|
|
1
2
|
import { type ConceptResponse } from '../../patient-registration.types';
|
|
2
3
|
|
|
3
|
-
export const useConcept =
|
|
4
|
+
export const useConcept = vi.fn(function mockUseConceptImplementation(uuid: string): {
|
|
4
5
|
data: ConceptResponse;
|
|
5
6
|
isLoading: boolean;
|
|
6
7
|
} {
|
|
7
8
|
let data;
|
|
8
|
-
if (uuid
|
|
9
|
+
if (uuid === 'weight-uuid') {
|
|
9
10
|
data = {
|
|
10
11
|
uuid: 'weight-uuid',
|
|
11
12
|
display: 'Weight (kg)',
|
|
@@ -13,7 +14,7 @@ export const useConcept = jest.fn(function mockUseConceptImplementation(uuid: st
|
|
|
13
14
|
answers: [],
|
|
14
15
|
setMembers: [],
|
|
15
16
|
};
|
|
16
|
-
} else if (uuid
|
|
17
|
+
} else if (uuid === 'chief-complaint-uuid') {
|
|
17
18
|
data = {
|
|
18
19
|
uuid: 'chief-complaint-uuid',
|
|
19
20
|
display: 'Chief Complaint',
|
|
@@ -21,7 +22,7 @@ export const useConcept = jest.fn(function mockUseConceptImplementation(uuid: st
|
|
|
21
22
|
answers: [],
|
|
22
23
|
setMembers: [],
|
|
23
24
|
};
|
|
24
|
-
} else if (uuid
|
|
25
|
+
} else if (uuid === 'nationality-uuid') {
|
|
25
26
|
data = {
|
|
26
27
|
uuid: 'nationality-uuid',
|
|
27
28
|
display: 'Nationality',
|
|
@@ -39,8 +40,8 @@ export const useConcept = jest.fn(function mockUseConceptImplementation(uuid: st
|
|
|
39
40
|
};
|
|
40
41
|
});
|
|
41
42
|
|
|
42
|
-
export const useConceptAnswers =
|
|
43
|
-
if (uuid
|
|
43
|
+
export const useConceptAnswers = vi.fn((uuid: string) => {
|
|
44
|
+
if (uuid === 'nationality-uuid') {
|
|
44
45
|
return {
|
|
45
46
|
data: [
|
|
46
47
|
{ display: 'USA', uuid: 'usa' },
|
|
@@ -48,7 +49,7 @@ export const useConceptAnswers = jest.fn((uuid: string) => {
|
|
|
48
49
|
],
|
|
49
50
|
isLoading: false,
|
|
50
51
|
};
|
|
51
|
-
} else if (uuid
|
|
52
|
+
} else if (uuid === 'other-countries-uuid') {
|
|
52
53
|
return {
|
|
53
54
|
data: [
|
|
54
55
|
{ display: 'Kenya', uuid: 'ke' },
|