@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kenyaemr/esm-patient-registration-app",
3
- "version": "8.1.1-pre.123",
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
+ }
@@ -98,6 +98,7 @@ export function CodedPersonAttributeField({
98
98
  return (
99
99
  <>
100
100
  <Select
101
+ style={{ marginBottom: '1rem' }}
101
102
  id={id}
102
103
  name={`person-attribute-${personAttributeType.uuid}`}
103
104
  labelText={label ?? personAttributeType?.display}
@@ -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
- type CustomPersonAttributeFieldProps = {
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: Array<{ uuid: string; label?: string }>;
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;