@kenyaemr/esm-patient-registration-app 4.5.5 → 4.5.7

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 (49) hide show
  1. package/dist/130.js +1 -1
  2. package/dist/130.js.map +1 -1
  3. package/dist/218.js +1 -1
  4. package/dist/319.js +1 -1
  5. package/dist/330.js +1 -1
  6. package/dist/348.js +1 -1
  7. package/dist/460.js +1 -0
  8. package/dist/520.js +2 -0
  9. package/dist/520.js.LICENSE.txt +14 -0
  10. package/dist/520.js.map +1 -0
  11. package/dist/537.js +1 -1
  12. package/dist/68.js +1 -1
  13. package/dist/68.js.map +1 -1
  14. package/dist/693.js +1 -1
  15. package/dist/693.js.map +1 -1
  16. package/dist/735.js +1 -1
  17. package/dist/742.js +1 -1
  18. package/dist/kenyaemr-esm-patient-registration-app.js +1 -1
  19. package/dist/kenyaemr-esm-patient-registration-app.js.buildmanifest.json +79 -57
  20. package/dist/kenyaemr-esm-patient-registration-app.js.map +1 -1
  21. package/dist/main.js +1 -1
  22. package/dist/main.js.map +1 -1
  23. package/dist/routes.json +1 -1
  24. package/package.json +1 -1
  25. package/src/offline.resources.ts +37 -2
  26. package/src/offline.ts +3 -2
  27. package/src/patient-registration/field/__mocks__/identifier-types.mock.ts +76 -0
  28. package/src/patient-registration/field/__mocks__/identifiers.mock.ts +27 -0
  29. package/src/patient-registration/field/address/address-field.component.tsx +12 -6
  30. package/src/patient-registration/field/address/address-hierarchy-levels.component.tsx +4 -1
  31. package/src/patient-registration/field/dob/dob.component.tsx +17 -14
  32. package/src/patient-registration/field/dob/dob.test.tsx +13 -0
  33. package/src/patient-registration/field/id/id-field.component.tsx +3 -3
  34. package/src/patient-registration/field/id/id-field.test.tsx +105 -0
  35. package/src/patient-registration/field/obs/obs-field.component.tsx +19 -4
  36. package/src/patient-registration/form-manager.ts +3 -4
  37. package/src/patient-registration/patient-registration-hooks.ts +6 -4
  38. package/src/patient-registration/patient-registration.test.tsx +18 -3
  39. package/src/patient-registration/patient-registration.types.tsx +2 -2
  40. package/src/patient-registration/section/demographics/demographics-section.test.tsx +13 -0
  41. package/src/patient-registration/section/patient-relationships/relationships-section.component.tsx +3 -5
  42. package/src/patient-registration/ui-components/overlay/index.tsx +1 -1
  43. package/src/root.component.tsx +1 -1
  44. package/src/routes.json +3 -1
  45. package/translations/ar.json +84 -0
  46. package/translations/es.json +80 -80
  47. package/dist/208.js +0 -2
  48. package/dist/208.js.LICENSE.txt +0 -27
  49. package/dist/208.js.map +0 -1
package/src/offline.ts CHANGED
@@ -4,12 +4,12 @@ import {
4
4
  navigate,
5
5
  setupDynamicOfflineDataHandler,
6
6
  setupOfflineSync,
7
- subscribePrecacheStaticDependencies,
8
7
  SyncProcessOptions,
9
8
  } from '@openmrs/esm-framework';
10
9
  import { patientRegistration, personRelationshipRepresentation } from './constants';
11
10
  import {
12
11
  fetchAddressTemplate,
12
+ fetchAllFieldDefinitionTypes,
13
13
  fetchAllRelationshipTypes,
14
14
  fetchCurrentSession,
15
15
  fetchPatientIdentifierTypesWithSources,
@@ -24,7 +24,7 @@ export function setupOffline() {
24
24
  },
25
25
  });
26
26
 
27
- subscribePrecacheStaticDependencies(precacheStaticAssets);
27
+ precacheStaticAssets();
28
28
 
29
29
  setupDynamicOfflineDataHandler({
30
30
  id: 'esm-patient-registration-app:patient',
@@ -66,6 +66,7 @@ async function precacheStaticAssets() {
66
66
  fetchCurrentSession(),
67
67
  fetchAddressTemplate(),
68
68
  fetchAllRelationshipTypes(),
69
+ fetchAllFieldDefinitionTypes(),
69
70
  fetchPatientIdentifierTypesWithSources(),
70
71
  ]);
71
72
  }
@@ -0,0 +1,76 @@
1
+ export const mockedIdentifierTypes = [
2
+ {
3
+ fieldName: 'openMrsId',
4
+ format: null,
5
+ identifierSources: [
6
+ {
7
+ uuid: '8549f706-7e85-4c1d-9424-217d50a2988b',
8
+ name: 'Generator for OpenMRS ID',
9
+ description: 'Generator for OpenMRS ID',
10
+ baseCharacterSet: '0123456789ACDEFGHJKLMNPRTUVWXY',
11
+ prefix: '',
12
+ autoGenerationOption: {
13
+ manualEntryEnabled: false,
14
+ automaticGenerationEnabled: true,
15
+ },
16
+ },
17
+ {
18
+ uuid: '01af8526-cea4-4175-aa90-340acb411771',
19
+ name: 'Generator 2 for OpenMRS ID',
20
+ description: 'Generator 2 for OpenMRS ID',
21
+ baseCharacterSet: '0123456789ACDEFGHJKLMNPRTUVWXY',
22
+ prefix: '',
23
+ autoGenerationOption: {
24
+ manualEntryEnabled: true,
25
+ automaticGenerationEnabled: true,
26
+ },
27
+ },
28
+ ],
29
+ isPrimary: true,
30
+ name: 'OpenMRS ID',
31
+ required: true,
32
+ uniquenessBehavior: 'UNIQUE',
33
+ uuid: '05a29f94-c0ed-11e2-94be-8c13b969e334',
34
+ autoGenerationSource: null,
35
+ },
36
+ {
37
+ fieldName: 'idCard',
38
+ format: null,
39
+ identifierSources: [],
40
+ isPrimary: false,
41
+ name: 'ID Card',
42
+ required: false,
43
+ uniquenessBehavior: 'UNIQUE',
44
+ uuid: 'b4143563-16cd-4439-b288-f83d61670fc8',
45
+ },
46
+ {
47
+ fieldName: 'legacyId',
48
+ format: null,
49
+ identifierSources: [],
50
+ isPrimary: false,
51
+ name: 'Legacy ID',
52
+ required: false,
53
+ uniquenessBehavior: null,
54
+ uuid: '22348099-3873-459e-a32e-d93b17eda533',
55
+ },
56
+ {
57
+ fieldName: 'oldIdentificationNumber',
58
+ format: '',
59
+ identifierSources: [],
60
+ isPrimary: false,
61
+ name: 'Old Identification Number',
62
+ required: false,
63
+ uniquenessBehavior: null,
64
+ uuid: '8d79403a-c2cc-11de-8d13-0010c6dffd0f',
65
+ },
66
+ {
67
+ fieldName: 'openMrsIdentificationNumber',
68
+ format: '',
69
+ identifierSources: [],
70
+ isPrimary: false,
71
+ name: 'OpenMRS Identification Number',
72
+ required: false,
73
+ uniquenessBehavior: null,
74
+ uuid: '8d793bee-c2cc-11de-8d13-0010c6dffd0f',
75
+ },
76
+ ];
@@ -0,0 +1,27 @@
1
+ export const openmrsID = {
2
+ name: 'OpenMRS ID',
3
+ fieldName: 'openMrsId',
4
+ required: true,
5
+ uuid: '05a29f94-c0ed-11e2-94be-8c13b969e334',
6
+ format: null,
7
+ isPrimary: true,
8
+ identifierSources: [
9
+ {
10
+ uuid: '691eed12-c0f1-11e2-94be-8c13b969e334',
11
+ name: 'Generator 1 for OpenMRS ID',
12
+ autoGenerationOption: {
13
+ manualEntryEnabled: false,
14
+ automaticGenerationEnabled: true,
15
+ },
16
+ },
17
+ {
18
+ uuid: '01af8526-cea4-4175-aa90-340acb411771',
19
+ name: 'Generator 2 for OpenMRS ID',
20
+ autoGenerationOption: {
21
+ manualEntryEnabled: true,
22
+ automaticGenerationEnabled: true,
23
+ },
24
+ },
25
+ ],
26
+ autoGenerationSource: null,
27
+ };
@@ -22,14 +22,18 @@ export const AddressComponent: React.FC = () => {
22
22
  if (!addressTemplate?.lines) {
23
23
  return [];
24
24
  }
25
+
25
26
  const allFields = addressTemplate?.lines?.flat();
26
27
  const fields = allFields?.filter(({ isToken }) => isToken === 'IS_ADDR_TOKEN');
27
-
28
- return fields.map(({ displayText, codeName }) => ({
29
- id: codeName,
30
- name: codeName,
31
- label: displayText,
32
- }));
28
+ const allRequiredFields = Object.fromEntries(addressTemplate?.requiredElements?.map((curr) => [curr, curr]) || []);
29
+ return fields.map(({ displayText, codeName }) => {
30
+ return {
31
+ id: codeName,
32
+ name: codeName,
33
+ label: displayText,
34
+ required: Boolean(allRequiredFields[codeName]),
35
+ };
36
+ });
33
37
  }, [addressTemplate]);
34
38
 
35
39
  const { t } = useTranslation();
@@ -83,6 +87,7 @@ export const AddressComponent: React.FC = () => {
83
87
  labelText={t(attributes.label)}
84
88
  id={attributes.name}
85
89
  selected={selected}
90
+ required={attributes.required}
86
91
  />
87
92
  ))}
88
93
  </AddressComponentContainer>
@@ -123,6 +128,7 @@ export const AddressComponent: React.FC = () => {
123
128
  labelText={t(attributes.label)}
124
129
  id={attributes.name}
125
130
  selected={selected}
131
+ required={attributes.required}
126
132
  />
127
133
  ))
128
134
  )}
@@ -28,6 +28,7 @@ interface AddressComboBoxProps {
28
28
  name: string;
29
29
  value: string;
30
30
  label: string;
31
+ required?: boolean;
31
32
  };
32
33
  }
33
34
 
@@ -36,6 +37,7 @@ const AddressComboBox: React.FC<AddressComboBoxProps> = ({ attribute }) => {
36
37
  const [field, meta, { setValue }] = useField(`address.${attribute.name}`);
37
38
  const { fetchEntriesForField, searchString, updateChildElements } = useAddressEntryFetchConfig(attribute.name);
38
39
  const { entries } = useAddressEntries(fetchEntriesForField, searchString);
40
+ const label = t(attribute.label) + (attribute?.required ? '' : ` (${t('optional', 'optional')})`);
39
41
 
40
42
  const handleInputChange = useCallback(
41
43
  (newValue) => {
@@ -62,7 +64,8 @@ const AddressComboBox: React.FC<AddressComboBoxProps> = ({ attribute }) => {
62
64
  fieldProps={{
63
65
  ...field,
64
66
  id: attribute.name,
65
- labelText: `${t(attribute.label)} (${t('optional', 'optional')})`,
67
+ labelText: label,
68
+ required: attribute?.required,
66
69
  }}
67
70
  handleInputChange={handleInputChange}
68
71
  />
@@ -1,10 +1,10 @@
1
1
  import React, { 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
5
  import { generateFormatting } from '../../date-util';
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 { RegistrationConfig } from '../../../config-schema';
9
9
  import styles from '../field.scss';
10
10
 
@@ -42,7 +42,7 @@ export const DobField: React.FC = () => {
42
42
  setFieldValue('monthsEstimated', '');
43
43
  };
44
44
 
45
- const onDateChange = ([birthdate]) => {
45
+ const onDateChange = (birthdate) => {
46
46
  setFieldValue('birthdate', birthdate);
47
47
  };
48
48
 
@@ -89,17 +89,20 @@ export const DobField: React.FC = () => {
89
89
  <Layer>
90
90
  {!dobUnknown ? (
91
91
  <div className={styles.dobField}>
92
- <DatePicker dateFormat={dateFormat} datePickerType="single" onChange={onDateChange} maxDate={format(today)}>
93
- <DatePickerInput
94
- id="birthdate"
95
- {...birthdate}
96
- placeholder={placeHolder}
97
- labelText={t('dateOfBirthLabelText', 'Date of Birth')}
98
- invalid={!!(birthdateMeta.touched && birthdateMeta.error)}
99
- invalidText={birthdateMeta.error && t(birthdateMeta.error)}
100
- value={format(birthdate.value)}
101
- />
102
- </DatePicker>
92
+ <OpenmrsDatePicker
93
+ id="birthdate"
94
+ {...birthdate}
95
+ dateFormat={dateFormat}
96
+ onChange={onDateChange}
97
+ maxDate={format(today)}
98
+ labelText={t('dateOfBirthLabelText', 'Date of Birth')}
99
+ invalid={!!(birthdateMeta.touched && birthdateMeta.error)}
100
+ invalidText={birthdateMeta.error && t(birthdateMeta.error)}
101
+ value={format(birthdate.value)}
102
+ carbonOptions={{
103
+ placeholder: placeHolder,
104
+ }}
105
+ />
103
106
  </div>
104
107
  ) : (
105
108
  <div className={styles.grid}>
@@ -8,6 +8,7 @@ import { DobField } from './dob.component';
8
8
  import { PatientRegistrationContext } from '../../patient-registration-context';
9
9
  import { initialFormValues } from '../../patient-registration.component';
10
10
  import { FormValues } from '../../patient-registration-types';
11
+ import { OpenmrsDatePicker } from '@openmrs/esm-styleguide/src/public';
11
12
 
12
13
  jest.mock('@openmrs/esm-framework', () => {
13
14
  const originalModule = jest.requireActual('@openmrs/esm-framework');
@@ -21,6 +22,18 @@ jest.mock('@openmrs/esm-framework', () => {
21
22
  },
22
23
  },
23
24
  })),
25
+ getLocale: jest.fn().mockReturnValue('en'),
26
+ OpenmrsDatePicker: (datePickerProps) => (
27
+ <OpenmrsDatePicker
28
+ id={datePickerProps.id}
29
+ dateFormat={datePickerProps.dateFormat}
30
+ onChange={datePickerProps.onChange}
31
+ maxDate={datePickerProps.maxDate}
32
+ labelText={datePickerProps.labelText}
33
+ value={datePickerProps.value}
34
+ carbonOptions={datePickerProps.carbonOptions}
35
+ />
36
+ ),
24
37
  };
25
38
  });
26
39
 
@@ -59,7 +59,7 @@ export function deleteIdentifierType(identifiers: FormValues['identifiers'], ide
59
59
  export const Identifiers: React.FC = () => {
60
60
  const { identifierTypes } = useContext(ResourcesContext);
61
61
  const isLoading = !identifierTypes;
62
- const { values, setFieldValue, initialFormValues } = useContext(PatientRegistrationContext);
62
+ const { values, setFieldValue, initialFormValues, isOffline } = useContext(PatientRegistrationContext);
63
63
  const { t } = useTranslation();
64
64
  const layout = useLayoutType();
65
65
  const [showIdentifierOverlay, setShowIdentifierOverlay] = useState(false);
@@ -101,9 +101,9 @@ export const Identifiers: React.FC = () => {
101
101
  // eslint-disable-next-line react-hooks/exhaustive-deps
102
102
  }, [identifierTypes, setFieldValue, defaultPatientIdentifierTypes, values.identifiers, initializeIdentifier]);
103
103
 
104
- if (isLoading) {
104
+ if (isLoading && !isOffline) {
105
105
  return (
106
- <div className={styles.halfWidthInDesktopView}>
106
+ <div data-testid="loading-skeleton" className={styles.halfWidthInDesktopView}>
107
107
  <div className={styles.identifierLabelText}>
108
108
  <h4 className={styles.productiveHeading02Light}>{t('idFieldLabelText', 'Identifiers')}</h4>
109
109
  </div>
@@ -0,0 +1,105 @@
1
+ import React from 'react';
2
+ import { render, screen, fireEvent } from '@testing-library/react';
3
+ import { Identifiers } from './id-field.component';
4
+ import { Resources, ResourcesContext } from '../../../offline.resources';
5
+ import { Form, Formik } from 'formik';
6
+ import { PatientRegistrationContext } from '../../patient-registration-context';
7
+ import { openmrsID } from '../__mocks__/identifiers.mock';
8
+ import { mockedIdentifierTypes } from '../__mocks__/identifier-types.mock';
9
+
10
+ jest.mock('@openmrs/esm-framework', () => ({
11
+ ...jest.requireActual('@openmrs/esm-framework'),
12
+ useConfig: jest.fn().mockImplementation(() => ({
13
+ defaultPatientIdentifierTypes: ['OpenMRS ID'],
14
+ })),
15
+ }));
16
+
17
+ describe('Identifiers', () => {
18
+ const mockResourcesContextValue = {
19
+ addressTemplate: [],
20
+ currentSession: {
21
+ authenticated: true,
22
+ sessionId: 'JSESSION',
23
+ currentProvider: { uuid: 'provider-uuid', identifier: 'PRO-123' },
24
+ },
25
+ relationshipTypes: [],
26
+ identifierTypes: [...mockedIdentifierTypes],
27
+ } as Resources;
28
+
29
+ it('should render loading skeleton when identifier types are loading', () => {
30
+ render(
31
+ <ResourcesContext.Provider value={[]}>
32
+ <Formik initialValues={{}} onSubmit={null}>
33
+ <Form>
34
+ <PatientRegistrationContext.Provider
35
+ value={{
36
+ setFieldValue: jest.fn(),
37
+ initialFormValues: { identifiers: { ...mockedIdentifierTypes[0] } },
38
+ setInitialFormValues: jest.fn(),
39
+ values: {
40
+ identifiers: { openmrsID },
41
+ },
42
+ }}>
43
+ <Identifiers />
44
+ </PatientRegistrationContext.Provider>
45
+ </Form>
46
+ </Formik>
47
+ </ResourcesContext.Provider>,
48
+ );
49
+ expect(screen.getByTestId('loading-skeleton')).toBeInTheDocument();
50
+ });
51
+
52
+ it('should render identifier inputs when identifier types are loaded', () => {
53
+ render(
54
+ <ResourcesContext.Provider value={mockResourcesContextValue}>
55
+ <Formik initialValues={{}} onSubmit={null}>
56
+ <Form>
57
+ <PatientRegistrationContext.Provider
58
+ value={{
59
+ setFieldValue: jest.fn(),
60
+ initialFormValues: { identifiers: { ...mockedIdentifierTypes[0] } },
61
+ setInitialFormValues: jest.fn(),
62
+ values: {
63
+ identifiers: { openmrsID },
64
+ },
65
+ }}>
66
+ <Identifiers />
67
+ </PatientRegistrationContext.Provider>
68
+ </Form>
69
+ </Formik>
70
+ </ResourcesContext.Provider>,
71
+ );
72
+
73
+ expect(screen.getByText('Identifiers')).toBeInTheDocument();
74
+ const configureButton = screen.getByRole('button', { name: 'Configure' });
75
+ expect(configureButton).toBeInTheDocument();
76
+ expect(configureButton).toBeEnabled();
77
+ });
78
+
79
+ it('should open identifier selection overlay when "Configure" button is clicked', () => {
80
+ render(
81
+ <ResourcesContext.Provider value={mockResourcesContextValue}>
82
+ <Formik initialValues={{}} onSubmit={null}>
83
+ <Form>
84
+ <PatientRegistrationContext.Provider
85
+ value={{
86
+ setFieldValue: jest.fn(),
87
+ initialFormValues: { identifiers: { ...mockedIdentifierTypes[0] } },
88
+ setInitialFormValues: jest.fn(),
89
+ values: {
90
+ identifiers: { openmrsID },
91
+ },
92
+ }}>
93
+ <Identifiers />
94
+ </PatientRegistrationContext.Provider>
95
+ </Form>
96
+ </Formik>
97
+ </ResourcesContext.Provider>,
98
+ );
99
+
100
+ const configureButton = screen.getByRole('button', { name: 'Configure' });
101
+ fireEvent.click(configureButton);
102
+
103
+ expect(screen.getByRole('button', { name: 'Close overlay' })).toBeInTheDocument();
104
+ });
105
+ });
@@ -36,16 +36,24 @@ export function ObsField({ fieldDefinition }: ObsFieldProps) {
36
36
  concept={concept}
37
37
  validationRegex={fieldDefinition.validation.matches}
38
38
  label={fieldDefinition.label}
39
+ required={fieldDefinition.validation.required}
39
40
  />
40
41
  );
41
42
  case 'Numeric':
42
- return <NumericObsField concept={concept} label={fieldDefinition.label} />;
43
+ return (
44
+ <NumericObsField
45
+ concept={concept}
46
+ label={fieldDefinition.label}
47
+ required={fieldDefinition.validation.required}
48
+ />
49
+ );
43
50
  case 'Coded':
44
51
  return (
45
52
  <CodedObsField
46
53
  concept={concept}
47
54
  answerConceptSetUuid={fieldDefinition.answerConceptSetUuid}
48
55
  label={fieldDefinition.label}
56
+ required={fieldDefinition.validation.required}
49
57
  />
50
58
  );
51
59
  default:
@@ -61,9 +69,10 @@ interface TextObsFieldProps {
61
69
  concept: ConceptResponse;
62
70
  validationRegex: string;
63
71
  label: string;
72
+ required?: boolean;
64
73
  }
65
74
 
66
- function TextObsField({ concept, validationRegex, label }: TextObsFieldProps) {
75
+ function TextObsField({ concept, validationRegex, label, required }: TextObsFieldProps) {
67
76
  const { t } = useTranslation();
68
77
 
69
78
  const validateInput = (value: string) => {
@@ -87,6 +96,7 @@ function TextObsField({ concept, validationRegex, label }: TextObsFieldProps) {
87
96
  <Input
88
97
  id={fieldName}
89
98
  labelText={label ?? concept.display}
99
+ required={required}
90
100
  invalid={errors[fieldName] && touched[fieldName]}
91
101
  {...field}
92
102
  />
@@ -100,9 +110,10 @@ function TextObsField({ concept, validationRegex, label }: TextObsFieldProps) {
100
110
  interface NumericObsFieldProps {
101
111
  concept: ConceptResponse;
102
112
  label: string;
113
+ required?: boolean;
103
114
  }
104
115
 
105
- function NumericObsField({ concept, label }: NumericObsFieldProps) {
116
+ function NumericObsField({ concept, label, required }: NumericObsFieldProps) {
106
117
  const { t } = useTranslation();
107
118
 
108
119
  const fieldName = `obs.${concept.uuid}`;
@@ -115,6 +126,7 @@ function NumericObsField({ concept, label }: NumericObsFieldProps) {
115
126
  <Input
116
127
  id={fieldName}
117
128
  labelText={label ?? concept.display}
129
+ required={required}
118
130
  invalid={errors[fieldName] && touched[fieldName]}
119
131
  type="number"
120
132
  {...field}
@@ -130,9 +142,10 @@ interface CodedObsFieldProps {
130
142
  concept: ConceptResponse;
131
143
  answerConceptSetUuid?: string;
132
144
  label?: string;
145
+ required?: boolean;
133
146
  }
134
147
 
135
- function CodedObsField({ concept, answerConceptSetUuid, label }: CodedObsFieldProps) {
148
+ function CodedObsField({ concept, answerConceptSetUuid, label, required }: CodedObsFieldProps) {
136
149
  const config = useConfig() as RegistrationConfig;
137
150
  const { data: conceptAnswers, isLoading: isLoadingConceptAnswers } = useConceptAnswers(
138
151
  answerConceptSetUuid ?? concept.uuid,
@@ -152,6 +165,7 @@ function CodedObsField({ concept, answerConceptSetUuid, label }: CodedObsFieldPr
152
165
  <Select
153
166
  id={fieldName}
154
167
  name={fieldName}
168
+ required={required}
155
169
  labelText={label ?? concept?.display}
156
170
  invalid={errors[fieldName] && touched[fieldName]}
157
171
  {...field}>
@@ -169,6 +183,7 @@ function CodedObsField({ concept, answerConceptSetUuid, label }: CodedObsFieldPr
169
183
  id={fieldName}
170
184
  name={fieldName}
171
185
  labelText={label ?? concept?.display}
186
+ required={required}
172
187
  invalid={errors[fieldName] && touched[fieldName]}
173
188
  {...field}>
174
189
  <SelectItem key={`no-answer-select-item-${fieldName}`} value={''} text="" />
@@ -192,10 +192,9 @@ export class FormManager {
192
192
  },
193
193
  ],
194
194
  form: config.registrationObs.registrationFormUuid,
195
- obs: Object.keys(obss).map((conceptUuid) => ({
196
- concept: conceptUuid,
197
- value: obss[conceptUuid],
198
- })),
195
+ obs: Object.entries(obss)
196
+ .filter(([, value]) => value !== '')
197
+ .map(([conceptUuid, value]) => ({ concept: conceptUuid, value })),
199
198
  };
200
199
  return saveEncounter(encounterToSave);
201
200
  }
@@ -1,4 +1,4 @@
1
- import { FetchResponse, getSynchronizationItems, openmrsFetch, useConfig, usePatient } from '@openmrs/esm-framework';
1
+ import { FetchResponse, OpenmrsResource, getSynchronizationItems, openmrsFetch, useConfig, usePatient } from '@openmrs/esm-framework';
2
2
  import last from 'lodash-es/last';
3
3
  import camelCase from 'lodash-es/camelCase';
4
4
  import { Dispatch, useEffect, useMemo, useState } from 'react';
@@ -261,14 +261,16 @@ export function useInitialPatientIdentifiers(patientUuid: string): {
261
261
  function useInitialEncounters(patientUuid: string, patientToEdit: fhir.Patient) {
262
262
  const { registrationObs } = useConfig() as RegistrationConfig;
263
263
  const { data, error, isLoading } = useSWR<FetchResponse<{ results: Array<Encounter> }>>(
264
- patientToEdit
265
- ? `/ws/rest/v1/encounter?patient=${patientUuid}&v=full&encounterType=${registrationObs.encounterTypeUuid}`
264
+ patientToEdit && registrationObs.encounterTypeUuid
265
+ ? `/ws/rest/v1/encounter?patient=${patientUuid}&v=custom:(encounterDatetime,obs:(concept:ref,value:ref))&encounterType=${registrationObs.encounterTypeUuid}`
266
266
  : null,
267
267
  openmrsFetch,
268
268
  );
269
269
  const obs = data?.data.results.sort(latestFirstEncounter)?.at(0)?.obs;
270
270
  const encounters = obs
271
- ?.map(({ concept, value }) => ({ [concept['uuid']]: value['uuid'] }))
271
+ ?.map(({ concept, value }) => ({
272
+ [(concept as OpenmrsResource).uuid]: typeof value === 'object' ? value?.uuid : value,
273
+ }))
272
274
  .reduce((accu, curr) => Object.assign(accu, curr), {});
273
275
 
274
276
  return { data: encounters, isLoading, error };
@@ -5,12 +5,13 @@ import userEvent from '@testing-library/user-event';
5
5
  import { showToast, useConfig, usePatient } from '@openmrs/esm-framework';
6
6
  import { FormManager } from './form-manager';
7
7
  import { saveEncounter, savePatient } from './patient-registration.resource';
8
- import { Encounter } from './patient-registration-types';
8
+ import type { Encounter } from './patient-registration.types';
9
9
  import { Resources, ResourcesContext } from '../offline.resources';
10
10
  import { PatientRegistration } from './patient-registration.component';
11
11
  import { RegistrationConfig } from '../config-schema';
12
12
  import { mockedAddressTemplate } from './field/address/tests/mocks';
13
13
  import { mockPatient } from '../../../../tools/test-helpers';
14
+ import { OpenmrsDatePicker } from '@openmrs/esm-styleguide/src/public';
14
15
 
15
16
  const mockedUseConfig = useConfig as jest.Mock;
16
17
  const mockedUsePatient = usePatient as jest.Mock;
@@ -18,6 +19,8 @@ const mockedSaveEncounter = saveEncounter as jest.Mock;
18
19
  const mockedSavePatient = savePatient as jest.Mock;
19
20
  const mockedShowToast = showToast as jest.Mock;
20
21
 
22
+ jest.setTimeout(10000);
23
+
21
24
  jest.mock('@openmrs/esm-framework', () => {
22
25
  const originalModule = jest.requireActual('@openmrs/esm-framework');
23
26
 
@@ -55,6 +58,18 @@ jest.mock('@openmrs/esm-framework', () => {
55
58
  return {
56
59
  ...originalModule,
57
60
  validator: jest.fn(),
61
+ getLocale: jest.fn().mockReturnValue('en'),
62
+ OpenmrsDatePicker: (datePickerProps) => (
63
+ <OpenmrsDatePicker
64
+ id={datePickerProps.id}
65
+ dateFormat={datePickerProps.dateFormat}
66
+ onChange={datePickerProps.onChange}
67
+ maxDate={datePickerProps.maxDate}
68
+ labelText={datePickerProps.labelText}
69
+ value={datePickerProps.value}
70
+ carbonOptions={datePickerProps.carbonOptions}
71
+ />
72
+ ),
58
73
  };
59
74
  });
60
75
 
@@ -345,7 +360,7 @@ describe('patient registration component', () => {
345
360
  jest.clearAllMocks();
346
361
  });
347
362
 
348
- it('edits patient demographics', async () => {
363
+ fit('edits patient demographics', async () => {
349
364
  const user = userEvent.setup();
350
365
 
351
366
  mockedSavePatient.mockResolvedValue({});
@@ -379,7 +394,7 @@ describe('patient registration component', () => {
379
394
  expect(givenNameInput.value).toBe('John');
380
395
  expect(familyNameInput.value).toBe('Wilson');
381
396
  expect(middleNameInput.value).toBeFalsy();
382
- expect(dateOfBirthInput.value).toBe('4/4/1972');
397
+ expect(dateOfBirthInput.value).toBe('04/04/1972');
383
398
  expect(genderInput.value).toBe('Male');
384
399
 
385
400
  // do some edits
@@ -128,8 +128,8 @@ export interface Encounter {
128
128
  }>;
129
129
  form: string;
130
130
  obs: Array<{
131
- concept: string;
132
- value: string | number;
131
+ concept: string | OpenmrsResource;
132
+ value: string | number | OpenmrsResource;
133
133
  }>;
134
134
  }
135
135
 
@@ -5,6 +5,7 @@ import { initialFormValues } from '../../patient-registration.component';
5
5
  import { DemographicsSection } from './demographics-section.component';
6
6
  import { PatientRegistrationContext } from '../../patient-registration-context';
7
7
  import { FormValues } from '../../patient-registration-types';
8
+ import { OpenmrsDatePicker } from '@openmrs/esm-styleguide/src/public';
8
9
 
9
10
  jest.mock('@openmrs/esm-framework', () => {
10
11
  const originalModule = jest.requireActual('@openmrs/esm-framework');
@@ -15,6 +16,18 @@ jest.mock('@openmrs/esm-framework', () => {
15
16
  useConfig: jest.fn().mockImplementation(() => ({
16
17
  fieldConfigurations: { dateOfBirth: { useEstimatedDateOfBirth: { enabled: true, dayOfMonth: 0, month: 0 } } },
17
18
  })),
19
+ getLocale: jest.fn().mockReturnValue('en'),
20
+ OpenmrsDatePicker: (datePickerProps) => (
21
+ <OpenmrsDatePicker
22
+ id={datePickerProps.id}
23
+ dateFormat={datePickerProps.dateFormat}
24
+ onChange={datePickerProps.onChange}
25
+ maxDate={datePickerProps.maxDate}
26
+ labelText={datePickerProps.labelText}
27
+ value={datePickerProps.value}
28
+ carbonOptions={datePickerProps.carbonOptions}
29
+ />
30
+ ),
18
31
  };
19
32
  });
20
33
 
@@ -83,9 +83,7 @@ const RelationshipView: React.FC<RelationshipViewProps> = ({
83
83
  <div className={styles.relationship}>
84
84
  <div className={styles.searchBox}>
85
85
  <div className={styles.relationshipHeader}>
86
- <h4 className={styles.productiveHeading}>
87
- {relationship?.relation ?? t('relationshipPlaceholder', 'Relationship')}
88
- </h4>
86
+ <h4 className={styles.productiveHeading}>{t('relationshipPlaceholder', 'Relationship')}</h4>
89
87
  <Button
90
88
  kind="ghost"
91
89
  iconDescription={t('deleteRelationshipTooltipText', 'Delete')}
@@ -163,12 +161,12 @@ export const RelationshipsSection = () => {
163
161
  const tmp: RelationshipType[] = [];
164
162
  relationshipTypes.results.forEach((type) => {
165
163
  const aIsToB = {
166
- display: type.aIsToB,
164
+ display: type.displayAIsToB ? type.displayAIsToB : type.aIsToB,
167
165
  uuid: type.uuid,
168
166
  direction: 'aIsToB',
169
167
  };
170
168
  const bIsToA = {
171
- display: type.bIsToA,
169
+ display: type.displayBIsToA ? type.displayBIsToA : type.bIsToA,
172
170
  uuid: type.uuid,
173
171
  direction: 'bIsToA',
174
172
  };