@ampath/esm-patient-registration-app 6.0.1-pre.24 → 6.0.1-pre.241

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 (136) hide show
  1. package/.turbo/turbo-build.log +24 -23
  2. package/dist/132.js +1 -1
  3. package/dist/197.js +1 -1
  4. package/dist/236.js +1 -1
  5. package/dist/300.js +1 -1
  6. package/dist/335.js +1 -1
  7. package/dist/41.js +1 -1
  8. package/dist/41.js.map +1 -1
  9. package/dist/421.js +1 -0
  10. package/dist/421.js.map +1 -0
  11. package/dist/449.js +1 -0
  12. package/dist/449.js.map +1 -0
  13. package/dist/531.js +2 -0
  14. package/dist/531.js.LICENSE.txt +24 -0
  15. package/dist/531.js.map +1 -0
  16. package/dist/55.js +1 -1
  17. package/dist/652.js +1 -1
  18. package/dist/661.js +1 -1
  19. package/dist/757.js +1 -0
  20. package/dist/757.js.map +1 -0
  21. package/dist/813.js +1 -0
  22. package/dist/813.js.map +1 -0
  23. package/dist/828.js +1 -0
  24. package/dist/828.js.map +1 -0
  25. package/dist/830.js +1 -0
  26. package/dist/830.js.map +1 -0
  27. package/dist/831.js +1 -1
  28. package/dist/831.js.map +1 -1
  29. package/dist/879.js +1 -1
  30. package/dist/913.js +1 -1
  31. package/dist/913.js.LICENSE.txt +3 -3
  32. package/dist/913.js.map +1 -1
  33. package/dist/927.js +1 -0
  34. package/dist/927.js.map +1 -0
  35. package/dist/99.js +1 -1
  36. package/dist/ampath-esm-patient-registration-app.js +1 -1
  37. package/dist/ampath-esm-patient-registration-app.js.buildmanifest.json +204 -132
  38. package/dist/ampath-esm-patient-registration-app.js.map +1 -1
  39. package/dist/main.js +1 -1
  40. package/dist/main.js.LICENSE.txt +0 -32
  41. package/dist/main.js.map +1 -1
  42. package/dist/routes.json +1 -1
  43. package/package.json +7 -10
  44. package/src/config-schema.ts +19 -10
  45. package/src/index.ts +11 -4
  46. package/src/offline.resources.ts +13 -18
  47. package/src/offline.ts +6 -4
  48. package/src/patient-photo.extension.tsx +9 -0
  49. package/src/patient-registration/field/address/custom-address-field.component.tsx +1 -0
  50. package/src/patient-registration/field/custom-field.component.tsx +6 -0
  51. package/src/patient-registration/field/dob/dob.component.tsx +45 -49
  52. package/src/patient-registration/field/field.resource.ts +3 -3
  53. package/src/patient-registration/field/obs/obs-field.component.tsx +1 -1
  54. package/src/patient-registration/field/person-attributes/coded-person-attribute-field.component.tsx +4 -0
  55. package/src/patient-registration/field/person-attributes/custom-person-attribute-field.component.tsx +56 -0
  56. package/src/patient-registration/field/person-attributes/person-attribute-field.component.tsx +22 -7
  57. package/src/patient-registration/field/person-attributes/person-attributes.resource.ts +2 -2
  58. package/src/patient-registration/field/phone/phone-field.component.tsx +1 -0
  59. package/src/patient-registration/form-manager.ts +13 -6
  60. package/src/patient-registration/patient-registration-hooks.ts +133 -9
  61. package/src/patient-registration/patient-registration.component.tsx +55 -13
  62. package/src/patient-registration/{patient-registration.resource.test.tsx → patient-registration.resource.testt.tsx} +4 -4
  63. package/src/patient-registration/patient-registration.resource.ts +15 -75
  64. package/src/patient-registration/patient-registration.scss +0 -8
  65. package/src/patient-registration/patient-registration.types.ts +7 -1
  66. package/src/patient-registration/section/patient-relationships/relationships-section.component.tsx +5 -1
  67. package/src/patient-registration/section/patient-relationships/relationships.resource.tsx +2 -2
  68. package/src/patient-verification/assets/counties.json +236 -0
  69. package/src/patient-verification/assets/verification-assets.ts +11 -0
  70. package/src/patient-verification/patient-verification-hook.tsx +181 -0
  71. package/src/patient-verification/patient-verification-utils.ts +179 -0
  72. package/src/patient-verification/patient-verification.component.tsx +124 -0
  73. package/src/patient-verification/patient-verification.scss +25 -0
  74. package/src/patient-verification/verification-modal/confirm-prompt.component.tsx +72 -0
  75. package/src/patient-verification/verification-modal/empty-prompt.component.tsx +35 -0
  76. package/src/patient-verification/verification-types.ts +50 -0
  77. package/src/patient-verification-HIE/assets/counties.json +236 -0
  78. package/src/patient-verification-HIE/assets/verification-assets.ts +11 -0
  79. package/src/patient-verification-HIE/patient-verification-hook.tsx +181 -0
  80. package/src/patient-verification-HIE/patient-verification-utils.ts +179 -0
  81. package/src/patient-verification-HIE/patient-verification.component.tsx +124 -0
  82. package/src/patient-verification-HIE/patient-verification.scss +25 -0
  83. package/src/patient-verification-HIE/verification-modal/confirm-prompt.component.tsx +72 -0
  84. package/src/patient-verification-HIE/verification-modal/empty-prompt.component.tsx +35 -0
  85. package/src/patient-verification-HIE/verification-types.ts +50 -0
  86. package/src/routes.json +12 -3
  87. package/translations/am.json +26 -12
  88. package/translations/ar.json +26 -12
  89. package/translations/en.json +18 -4
  90. package/translations/es.json +10 -0
  91. package/translations/fr.json +6 -0
  92. package/translations/he.json +18 -0
  93. package/translations/km.json +18 -0
  94. package/translations/zh.json +30 -22
  95. package/translations/zh_CN.json +30 -22
  96. package/dist/229.js +0 -2
  97. package/dist/229.js.LICENSE.txt +0 -56
  98. package/dist/229.js.map +0 -1
  99. package/dist/537.js +0 -1
  100. package/dist/537.js.map +0 -1
  101. package/dist/56.js +0 -1
  102. package/dist/56.js.map +0 -1
  103. package/dist/885.js +0 -1
  104. package/dist/885.js.map +0 -1
  105. package/dist/918.js +0 -1
  106. package/dist/918.js.map +0 -1
  107. package/src/patient-registration/field/address/tests/address-hierarchy.test.tsx +0 -214
  108. package/src/patient-registration/field/address/tests/address-search-component.test.tsx +0 -135
  109. package/src/patient-registration/field/dob/dob.test.tsx +0 -75
  110. package/src/patient-registration/field/field.test.tsx +0 -294
  111. package/src/patient-registration/field/id/id-field.test.tsx +0 -107
  112. package/src/patient-registration/field/person-attributes/coded-attributes.component.tsx +0 -60
  113. package/src/patient-registration/field/person-attributes/coded-person-attribute-field.test.tsx +0 -127
  114. package/src/patient-registration/field/person-attributes/person-attribute-field.test.tsx +0 -187
  115. package/src/patient-registration/field/person-attributes/text-person-attribute-field.test.tsx +0 -88
  116. package/src/patient-registration/form-manager.test.ts +0 -67
  117. package/src/patient-registration/input/basic-input/select/select-input.test.tsx +0 -49
  118. package/src/patient-registration/input/custom-input/autosuggest/autosuggest.test.tsx +0 -132
  119. package/src/patient-registration/input/custom-input/identifier/identifier-input.test.tsx +0 -107
  120. package/src/patient-registration/patient-registration.test.tsx +0 -471
  121. package/src/patient-registration/section/death-info/death-info-section.test.tsx +0 -64
  122. package/src/patient-registration/section/demographics/demographics-section.test.tsx +0 -83
  123. package/src/patient-registration/section/patient-relationships/relationships-section.test.tsx +0 -100
  124. package/src/patient-verification/client-registry-constants.ts +0 -13
  125. package/src/patient-verification/client-registry.component.tsx +0 -66
  126. package/src/patient-verification/client-registry.scss +0 -1
  127. package/src/patient-verification/utils.tsx +0 -56
  128. package/src/patient-verification/verification-modal.scss +0 -20
  129. package/src/patient-verification/verification.component.tsx +0 -48
  130. package/src/root.test.tsx +0 -32
  131. package/src/widgets/cancel-patient-edit.test.tsx +0 -27
  132. package/src/widgets/display-photo.component.tsx +0 -30
  133. package/src/widgets/display-photo.test.tsx +0 -37
  134. package/src/widgets/edit-patient-details-button.test.tsx +0 -41
  135. /package/src/patient-registration/input/custom-input/identifier/{utils.test.ts → utils.testt.ts} +0 -0
  136. /package/src/widgets/{delete-identifier-confirmation-modal.test.tsx → delete-identifier-confirmation-modal.testt.tsx} +0 -0
package/dist/routes.json CHANGED
@@ -1 +1 @@
1
- {"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"webservices.rest":"^2.24.0"},"pages":[{"component":"root","route":"patient-registration","online":true,"offline":true},{"component":"editPatient","routeRegex":"patient\\/([a-zA-Z0-9\\-]+)\\/edit","online":true,"offline":true}],"extensions":[{"component":"addPatientLink","name":"add-patient-action","slot":"top-nav-actions-slot","online":true,"offline":true},{"component":"cancelPatientEditModal","name":"cancel-patient-edit-modal","online":true,"offline":true},{"component":"patientPhoto","name":"patient-photo-widget","slot":"patient-photo-slot","online":true,"offline":true},{"component":"editPatientDetailsButton","name":"edit-patient-details-button","slot":"patient-actions-slot","online":true,"offline":true},{"component":"editPatientDetailsButton","name":"edit-patient-details-button","slot":"patient-search-actions-slot","online":true,"offline":true},{"component":"deleteIdentifierConfirmationModal","name":"delete-identifier-confirmation-modal","online":true,"offline":true},{"name":"client-registry-modal","component":"clientRegistryModal"}],"version":"6.0.1-pre.24"}
1
+ {"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"webservices.rest":"^2.24.0"},"pages":[{"component":"root","route":"patient-registration","online":true,"offline":true},{"component":"editPatient","routeRegex":"patient\\/([a-zA-Z0-9\\-]+)\\/edit","online":true,"offline":true}],"extensions":[{"component":"addPatientLink","name":"add-patient-action","slot":"top-nav-actions-slot","online":true,"offline":true},{"component":"cancelPatientEditModal","name":"cancel-patient-edit-modal","online":true,"offline":true},{"component":"patientPhotoExtension","name":"patient-photo-widget","slot":"patient-photo-slot","online":true,"offline":true},{"component":"editPatientDetailsButton","name":"edit-patient-details-button","slot":"patient-actions-slot","online":true,"offline":true},{"component":"editPatientDetailsButton","name":"edit-patient-details-button","slot":"patient-search-actions-slot","online":true,"offline":true},{"component":"deleteIdentifierConfirmationModal","name":"delete-identifier-confirmation-modal","online":true,"offline":true},{"component":"emptyClientRegistryModal","name":"empty-client-registry-modal","online":true,"offline":true},{"component":"confirmClientRegistryModal","name":"confirm-client-registry-modal","online":true,"offline":true}],"version":"6.0.1-pre.241"}
package/package.json CHANGED
@@ -1,14 +1,14 @@
1
1
  {
2
2
  "name": "@ampath/esm-patient-registration-app",
3
- "version": "6.0.1-pre.24",
3
+ "version": "6.0.1-pre.241",
4
4
  "description": "Patient registration microfrontend for the OpenMRS SPA",
5
5
  "browser": "dist/ampath-esm-patient-registration-app.js",
6
6
  "main": "src/index.ts",
7
7
  "source": true,
8
8
  "license": "MPL-2.0",
9
- "homepage": "https://github.com/ampath/ampath-esm-3.x#readme",
9
+ "homepage": "https://github.com/openmrs/openmrs-esm-patient-management#readme",
10
10
  "scripts": {
11
- "start": "openmrs develop ",
11
+ "start": "openmrs develop",
12
12
  "serve": "webpack serve --mode=development",
13
13
  "debug": "npm run serve",
14
14
  "build": "webpack --mode production",
@@ -18,7 +18,7 @@
18
18
  "test:watch": "cross-env TZ=UTC jest --watch --config jest.config.js --color",
19
19
  "coverage": "yarn test --coverage",
20
20
  "typescript": "tsc",
21
- "extract-translations": "i18next 'src/**/*.component.tsx' 'src/index.ts'"
21
+ "extract-translations": "i18next 'src/**/*.component.tsx' 'src/index.ts' --config ../../tools/i18next-parser.config.js"
22
22
  },
23
23
  "browserslist": [
24
24
  "extends browserslist-config-openmrs"
@@ -31,18 +31,15 @@
31
31
  },
32
32
  "repository": {
33
33
  "type": "git",
34
- "url": "git+https://github.com/ampath/ampath-esm-3.x.git"
34
+ "url": "git+https://github.com/openmrs/openmrs-esm-patient-management.git"
35
35
  },
36
36
  "bugs": {
37
- "url": "https://github.com/ampath/ampath-esm-3.x/issues"
37
+ "url": "https://github.com/openmrs/openmrs-esm-patient-management/issues"
38
38
  },
39
39
  "dependencies": {
40
40
  "@carbon/react": "~1.37.0",
41
- "core-js-pure": "^3.34.0",
42
41
  "formik": "^2.1.5",
43
- "geopattern": "^1.2.3",
44
42
  "lodash-es": "^4.17.15",
45
- "react-avatar": "^5.0.3",
46
43
  "uuid": "^8.3.2",
47
44
  "yup": "^0.29.1"
48
45
  },
@@ -57,5 +54,5 @@
57
54
  "devDependencies": {
58
55
  "webpack": "^5.74.0"
59
56
  },
60
- "stableVersion": "6.0.0"
57
+ "stableVersion": "6.0.1-pre.1.0.6"
61
58
  }
@@ -19,6 +19,11 @@ export interface FieldDefinition {
19
19
  };
20
20
  answerConceptSetUuid?: string;
21
21
  customConceptAnswers?: Array<CustomConceptAnswer>;
22
+ showWhenExpression?: {
23
+ field: string;
24
+ value: string;
25
+ };
26
+ renderType?: string;
22
27
  }
23
28
  export interface CustomConceptAnswer {
24
29
  uuid: string;
@@ -32,6 +37,7 @@ export interface Gender {
32
37
  export interface RegistrationConfig {
33
38
  sections: Array<string>;
34
39
  sectionDefinitions: Array<SectionDefinition>;
40
+
35
41
  fieldDefinitions: Array<FieldDefinition>;
36
42
  fieldConfigurations: {
37
43
  name: {
@@ -60,14 +66,15 @@ export interface RegistrationConfig {
60
66
  };
61
67
  phone: {
62
68
  personAttributeUuid: string;
69
+ validation?: {
70
+ required: boolean;
71
+ matches?: string;
72
+ };
63
73
  };
64
74
  };
65
75
  links: {
66
76
  submitButton: string;
67
77
  };
68
- concepts: {
69
- patientPhotoUuid: string;
70
- };
71
78
  defaultPatientIdentifierTypes: Array<string>;
72
79
  registrationObs: {
73
80
  encounterTypeUuid: string | null;
@@ -309,9 +316,17 @@ export const esmPatientRegistrationSchema = {
309
316
  phone: {
310
317
  personAttributeUuid: {
311
318
  _type: Type.UUID,
312
- _default: '14d4f066-15f5-102d-96e4-000c29c2a5d7',
319
+ _default: '72a759a8-1359-11df-a1f1-0026b9348838',
313
320
  _description: 'The UUID of the phone number person attribute type',
314
321
  },
322
+ validation: {
323
+ required: { _type: Type.Boolean, _default: false },
324
+ matches: {
325
+ _type: Type.String,
326
+ _default: null,
327
+ _description: 'Optional RegEx for testing the validity of the input.',
328
+ },
329
+ },
315
330
  },
316
331
  },
317
332
  links: {
@@ -321,12 +336,6 @@ export const esmPatientRegistrationSchema = {
321
336
  _validators: [validators.isUrlWithTemplateParameters(['patientUuid'])],
322
337
  },
323
338
  },
324
- concepts: {
325
- patientPhotoUuid: {
326
- _type: Type.ConceptUuid,
327
- _default: '736e8771-e501-4615-bfa7-570c03f4bef5',
328
- },
329
- },
330
339
  defaultPatientIdentifierTypes: {
331
340
  _type: Type.Array,
332
341
  _elements: {
package/src/index.ts CHANGED
@@ -4,9 +4,8 @@ import { moduleName, patientRegistration } from './constants';
4
4
  import { setupOffline } from './offline';
5
5
  import rootComponent from './root.component';
6
6
  import addPatientLinkComponent from './add-patient-link';
7
- import patientPhotoComponent from './widgets/display-photo.component';
8
7
  import editPatientDetailsButtonComponent from './widgets/edit-patient-details-button.component';
9
- import VerificationModal from './patient-verification/verification.component';
8
+ import { PatientPhotoExtension } from './patient-photo.extension';
10
9
 
11
10
  export const importTranslation = require.context('../translations', false, /.json$/, 'lazy');
12
11
 
@@ -56,7 +55,7 @@ export const cancelPatientEditModal = getAsyncLifecycle(
56
55
  options,
57
56
  );
58
57
 
59
- export const patientPhoto = getSyncLifecycle(patientPhotoComponent, options);
58
+ export const patientPhotoExtension = getSyncLifecycle(PatientPhotoExtension, options);
60
59
 
61
60
  export const editPatientDetailsButton = getSyncLifecycle(editPatientDetailsButtonComponent, {
62
61
  featureName: 'edit-patient-details',
@@ -68,4 +67,12 @@ export const deleteIdentifierConfirmationModal = getAsyncLifecycle(
68
67
  options,
69
68
  );
70
69
 
71
- export const clientRegistryModal = getSyncLifecycle(VerificationModal, options);
70
+ export const confirmClientRegistryModal = getAsyncLifecycle(
71
+ () => import('./patient-verification/verification-modal/confirm-prompt.component'),
72
+ options,
73
+ );
74
+
75
+ export const emptyClientRegistryModal = getAsyncLifecycle(
76
+ () => import('./patient-verification/verification-modal/empty-prompt.component'),
77
+ options,
78
+ );
@@ -2,7 +2,7 @@ import React from 'react';
2
2
  import find from 'lodash-es/find';
3
3
  import camelCase from 'lodash-es/camelCase';
4
4
  import escapeRegExp from 'lodash-es/escapeRegExp';
5
- import { getConfig, messageOmrsServiceWorker, openmrsFetch, type Session } from '@openmrs/esm-framework';
5
+ import { getConfig, messageOmrsServiceWorker, openmrsFetch, restBaseUrl, type Session } from '@openmrs/esm-framework';
6
6
  import type {
7
7
  PatientIdentifierType,
8
8
  FetchedPatientIdentifierType,
@@ -20,17 +20,17 @@ export interface Resources {
20
20
  export const ResourcesContext = React.createContext<Resources>(null);
21
21
 
22
22
  export async function fetchCurrentSession(): Promise<Session> {
23
- const { data } = await cacheAndFetch<Session>('/ws/rest/v1/session');
23
+ const { data } = await cacheAndFetch<Session>(`${restBaseUrl}/session`);
24
24
  return data;
25
25
  }
26
26
 
27
27
  export async function fetchAddressTemplate() {
28
- const { data } = await cacheAndFetch<AddressTemplate>('/ws/rest/v1/addresstemplate');
28
+ const { data } = await cacheAndFetch<AddressTemplate>(`${restBaseUrl}/addresstemplate`);
29
29
  return data;
30
30
  }
31
31
 
32
32
  export async function fetchAllRelationshipTypes() {
33
- const { data } = await cacheAndFetch('/ws/rest/v1/relationshiptype?v=default');
33
+ const { data } = await cacheAndFetch(`${restBaseUrl}/relationshiptype?v=default`);
34
34
  return data;
35
35
  }
36
36
 
@@ -59,11 +59,11 @@ async function fetchFieldDefinitionType(fieldDefinition) {
59
59
  let apiUrl = '';
60
60
 
61
61
  if (fieldDefinition.type === 'person attribute') {
62
- apiUrl = `/ws/rest/v1/personattributetype/${fieldDefinition.uuid}`;
62
+ apiUrl = `${restBaseUrl}/personattributetype/${fieldDefinition.uuid}`;
63
63
  }
64
64
 
65
65
  if (fieldDefinition.answerConceptSetUuid) {
66
- await cacheAndFetch(`/ws/rest/v1/concept/${fieldDefinition.answerConceptSetUuid}`);
66
+ await cacheAndFetch(`${restBaseUrl}/concept/${fieldDefinition.answerConceptSetUuid}`);
67
67
  }
68
68
  const { data } = await cacheAndFetch(apiUrl);
69
69
  return data;
@@ -93,8 +93,10 @@ export async function fetchPatientIdentifierTypesWithSources(): Promise<Array<Pa
93
93
 
94
94
  async function fetchPatientIdentifierTypes(): Promise<Array<FetchedPatientIdentifierType>> {
95
95
  const [patientIdentifierTypesResponse, primaryIdentifierTypeResponse] = await Promise.all([
96
- cacheAndFetch('/ws/rest/v1/patientidentifiertype?v=custom:(display,uuid,name,format,required,uniquenessBehavior)'),
97
- cacheAndFetch('/ws/rest/v1/metadatamapping/termmapping?v=full&code=emr.primaryIdentifierType'),
96
+ cacheAndFetch(
97
+ `${restBaseUrl}/patientidentifiertype?v=custom:(display,uuid,name,format,required,uniquenessBehavior)`,
98
+ ),
99
+ cacheAndFetch(`${restBaseUrl}/metadatamapping/termmapping?v=full&code=emr.primaryIdentifierType`),
98
100
  ]);
99
101
 
100
102
  if (patientIdentifierTypesResponse.ok) {
@@ -103,14 +105,7 @@ async function fetchPatientIdentifierTypes(): Promise<Array<FetchedPatientIdenti
103
105
 
104
106
  const primaryIdentifierTypeUuid = primaryIdentifierTypeResponse?.data?.results?.[0]?.metadataUuid;
105
107
 
106
- let identifierTypes = primaryIdentifierTypeResponse?.ok
107
- ? [
108
- mapPatientIdentifierType(
109
- patientIdentifierTypes?.find((type) => type.uuid === primaryIdentifierTypeUuid),
110
- true,
111
- ),
112
- ]
113
- : [];
108
+ let identifierTypes = [];
114
109
 
115
110
  patientIdentifierTypes.forEach((type) => {
116
111
  if (type.uuid !== primaryIdentifierTypeUuid) {
@@ -124,11 +119,11 @@ async function fetchPatientIdentifierTypes(): Promise<Array<FetchedPatientIdenti
124
119
  }
125
120
 
126
121
  async function fetchIdentifierSources(identifierType: string) {
127
- return await cacheAndFetch(`/ws/rest/v1/idgen/identifiersource?v=default&identifierType=${identifierType}`);
122
+ return await cacheAndFetch(`${restBaseUrl}/idgen/identifiersource?v=default&identifierType=${identifierType}`);
128
123
  }
129
124
 
130
125
  async function fetchAutoGenerationOptions(abortController?: AbortController) {
131
- return await cacheAndFetch(`/ws/rest/v1/idgen/autogenerationoption?v=full`);
126
+ return await cacheAndFetch(`${restBaseUrl}/idgen/autogenerationoption?v=full`);
132
127
  }
133
128
 
134
129
  async function cacheAndFetch<T = any>(url?: string) {
package/src/offline.ts CHANGED
@@ -1,7 +1,9 @@
1
1
  import {
2
+ fhirBaseUrl,
2
3
  makeUrl,
3
4
  messageOmrsServiceWorker,
4
5
  navigate,
6
+ restBaseUrl,
5
7
  setupDynamicOfflineDataHandler,
6
8
  setupOfflineSync,
7
9
  type SyncProcessOptions,
@@ -54,10 +56,10 @@ export function setupOffline() {
54
56
 
55
57
  function getPatientUrlsToBeCached(patientUuid: string) {
56
58
  return [
57
- `/ws/fhir2/R4/Patient/${patientUuid}`,
58
- `/ws/rest/v1/relationship?v=${personRelationshipRepresentation}&person=${patientUuid}`,
59
- `/ws/rest/v1/person/${patientUuid}/attribute`,
60
- `/ws/rest/v1/patient/${patientUuid}/identifier?v=custom:(uuid,identifier,identifierType:(uuid,required,name),preferred)`,
59
+ `${fhirBaseUrl}/Patient/${patientUuid}`,
60
+ `${restBaseUrl}/relationship?v=${personRelationshipRepresentation}&person=${patientUuid}`,
61
+ `${restBaseUrl}/person/${patientUuid}/attribute`,
62
+ `${restBaseUrl}/patient/${patientUuid}/identifier?v=custom:(uuid,identifier,identifierType:(uuid,required,name),preferred)`,
61
63
  ].map((url) => window.origin + makeUrl(url));
62
64
  }
63
65
 
@@ -0,0 +1,9 @@
1
+ import React from 'react';
2
+ import { PatientPhoto, type PatientPhotoProps } from '@openmrs/esm-framework';
3
+
4
+ export function PatientPhotoExtension(props: PatientPhotoProps) {
5
+ console.warn(
6
+ 'Using the patient-photo extension (or patient-photo-slot slot) is deprecated. Please use the PatientPhoto component from @openmrs/esm-framework.',
7
+ );
8
+ return <PatientPhoto {...props} />;
9
+ }
@@ -21,6 +21,7 @@ export const AddressField: React.FC<AddressFieldProps> = ({ fieldDefinition }) =
21
21
  <Input
22
22
  id={fieldDefinition.id}
23
23
  labelText={t(`${fieldDefinition.label}`, `${fieldDefinition.label}`)}
24
+ required={fieldDefinition?.validation?.required ?? false}
24
25
  {...field}
25
26
  />
26
27
  );
@@ -4,6 +4,7 @@ import { type RegistrationConfig } from '../../config-schema';
4
4
  import { AddressField } from './address/custom-address-field.component';
5
5
  import { ObsField } from './obs/obs-field.component';
6
6
  import { PersonAttributeField } from './person-attributes/person-attribute-field.component';
7
+ import { useField } from 'formik';
7
8
 
8
9
  export interface CustomFieldProps {
9
10
  name: string;
@@ -13,6 +14,11 @@ export function CustomField({ name }: CustomFieldProps) {
13
14
  const config = useConfig() as RegistrationConfig;
14
15
  const fieldDefinition = config.fieldDefinitions.filter((def) => def.id == name)[0];
15
16
 
17
+ const [{ value }] = useField(`attributes.${fieldDefinition.showWhenExpression?.field}`);
18
+ if (fieldDefinition.showWhenExpression && value !== fieldDefinition.showWhenExpression.value) {
19
+ return null;
20
+ }
21
+
16
22
  if (fieldDefinition.type === 'person attribute') {
17
23
  return <PersonAttributeField fieldDefinition={fieldDefinition} />;
18
24
  } else if (fieldDefinition.type === 'obs') {
@@ -1,10 +1,10 @@
1
1
  import React, { type ChangeEvent, useCallback, useContext } from 'react';
2
- import { ContentSwitcher, DatePicker, DatePickerInput, Layer, Switch, TextInput } from '@carbon/react';
2
+ import { ContentSwitcher, Layer, Switch, TextInput } from '@carbon/react';
3
3
  import { useTranslation } from 'react-i18next';
4
4
  import { useField } from 'formik';
5
- import { generateFormatting } from '../../date-util';
5
+ import { type CalendarDate, getLocalTimeZone } from '@internationalized/date';
6
6
  import { PatientRegistrationContext } from '../../patient-registration-context';
7
- import { useConfig } from '@openmrs/esm-framework';
7
+ import { OpenmrsDatePicker, useConfig } from '@openmrs/esm-framework';
8
8
  import { type RegistrationConfig } from '../../../config-schema';
9
9
  import styles from '../field.scss';
10
10
 
@@ -32,7 +32,6 @@ export const DobField: React.FC = () => {
32
32
  const [yearsEstimated, yearsEstimateMeta] = useField('yearsEstimated');
33
33
  const [monthsEstimated, monthsEstimateMeta] = useField('monthsEstimated');
34
34
  const { setFieldValue } = useContext(PatientRegistrationContext);
35
- const { format, placeHolder, dateFormat } = generateFormatting(['d', 'm', 'Y'], '/');
36
35
  const today = new Date();
37
36
 
38
37
  const onToggle = useCallback(
@@ -46,8 +45,8 @@ export const DobField: React.FC = () => {
46
45
  );
47
46
 
48
47
  const onDateChange = useCallback(
49
- (birthdate: Date[]) => {
50
- setFieldValue('birthdate', birthdate[0]);
48
+ (birthdate: CalendarDate) => {
49
+ setFieldValue('birthdate', birthdate?.toDate(getLocalTimeZone()));
51
50
  },
52
51
  [setFieldValue],
53
52
  );
@@ -101,55 +100,52 @@ export const DobField: React.FC = () => {
101
100
  <Layer>
102
101
  {!dobUnknown ? (
103
102
  <div className={styles.dobField}>
104
- <DatePicker dateFormat={dateFormat} datePickerType="single" onChange={onDateChange} maxDate={format(today)}>
105
- <DatePickerInput
106
- id="birthdate"
107
- {...birthdate}
108
- placeholder={placeHolder}
109
- labelText={t('dateOfBirthLabelText', 'Date of Birth')}
110
- invalid={!!(birthdateMeta.touched && birthdateMeta.error)}
111
- invalidText={birthdateMeta.error && t(birthdateMeta.error)}
112
- value={format(birthdate.value)}
113
- />
114
- </DatePicker>
103
+ <OpenmrsDatePicker
104
+ id="birthdate"
105
+ {...birthdate}
106
+ onChange={onDateChange}
107
+ maxDate={today}
108
+ labelText={t('dateOfBirthLabelText', 'Date of Birth')}
109
+ isInvalid={!!(birthdateMeta.touched && birthdateMeta.error)}
110
+ value={birthdate.value}
111
+ />
112
+ {!!(birthdateMeta.touched && birthdateMeta.error) && (
113
+ <div className={styles.radioFieldError}>{birthdateMeta.error && t(birthdateMeta.error)}</div>
114
+ )}
115
115
  </div>
116
116
  ) : (
117
117
  <div className={styles.grid}>
118
118
  <div className={styles.dobField}>
119
- <Layer>
120
- <TextInput
121
- id="yearsEstimated"
122
- type="number"
123
- name={yearsEstimated.name}
124
- onChange={onEstimatedYearsChange}
125
- labelText={t('estimatedAgeInYearsLabelText', 'Estimated age in years')}
126
- invalid={!!(yearsEstimateMeta.touched && yearsEstimateMeta.error)}
127
- invalidText={yearsEstimateMeta.error && t(yearsEstimateMeta.error)}
128
- value={yearsEstimated.value}
129
- min={0}
130
- required
131
- {...yearsEstimated}
132
- onBlur={updateBirthdate}
133
- />
134
- </Layer>
119
+ <TextInput
120
+ id="yearsEstimated"
121
+ type="number"
122
+ name={yearsEstimated.name}
123
+ onChange={onEstimatedYearsChange}
124
+ labelText={t('estimatedAgeInYearsLabelText', 'Estimated age in years')}
125
+ invalid={!!(yearsEstimateMeta.touched && yearsEstimateMeta.error)}
126
+ invalidText={yearsEstimateMeta.error && t(yearsEstimateMeta.error)}
127
+ value={yearsEstimated.value}
128
+ min={0}
129
+ required
130
+ {...yearsEstimated}
131
+ onBlur={updateBirthdate}
132
+ />
135
133
  </div>
136
134
  <div className={styles.dobField}>
137
- <Layer>
138
- <TextInput
139
- id="monthsEstimated"
140
- type="number"
141
- name={monthsEstimated.name}
142
- onChange={onEstimatedMonthsChange}
143
- labelText={t('estimatedAgeInMonthsLabelText', 'Estimated age in months')}
144
- invalid={!!(monthsEstimateMeta.touched && monthsEstimateMeta.error)}
145
- invalidText={monthsEstimateMeta.error && t(monthsEstimateMeta.error)}
146
- value={monthsEstimated.value}
147
- min={0}
148
- {...monthsEstimated}
149
- required={!yearsEstimateMeta.value}
150
- onBlur={updateBirthdate}
151
- />
152
- </Layer>
135
+ <TextInput
136
+ id="monthsEstimated"
137
+ type="number"
138
+ name={monthsEstimated.name}
139
+ onChange={onEstimatedMonthsChange}
140
+ labelText={t('estimatedAgeInMonthsLabelText', 'Estimated age in months')}
141
+ invalid={!!(monthsEstimateMeta.touched && monthsEstimateMeta.error)}
142
+ invalidText={monthsEstimateMeta.error && t(monthsEstimateMeta.error)}
143
+ value={monthsEstimated.value}
144
+ min={0}
145
+ {...monthsEstimated}
146
+ required={!yearsEstimateMeta.value}
147
+ onBlur={updateBirthdate}
148
+ />
153
149
  </div>
154
150
  </div>
155
151
  )}
@@ -1,11 +1,11 @@
1
- import { type FetchResponse, openmrsFetch, showSnackbar } from '@openmrs/esm-framework';
1
+ import { type FetchResponse, openmrsFetch, showSnackbar, restBaseUrl } from '@openmrs/esm-framework';
2
2
  import useSWRImmutable from 'swr/immutable';
3
3
  import { type ConceptAnswers, type ConceptResponse } from '../patient-registration.types';
4
4
 
5
5
  export function useConcept(conceptUuid: string): { data: ConceptResponse; isLoading: boolean } {
6
6
  const shouldFetch = typeof conceptUuid === 'string' && conceptUuid !== '';
7
7
  const { data, error, isLoading } = useSWRImmutable<FetchResponse<ConceptResponse>, Error>(
8
- shouldFetch ? `/ws/rest/v1/concept/${conceptUuid}` : null,
8
+ shouldFetch ? `${restBaseUrl}/concept/${conceptUuid}` : null,
9
9
  openmrsFetch,
10
10
  );
11
11
  if (error) {
@@ -21,7 +21,7 @@ export function useConcept(conceptUuid: string): { data: ConceptResponse; isLoad
21
21
  export function useConceptAnswers(conceptUuid: string): { data: Array<ConceptAnswers>; isLoading: boolean } {
22
22
  const shouldFetch = typeof conceptUuid === 'string' && conceptUuid !== '';
23
23
  const { data, error, isLoading } = useSWRImmutable<FetchResponse<ConceptResponse>, Error>(
24
- shouldFetch ? `/ws/rest/v1/concept/${conceptUuid}` : null,
24
+ shouldFetch ? `${restBaseUrl}/concept/${conceptUuid}` : null,
25
25
  openmrsFetch,
26
26
  );
27
27
  if (error) {
@@ -158,7 +158,7 @@ function CodedObsField({ concept, answerConceptSetUuid, label, required, customC
158
158
  const fieldName = `obs.${concept.uuid}`;
159
159
 
160
160
  const { data: conceptAnswers, isLoading: isLoadingConceptAnswers } = useConceptAnswers(
161
- customConceptAnswers.length ? '' : answerConceptSetUuid ?? concept.uuid,
161
+ customConceptAnswers.length ? '' : (answerConceptSetUuid ?? concept.uuid),
162
162
  );
163
163
 
164
164
  const answers = useMemo(
@@ -14,6 +14,7 @@ export interface CodedPersonAttributeFieldProps {
14
14
  answerConceptSetUuid: string;
15
15
  label?: string;
16
16
  customConceptAnswers: Array<{ uuid: string; label?: string }>;
17
+ required: boolean;
17
18
  }
18
19
 
19
20
  export function CodedPersonAttributeField({
@@ -22,10 +23,12 @@ export function CodedPersonAttributeField({
22
23
  answerConceptSetUuid,
23
24
  label,
24
25
  customConceptAnswers,
26
+ required,
25
27
  }: CodedPersonAttributeFieldProps) {
26
28
  const { data: conceptAnswers, isLoading: isLoadingConceptAnswers } = useConceptAnswers(
27
29
  customConceptAnswers.length ? '' : answerConceptSetUuid,
28
30
  );
31
+
29
32
  const { t } = useTranslation();
30
33
  const fieldName = `attributes.${personAttributeType.uuid}`;
31
34
  const [error, setError] = useState(false);
@@ -99,6 +102,7 @@ export function CodedPersonAttributeField({
99
102
  name={`person-attribute-${personAttributeType.uuid}`}
100
103
  labelText={label ?? personAttributeType?.display}
101
104
  invalid={errors[fieldName] && touched[fieldName]}
105
+ required={required}
102
106
  {...field}>
103
107
  <SelectItem value={''} text={t('selectAnOption', 'Select an option')} />
104
108
  {answers.map((answer) => (
@@ -0,0 +1,56 @@
1
+ import React from 'react';
2
+ import { Field } from 'formik';
3
+ import { Layer, Select, SelectItem } from '@carbon/react';
4
+ import { type PersonAttributeTypeResponse } from '../../patient-registration.types';
5
+ import { useTranslation } from 'react-i18next';
6
+ import styles from './../field.scss';
7
+ import classNames from 'classnames';
8
+
9
+ type CustomPersonAttributeFieldProps = {
10
+ id: string;
11
+ personAttributeType: PersonAttributeTypeResponse;
12
+ answerConceptSetUuid: string;
13
+ label?: string;
14
+ customConceptAnswers: Array<{ uuid: string; label?: string }>;
15
+ required: boolean;
16
+ };
17
+
18
+ const CustomPersonAttributeField: React.FC<CustomPersonAttributeFieldProps> = ({
19
+ personAttributeType,
20
+ required,
21
+ id,
22
+ label,
23
+ customConceptAnswers,
24
+ }) => {
25
+ const { t } = useTranslation();
26
+ const fieldName = `attributes.${personAttributeType.uuid}`;
27
+
28
+ return (
29
+ <div className={classNames(styles.customField, styles.halfWidthInDesktopView)}>
30
+ <Layer>
31
+ <Field name={fieldName}>
32
+ {({ field, form: { touched, errors }, meta }) => {
33
+ return (
34
+ <>
35
+ <Select
36
+ id={id}
37
+ name={`person-attribute-${personAttributeType.uuid}`}
38
+ labelText={label ?? personAttributeType?.display}
39
+ invalid={errors[fieldName] && touched[fieldName]}
40
+ required={required}
41
+ {...field}>
42
+ <SelectItem value={''} text={t('selectAnOption', 'Select an option')} />
43
+ {customConceptAnswers.map((answer) => (
44
+ <SelectItem key={answer.uuid} value={answer.uuid} text={answer.uuid} />
45
+ ))}
46
+ </Select>
47
+ </>
48
+ );
49
+ }}
50
+ </Field>
51
+ </Layer>
52
+ </div>
53
+ );
54
+ };
55
+
56
+ export default CustomPersonAttributeField;
@@ -6,6 +6,7 @@ import { usePersonAttributeType } from './person-attributes.resource';
6
6
  import { TextPersonAttributeField } from './text-person-attribute-field.component';
7
7
  import { useTranslation } from 'react-i18next';
8
8
  import styles from '../field.scss';
9
+ import CustomPersonAttributeField from './custom-person-attribute-field.component';
9
10
 
10
11
  export interface PersonAttributeFieldProps {
11
12
  fieldDefinition: FieldDefinition;
@@ -22,13 +23,26 @@ export function PersonAttributeField({ fieldDefinition }: PersonAttributeFieldPr
22
23
  switch (personAttributeType.format) {
23
24
  case 'java.lang.String':
24
25
  return (
25
- <TextPersonAttributeField
26
- personAttributeType={personAttributeType}
27
- validationRegex={fieldDefinition.validation?.matches ?? ''}
28
- label={fieldDefinition.label}
29
- required={fieldDefinition.validation?.required ?? false}
30
- id={fieldDefinition?.id}
31
- />
26
+ <>
27
+ {fieldDefinition.renderType === 'select' ? (
28
+ <CustomPersonAttributeField
29
+ personAttributeType={personAttributeType}
30
+ answerConceptSetUuid={fieldDefinition.answerConceptSetUuid}
31
+ label={fieldDefinition.label}
32
+ id={fieldDefinition?.id}
33
+ customConceptAnswers={fieldDefinition.customConceptAnswers ?? []}
34
+ required={fieldDefinition.validation?.required ?? false}
35
+ />
36
+ ) : (
37
+ <TextPersonAttributeField
38
+ personAttributeType={personAttributeType}
39
+ validationRegex={fieldDefinition.validation?.matches ?? ''}
40
+ label={fieldDefinition.label}
41
+ required={fieldDefinition.validation?.required ?? false}
42
+ id={fieldDefinition?.id}
43
+ />
44
+ )}
45
+ </>
32
46
  );
33
47
  case 'org.openmrs.Concept':
34
48
  return (
@@ -38,6 +52,7 @@ export function PersonAttributeField({ fieldDefinition }: PersonAttributeFieldPr
38
52
  label={fieldDefinition.label}
39
53
  id={fieldDefinition?.id}
40
54
  customConceptAnswers={fieldDefinition.customConceptAnswers ?? []}
55
+ required={fieldDefinition.validation?.required ?? false}
41
56
  />
42
57
  );
43
58
  default:
@@ -1,4 +1,4 @@
1
- import { type FetchResponse, openmrsFetch } from '@openmrs/esm-framework';
1
+ import { type FetchResponse, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
2
2
  import useSWRImmutable from 'swr/immutable';
3
3
  import { type PersonAttributeTypeResponse } from '../../patient-registration.types';
4
4
 
@@ -8,7 +8,7 @@ export function usePersonAttributeType(personAttributeTypeUuid: string): {
8
8
  error: any;
9
9
  } {
10
10
  const { data, error, isLoading } = useSWRImmutable<FetchResponse<PersonAttributeTypeResponse>>(
11
- `/ws/rest/v1/personattributetype/${personAttributeTypeUuid}`,
11
+ `${restBaseUrl}/personattributetype/${personAttributeTypeUuid}`,
12
12
  openmrsFetch,
13
13
  );
14
14
 
@@ -10,6 +10,7 @@ export function PhoneField() {
10
10
  id: 'phone',
11
11
  type: 'person attribute',
12
12
  uuid: config.fieldConfigurations.phone.personAttributeUuid,
13
+ validation: config.fieldConfigurations.phone.validation,
13
14
  showHeading: false,
14
15
  };
15
16
  return <PersonAttributeField fieldDefinition={fieldDefinition} />;