@kenyaemr/esm-patient-registration-app 8.1.1-pre.123 → 8.1.1-pre.129
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/.turbo/turbo-build.log +11 -11
- package/dist/130.js +1 -1
- package/dist/130.js.map +1 -1
- package/dist/66.js +1 -0
- package/dist/66.js.map +1 -0
- package/dist/kenyaemr-esm-patient-registration-app.js +1 -1
- package/dist/kenyaemr-esm-patient-registration-app.js.buildmanifest.json +12 -12
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package-lock.json +6047 -0
- package/package.json +1 -1
- package/src/patient-registration/field/field.scss +4 -4
- package/src/patient-registration/field/person-attributes/coded-person-attribute-field.component.tsx +1 -0
- package/src/patient-registration/field/person-attributes/custom-person-attribute-field.component.tsx +76 -27
- package/src/patient-registration/field/person-attributes/useUpdateIdentifierRequirement.tsx +83 -0
- package/dist/10.js +0 -1
- package/dist/10.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kenyaemr/esm-patient-registration-app",
|
|
3
|
-
"version": "8.1.1-pre.
|
|
3
|
+
"version": "8.1.1-pre.129",
|
|
4
4
|
"description": "Patient registration microfrontend for the OpenMRS SPA",
|
|
5
5
|
"browser": "dist/kenyaemr-esm-patient-registration-app.js",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -111,10 +111,6 @@
|
|
|
111
111
|
svg {
|
|
112
112
|
margin-left: layout.$spacing-03;
|
|
113
113
|
}
|
|
114
|
-
|
|
115
|
-
.customField {
|
|
116
|
-
margin-bottom: layout.$spacing-05;
|
|
117
|
-
}
|
|
118
114
|
}
|
|
119
115
|
|
|
120
116
|
.attributeField {
|
|
@@ -169,3 +165,7 @@
|
|
|
169
165
|
line-height: 1.34;
|
|
170
166
|
margin: layout.$spacing-02 0 0;
|
|
171
167
|
}
|
|
168
|
+
|
|
169
|
+
.customField {
|
|
170
|
+
margin-bottom: layout.$spacing-05;
|
|
171
|
+
}
|
package/src/patient-registration/field/person-attributes/custom-person-attribute-field.component.tsx
CHANGED
|
@@ -1,19 +1,43 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { Field } from 'formik';
|
|
1
|
+
import React, { useContext, useEffect } from 'react';
|
|
2
|
+
import { Field, type FieldProps } from 'formik';
|
|
3
3
|
import { Layer, Select, SelectItem } from '@carbon/react';
|
|
4
|
-
import { type PersonAttributeTypeResponse } from '../../patient-registration.types';
|
|
5
4
|
import { useTranslation } from 'react-i18next';
|
|
6
|
-
import styles from './../field.scss';
|
|
7
5
|
import classNames from 'classnames';
|
|
6
|
+
import { PatientRegistrationContext } from '../../patient-registration-context';
|
|
7
|
+
import styles from './../field.scss';
|
|
8
|
+
import { ResourcesContext } from '../../../offline.resources';
|
|
9
|
+
import useUpdateIdentifierRequirement from './useUpdateIdentifierRequirement';
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
interface PersonAttributeTypeResponse {
|
|
12
|
+
uuid: string;
|
|
13
|
+
display?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface ConceptAnswer {
|
|
17
|
+
uuid?: string;
|
|
18
|
+
name?: string;
|
|
19
|
+
label?: string;
|
|
20
|
+
showServiceExpression?: {
|
|
21
|
+
attributeTypeUuid: string;
|
|
22
|
+
value: string;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
interface CustomPersonAttributeFieldProps {
|
|
10
27
|
id: string;
|
|
11
28
|
personAttributeType: PersonAttributeTypeResponse;
|
|
12
29
|
answerConceptSetUuid: string;
|
|
13
30
|
label?: string;
|
|
14
|
-
customConceptAnswers:
|
|
31
|
+
customConceptAnswers: ConceptAnswer[];
|
|
15
32
|
required: boolean;
|
|
16
|
-
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
interface PatientRegistrationContextType {
|
|
36
|
+
setFieldValue: (field: string, value: any) => void;
|
|
37
|
+
values: {
|
|
38
|
+
attributes?: Record<string, string>;
|
|
39
|
+
};
|
|
40
|
+
}
|
|
17
41
|
|
|
18
42
|
const CustomPersonAttributeField: React.FC<CustomPersonAttributeFieldProps> = ({
|
|
19
43
|
personAttributeType,
|
|
@@ -24,30 +48,55 @@ const CustomPersonAttributeField: React.FC<CustomPersonAttributeFieldProps> = ({
|
|
|
24
48
|
}) => {
|
|
25
49
|
const { t } = useTranslation();
|
|
26
50
|
const fieldName = `attributes.${personAttributeType.uuid}`;
|
|
51
|
+
const { setFieldValue, values } = useContext(PatientRegistrationContext);
|
|
52
|
+
useUpdateIdentifierRequirement(setFieldValue, values);
|
|
53
|
+
// TODO: Improve this logic
|
|
54
|
+
const filteredCustomConceptAnswers = customConceptAnswers.filter((answer) => {
|
|
55
|
+
const showExpression = answer.showServiceExpression;
|
|
56
|
+
if (!showExpression) return true;
|
|
57
|
+
|
|
58
|
+
const attributeValue = values?.attributes?.[showExpression.attributeTypeUuid];
|
|
59
|
+
const answerCadreId = answer.name;
|
|
60
|
+
|
|
61
|
+
if (answerCadreId == null) return true;
|
|
62
|
+
|
|
63
|
+
return showExpression.value.toLowerCase() === attributeValue?.toLowerCase();
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
useEffect(() => {
|
|
67
|
+
return () => {
|
|
68
|
+
setFieldValue(fieldName, '');
|
|
69
|
+
};
|
|
70
|
+
}, [fieldName, setFieldValue]);
|
|
71
|
+
|
|
72
|
+
const renderSelect = ({ field, form: { touched, errors } }: FieldProps) => {
|
|
73
|
+
const hasError = errors[fieldName] && touched[fieldName];
|
|
74
|
+
const displayLabel = label ?? personAttributeType?.display ?? '';
|
|
75
|
+
|
|
76
|
+
return (
|
|
77
|
+
<Select
|
|
78
|
+
id={id}
|
|
79
|
+
name={`person-attribute-${personAttributeType.uuid}`}
|
|
80
|
+
labelText={displayLabel}
|
|
81
|
+
invalid={Boolean(hasError)}
|
|
82
|
+
required={required}
|
|
83
|
+
{...field}>
|
|
84
|
+
<SelectItem value="" text={t('selectAnOption', 'Select an option')} />
|
|
85
|
+
{filteredCustomConceptAnswers.map((answer) => (
|
|
86
|
+
<SelectItem
|
|
87
|
+
key={answer.uuid ?? answer.name}
|
|
88
|
+
value={answer.uuid ?? answer.name ?? ''}
|
|
89
|
+
text={answer.label ?? answer.uuid ?? answer.name ?? ''}
|
|
90
|
+
/>
|
|
91
|
+
))}
|
|
92
|
+
</Select>
|
|
93
|
+
);
|
|
94
|
+
};
|
|
27
95
|
|
|
28
96
|
return (
|
|
29
97
|
<div className={classNames(styles.customField, styles.halfWidthInDesktopView)}>
|
|
30
98
|
<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>
|
|
99
|
+
<Field name={fieldName}>{renderSelect}</Field>
|
|
51
100
|
</Layer>
|
|
52
101
|
</div>
|
|
53
102
|
);
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { useEffect, useCallback, useRef, useMemo, useContext } from 'react';
|
|
2
|
+
import { deleteIdentifierType, initializeIdentifier } from '../id/id-field.component';
|
|
3
|
+
import { ResourcesContext } from '../../../offline.resources';
|
|
4
|
+
|
|
5
|
+
const useUpdateIdentifierRequirement = (setFieldValue, values) => {
|
|
6
|
+
const { identifierTypes = [] } = useContext(ResourcesContext);
|
|
7
|
+
const previousAttributes = useRef(values.attributes);
|
|
8
|
+
const previousIdentifiers = useRef(values.identifiers);
|
|
9
|
+
|
|
10
|
+
const publicationNumberIdentifier = useMemo(
|
|
11
|
+
() => identifierTypes?.find((identifier) => identifier.name === 'Publication Number'),
|
|
12
|
+
[identifierTypes],
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
// Memoize the civilian check
|
|
16
|
+
const isCivilian = useMemo(() => Object.values(values.attributes ?? {}).includes('Civilian'), [values.attributes]);
|
|
17
|
+
|
|
18
|
+
// Memoize the identifier initialization logic
|
|
19
|
+
const initializePublicationIdentifier = useCallback(
|
|
20
|
+
(currentIdentifiers) => {
|
|
21
|
+
if (!publicationNumberIdentifier) {
|
|
22
|
+
console.warn('Publication Number identifier type not found');
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const initializedIdentifier = initializeIdentifier(
|
|
27
|
+
publicationNumberIdentifier,
|
|
28
|
+
currentIdentifiers[publicationNumberIdentifier.uuid],
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
return initializedIdentifier;
|
|
32
|
+
},
|
|
33
|
+
[publicationNumberIdentifier],
|
|
34
|
+
);
|
|
35
|
+
// Only run the effect if isCivilian is true
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
// Skip if we don't have the required data
|
|
38
|
+
if (!values.attributes || !publicationNumberIdentifier) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Check if relevant values have actually changed
|
|
43
|
+
const attributesChanged = previousAttributes.current !== values.attributes;
|
|
44
|
+
const identifiersChanged = previousIdentifiers.current !== values.identifiers;
|
|
45
|
+
|
|
46
|
+
if (!attributesChanged && !identifiersChanged) {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// Update refs
|
|
51
|
+
previousAttributes.current = values.attributes;
|
|
52
|
+
previousIdentifiers.current = values.identifiers;
|
|
53
|
+
const isDependant = Object.values(values.attributes ?? {}).includes('Dependant');
|
|
54
|
+
// Only proceed if the user is a civilian
|
|
55
|
+
if (isCivilian && isDependant) {
|
|
56
|
+
const initializedIdentifier = initializePublicationIdentifier(values.identifiers);
|
|
57
|
+
|
|
58
|
+
// check if values.identifiers already has the publication number identifier
|
|
59
|
+
const hasPublicationNumberIdentifier = values.identifiers[publicationNumberIdentifier.fieldName];
|
|
60
|
+
|
|
61
|
+
if (initializedIdentifier && !hasPublicationNumberIdentifier) {
|
|
62
|
+
setFieldValue('identifiers', {
|
|
63
|
+
...values.identifiers,
|
|
64
|
+
[publicationNumberIdentifier.fieldName]: { ...initializedIdentifier, required: true },
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
} else {
|
|
68
|
+
// Before deleting the publication number identifier, check if it exists
|
|
69
|
+
if (values.identifiers[publicationNumberIdentifier.fieldName]) {
|
|
70
|
+
setFieldValue('identifiers', deleteIdentifierType(values.identifiers, publicationNumberIdentifier.fieldName));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}, [
|
|
74
|
+
values.attributes,
|
|
75
|
+
values.identifiers,
|
|
76
|
+
isCivilian,
|
|
77
|
+
publicationNumberIdentifier,
|
|
78
|
+
initializePublicationIdentifier,
|
|
79
|
+
setFieldValue,
|
|
80
|
+
]);
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
export default useUpdateIdentifierRequirement;
|