@kenyaemr/esm-patient-clinical-view-app 5.4.2-pre.2128 → 5.4.2-pre.2131

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 (31) hide show
  1. package/.turbo/turbo-build.log +75 -88
  2. package/dist/574.js +1 -1
  3. package/dist/{551.js → 825.js} +1 -1
  4. package/dist/825.js.map +1 -0
  5. package/dist/kenyaemr-esm-patient-clinical-view-app.js +1 -1
  6. package/dist/kenyaemr-esm-patient-clinical-view-app.js.buildmanifest.json +32 -32
  7. package/dist/main.js +1 -1
  8. package/dist/main.js.map +1 -1
  9. package/dist/routes.json +1 -1
  10. package/package.json +1 -1
  11. package/src/config-schema.ts +21 -5
  12. package/src/contact-list/contact-list.component.tsx +3 -1
  13. package/src/contact-list/contact-list.resource.tsx +15 -13
  14. package/src/contact-list/contact-list.workspace.tsx +10 -249
  15. package/src/contact-list/contact-tracing-history.component.tsx +1 -1
  16. package/src/family-partner-history/family-history.component.tsx +40 -9
  17. package/src/family-partner-history/family-relationship.workspace.tsx +10 -4
  18. package/src/family-partner-history/relationships.resource.tsx +24 -98
  19. package/src/hooks/useContacts.ts +1 -1
  20. package/src/hooks/usePersonAttributes.ts +15 -0
  21. package/src/index.ts +0 -4
  22. package/src/relationships/forms/baseline-info-form-section.component.tsx +315 -0
  23. package/src/relationships/forms/patient-search-create-form.tsx +38 -23
  24. package/src/relationships/relationship.resources.ts +15 -5
  25. package/src/relationships/tabs/relationships-tabs-component.tsx +0 -5
  26. package/src/routes.json +0 -14
  27. package/translations/en.json +13 -3
  28. package/dist/551.js.map +0 -1
  29. package/src/other-relationships/other-relationships.component.tsx +0 -229
  30. package/src/other-relationships/other-relationships.scss +0 -125
  31. package/src/other-relationships/other-relationships.workspace.tsx +0 -155
@@ -1,4 +1,4 @@
1
- import { Button, ButtonSet, Column, ComboBox, DatePicker, DatePickerInput, Form, Stack, TextArea } from '@carbon/react';
1
+ import { Button, ButtonSet, Column, ComboBox, DatePicker, DatePickerInput, Form, Stack } from '@carbon/react';
2
2
  import { zodResolver } from '@hookform/resolvers/zod';
3
3
  import { useConfig, useSession } from '@openmrs/esm-framework';
4
4
  import React, { useMemo } from 'react';
@@ -6,8 +6,11 @@ import { Controller, FormProvider, SubmitHandler, useForm } from 'react-hook-for
6
6
  import { useTranslation } from 'react-i18next';
7
7
  import { z } from 'zod';
8
8
  import { ConfigObject } from '../config-schema';
9
+ import { saveContact } from '../contact-list/contact-list.resource';
10
+ import usePersonAttributes from '../hooks/usePersonAttributes';
11
+ import RelationshipBaselineInfoFormSection from '../relationships/forms/baseline-info-form-section.component';
9
12
  import PatientSearchCreate from '../relationships/forms/patient-search-create-form';
10
- import { relationshipFormSchema, saveRelationship } from '../relationships/relationship.resources';
13
+ import { relationshipFormSchema } from '../relationships/relationship.resources';
11
14
  import { uppercaseText } from '../utils/expression-helper';
12
15
  import styles from './family-relationship.scss';
13
16
  import { useMappedRelationshipTypes } from './relationships.resource';
@@ -57,12 +60,14 @@ const FamilyRelationshipForm: React.FC<RelationshipFormProps> = ({ closeWorkspac
57
60
  },
58
61
  resolver: zodResolver(schema),
59
62
  });
63
+ const personUuid = form.watch('personB');
64
+ const { attributes } = usePersonAttributes(personUuid);
60
65
 
61
66
  const { control, handleSubmit } = form;
62
67
 
63
68
  const onSubmit: SubmitHandler<FormData> = async (data) => {
64
69
  try {
65
- await saveRelationship(data, config, session, []); /// Remove notes from payload since endpoint doesn't expect it to avoid 400 error
70
+ await saveContact(data, config, session, attributes); /// Remove notes from payload since endpoint doesn't expect it to avoid 400 error
66
71
  closeWorkspace();
67
72
  } catch (error) {}
68
73
  };
@@ -83,7 +88,7 @@ const FamilyRelationshipForm: React.FC<RelationshipFormProps> = ({ closeWorkspac
83
88
  titleText={t('relationship', 'Relationship')}
84
89
  placeholder="Relationship to patient"
85
90
  items={relationshipTypes}
86
- itemToString={(item) => (item ? uppercaseText(item.text) : '')}
91
+ itemToString={(item) => item?.text ?? ''}
87
92
  onChange={(e) => field.onChange(e.selectedItem?.id)}
88
93
  invalid={!!fieldState.error}
89
94
  invalidText={fieldState.error?.message}
@@ -142,6 +147,7 @@ const FamilyRelationshipForm: React.FC<RelationshipFormProps> = ({ closeWorkspac
142
147
  )}
143
148
  />
144
149
  </Column>
150
+ <RelationshipBaselineInfoFormSection />
145
151
  </Stack>
146
152
 
147
153
  <ButtonSet className={styles.buttonSet}>
@@ -4,45 +4,8 @@ import { type FetchResponse, openmrsFetch, FHIRResource, restBaseUrl, useConfig
4
4
  import useSWRImmutable from 'swr/immutable';
5
5
  import { RelationshipTypeResponse } from '../case-management/workspace/case-management.resource';
6
6
  import { ConfigObject } from '../config-schema';
7
-
8
- interface RelationshipsResponse {
9
- results: Array<Relationship>;
10
- }
11
-
12
- interface ExtractedRelationship {
13
- uuid: string;
14
- display: string;
15
- relativeAge: number;
16
- name: string;
17
- dead: boolean;
18
- causeOfDeath: string;
19
- relativeUuid: string;
20
- relationshipType: string;
21
- relationshipTypeDisplay: string;
22
- relationshipTypeUUID: string;
23
- patientUuid: string;
24
- }
25
-
26
- export interface Relationship {
27
- display: string;
28
- uuid: string;
29
- personA: Person;
30
- personB: Person;
31
- relationshipType: {
32
- uuid: string;
33
- display: string;
34
- aIsToB: string;
35
- bIsToA: string;
36
- };
37
- }
38
-
39
- interface Person {
40
- uuid: string;
41
- age: number;
42
- dead: boolean;
43
- display: string;
44
- causeOfDeath: string;
45
- }
7
+ import { extractContactData } from '../hooks/useContacts';
8
+ import { Relationship } from '../types';
46
9
 
47
10
  type FHIRResourceResponse = {
48
11
  total: number;
@@ -124,78 +87,41 @@ export const useMappedRelationshipTypes = () => {
124
87
  };
125
88
 
126
89
  export function usePatientRelationships(patientUuid: string) {
127
- const customRepresentation =
128
- 'custom:(display,uuid,personA:(uuid,age,display,dead,causeOfDeath),personB:(uuid,age,display,dead,causeOfDeath),relationshipType:(uuid,display,description,aIsToB,bIsToA))';
90
+ const customeRepresentation =
91
+ 'custom:(display,uuid,personA:(uuid,age,display,dead,causeOfDeath,gender,attributes:(uuid,display,value,attributeType:(uuid,display))),personB:(uuid,age,display,dead,causeOfDeath,gender,attributes:(uuid,display,value,attributeType:(uuid,display))),relationshipType:(uuid,display,description,aIsToB,bIsToA),startDate)';
92
+ const url = patientUuid ? `/ws/rest/v1/relationship?v=${customeRepresentation}&person=${patientUuid}` : null;
129
93
 
130
- const relationshipsUrl = patientUuid
131
- ? `/ws/rest/v1/relationship?person=${patientUuid}&v=${customRepresentation}`
132
- : null;
94
+ const config = useConfig<ConfigObject>();
133
95
 
134
- const { data, error, isLoading, isValidating } = useSWR<FetchResponse<RelationshipsResponse>, Error>(
135
- relationshipsUrl,
96
+ const { data, error, isLoading, isValidating } = useSWR<FetchResponse<{ results: Array<Relationship> }>, Error>(
97
+ url,
136
98
  openmrsFetch,
137
99
  {
138
100
  revalidateOnFocus: false,
139
101
  },
140
102
  );
103
+ const familyRelationships = useMemo(
104
+ () => config.relationshipTypesList.filter((rl) => rl.category.some((c) => c === 'family')),
105
+ [config],
106
+ );
141
107
 
142
108
  const relationships = useMemo(() => {
143
- return data?.data?.results?.length ? extractRelationshipData(patientUuid, data?.data?.results) : [];
144
- }, [data?.data?.results, patientUuid]);
109
+ return data?.data?.results?.length
110
+ ? extractContactData(
111
+ patientUuid,
112
+ data?.data?.results.filter((rel) =>
113
+ familyRelationships.some((famRel) => famRel.uuid === rel.relationshipType.uuid),
114
+ ),
115
+ config,
116
+ )
117
+ : [];
118
+ }, [data?.data?.results, patientUuid, config, familyRelationships]);
145
119
 
146
120
  return {
147
- relationships,
121
+ relationships: relationships ?? [],
148
122
  error,
149
123
  isLoading,
150
124
  isValidating,
151
- relationshipsUrl,
125
+ relationshipsUrl: url,
152
126
  };
153
127
  }
154
-
155
- function extractRelationshipData(
156
- patientIdentifier: string,
157
- relationships: Array<Relationship>,
158
- ): Array<ExtractedRelationship> {
159
- const relationshipsData = [];
160
- for (const r of relationships) {
161
- if (patientIdentifier === r.personA.uuid) {
162
- relationshipsData.push({
163
- uuid: r.uuid,
164
- name: extractName(r.personB.display),
165
- display: r.personB.display,
166
- relativeAge: r.personB.age,
167
- dead: r.personB.dead,
168
- causeOfDeath: r.personB.causeOfDeath,
169
- relativeUuid: r.personB.uuid,
170
- relationshipType: r.relationshipType.bIsToA,
171
- relationshipTypeDisplay: r.relationshipType.display,
172
- relationshipTypeUUID: r.relationshipType.uuid,
173
- patientUuid: r.personB.uuid,
174
- });
175
- } else {
176
- relationshipsData.push({
177
- uuid: r.uuid,
178
- name: extractName(r.personA.display),
179
- display: r.personA.display,
180
- relativeAge: r.personA.age,
181
- causeOfDeath: r.personA.causeOfDeath,
182
- relativeUuid: r.personA.uuid,
183
- dead: r.personA.dead,
184
- relationshipType: r.relationshipType.aIsToB,
185
- relationshipTypeDisplay: r.relationshipType.display,
186
- relationshipTypeUUID: r.relationshipType.uuid,
187
- patientUuid: r.personA.uuid,
188
- });
189
- }
190
- }
191
- return relationshipsData;
192
- }
193
-
194
- function extractName(display: string) {
195
- const pattern = /-\s*(.*)$/;
196
- const match = display.match(pattern);
197
- if (match && match.length > 1) {
198
- return match[1].trim();
199
- }
200
- return display.trim();
201
- }
@@ -84,7 +84,7 @@ function getContact(relationship: Relationship, config: ConfigObject, person: 'p
84
84
  endDate: !relationship.endDate ? null : formatDate(parseDate(relationship.endDate)),
85
85
  } as Contact;
86
86
  }
87
- function extractContactData(
87
+ export function extractContactData(
88
88
  patientIdentifier: string,
89
89
  relationships: Array<Relationship>,
90
90
  config: ConfigObject,
@@ -0,0 +1,15 @@
1
+ import { FetchResponse, openmrsFetch, restBaseUrl } from '@openmrs/esm-framework';
2
+ import useSWR from 'swr';
3
+ import { Person } from '../types';
4
+
5
+ const usePersonAttributes = (personUuid?: string) => {
6
+ const url = `${restBaseUrl}/person/${personUuid}/attribute`;
7
+ const { data, error, isLoading, mutate } = useSWR<
8
+ FetchResponse<{
9
+ results: Person['attributes'];
10
+ }>
11
+ >(personUuid ? url : null, openmrsFetch);
12
+ return { error, isLoading, mutate, attributes: data?.data?.results ?? [] };
13
+ };
14
+
15
+ export default usePersonAttributes;
package/src/index.ts CHANGED
@@ -44,8 +44,6 @@ import CaseManagementForm from './case-management/workspace/case-management.work
44
44
  import Relationships from './relationships/relationships.component';
45
45
  import CaseEncounterOverviewComponent from './case-management/encounters/case-encounter-overview.component';
46
46
  import FamilyRelationshipForm from './family-partner-history/family-relationship.workspace';
47
- import { OtherRelationships } from './other-relationships/other-relationships.component';
48
- import { OtherRelationshipsForm } from './other-relationships/other-relationships.workspace';
49
47
  import InPatient from './in-patient/in-patient.component';
50
48
  import { inPatientMeta } from './in-patient/in-patient.meta';
51
49
  import PeerCalendar from './peer-calendar/peer-calendar.component';
@@ -98,9 +96,7 @@ export const familyHistoryLink = getSyncLifecycle(createDashboardLink(familyHist
98
96
  export const familyRelationshipForm = getSyncLifecycle(FamilyRelationshipForm, options);
99
97
 
100
98
  // Dashboard links for Other relationships and the corresponding view in the patient chart
101
- export const otherRelationships = getSyncLifecycle(OtherRelationships, options);
102
99
  export const otherRelationshipsLink = getSyncLifecycle(createDashboardLink(otherRelationshipsDashboardMeta), options);
103
- export const otherRelationshipsForm = getSyncLifecycle(OtherRelationshipsForm, options);
104
100
 
105
101
  // Relationships links for Family History and the corresponding view in the patient chart
106
102
  export const relationshipsLink = getSyncLifecycle(createDashboardLink(relationshipsDashboardMeta), options);
@@ -0,0 +1,315 @@
1
+ import { Column, Dropdown, RadioButton, RadioButtonGroup, SelectSkeleton } from '@carbon/react';
2
+ import { useConfig } from '@openmrs/esm-framework';
3
+ import React, { useEffect, useMemo } from 'react';
4
+ import { Controller, useFormContext } from 'react-hook-form';
5
+ import { useTranslation } from 'react-i18next';
6
+ import { z } from 'zod';
7
+ import { ConfigObject } from '../../config-schema';
8
+ import { contactListConceptMap } from '../../contact-list/contact-list-concept-map';
9
+ import { contactIPVOutcomeOptions } from '../../contact-list/contact-list.resource';
10
+ import usePersonAttributes from '../../hooks/usePersonAttributes';
11
+ import { BOOLEAN_NO, BOOLEAN_YES, relationshipFormSchema } from '../relationship.resources';
12
+ import {
13
+ LIVING_WITH_PATIENT_CONCEPT_UUID,
14
+ PARTNER_HIV_STATUS_CONCEPT_UUID,
15
+ PNS_APROACH_CONCEPT_UUID,
16
+ } from '../relationships-constants';
17
+ import styles from './form.scss';
18
+
19
+ const RelationshipBaselineInfoFormSection = () => {
20
+ const form = useFormContext<z.infer<typeof relationshipFormSchema>>();
21
+ const { t } = useTranslation();
22
+ const personUuid = form.watch('personB');
23
+ const config = useConfig<ConfigObject>();
24
+ const { setValue } = form;
25
+ const { attributes, isLoading } = usePersonAttributes(personUuid);
26
+
27
+ const hivStatus = useMemo(
28
+ () =>
29
+ Object.entries(contactListConceptMap[PARTNER_HIV_STATUS_CONCEPT_UUID].answers).map(([uuid, display]) => ({
30
+ label: display,
31
+ value: uuid,
32
+ })),
33
+ [],
34
+ );
35
+
36
+ const pnsAproach = useMemo(
37
+ () =>
38
+ Object.entries(contactListConceptMap[PNS_APROACH_CONCEPT_UUID].answers).map(([uuid, display]) => ({
39
+ label: display,
40
+ value: uuid,
41
+ })),
42
+ [],
43
+ );
44
+
45
+ const contactLivingWithPatient = useMemo(
46
+ () =>
47
+ Object.entries(contactListConceptMap[LIVING_WITH_PATIENT_CONCEPT_UUID].answers).map(([uuid, display]) => ({
48
+ label: display,
49
+ value: uuid,
50
+ })),
51
+ [],
52
+ );
53
+
54
+ const observableRelationship = form.watch('relationshipType');
55
+ const observablePhysicalAssault = form.watch('physicalAssault');
56
+ const observableThreatened = form.watch('threatened');
57
+ const observableSexualAssault = form.watch('sexualAssault');
58
+ const showIPVRelatedFields =
59
+ config.relationshipTypesList.findIndex(
60
+ (r) => r.uuid === observableRelationship && r.category.some((c) => c === 'sexual'),
61
+ ) !== -1;
62
+
63
+ useEffect(() => {
64
+ if ([observablePhysicalAssault, observableThreatened, observableSexualAssault].includes(BOOLEAN_YES)) {
65
+ form.setValue('ipvOutCome', 'True');
66
+ } else if (
67
+ [observablePhysicalAssault, observableThreatened, observableSexualAssault].every((v) => v === BOOLEAN_NO)
68
+ ) {
69
+ form.setValue('ipvOutCome', 'False');
70
+ }
71
+ if (!showIPVRelatedFields) {
72
+ form.setValue('ipvOutCome', undefined);
73
+ }
74
+ }, [
75
+ observablePhysicalAssault,
76
+ observableThreatened,
77
+ observableSexualAssault,
78
+ observableRelationship,
79
+ form,
80
+ showIPVRelatedFields,
81
+ ]);
82
+
83
+ useEffect(() => {
84
+ if (attributes.length) {
85
+ // HIV Status
86
+ const hivStatusAttribute = attributes.find(
87
+ (a) => a.attributeType.uuid === config.contactPersonAttributesUuid.baselineHIVStatus,
88
+ );
89
+ if (hivStatusAttribute) {
90
+ const value = hivStatus.find((r) => r.value.startsWith(hivStatusAttribute.value))?.value;
91
+ if (value) {
92
+ setValue('baselineStatus', value);
93
+ }
94
+ }
95
+
96
+ const pnsAproachAttribute = attributes.find(
97
+ (a) => a.attributeType.uuid === config.contactPersonAttributesUuid.preferedPnsAproach,
98
+ );
99
+ if (pnsAproachAttribute) {
100
+ const value = pnsAproach.find((r) => r.value.startsWith(pnsAproachAttribute.value))?.value;
101
+ if (value) {
102
+ setValue('preferedPNSAproach', value);
103
+ }
104
+ }
105
+
106
+ const livingWithPatientAttribute = attributes.find(
107
+ (a) => a.attributeType.uuid === config.contactPersonAttributesUuid.livingWithContact,
108
+ );
109
+ if (livingWithPatientAttribute) {
110
+ const value = contactLivingWithPatient.find((r) => r.value.startsWith(livingWithPatientAttribute.value))?.value;
111
+ if (value) {
112
+ setValue('livingWithClient', value);
113
+ }
114
+ }
115
+
116
+ const ipvAttr = attributes.find(
117
+ (a) => a.attributeType.uuid === config.contactPersonAttributesUuid.contactIPVOutcome,
118
+ );
119
+ if (ipvAttr) {
120
+ const value = contactIPVOutcomeOptions.find((r) => r.value.startsWith(ipvAttr.value))?.value;
121
+ if (value) {
122
+ setValue('ipvOutCome', value as any);
123
+ }
124
+ }
125
+ }
126
+ }, [attributes, setValue, config, hivStatus, pnsAproach, contactLivingWithPatient]);
127
+
128
+ return (
129
+ <>
130
+ {showIPVRelatedFields && (
131
+ <>
132
+ <span className={styles.sectionHeader}>{t('ipvQuestions', 'IPV Questions')}</span>
133
+ <Column>
134
+ <Controller
135
+ control={form.control}
136
+ name="physicalAssault"
137
+ render={({ field, fieldState: { error } }) => (
138
+ <RadioButtonGroup
139
+ id="physicalAssault"
140
+ legendText={t(
141
+ 'physicalAssault',
142
+ '1. Has he/she ever hit, kicked, slapped, or otherwise physically hurt you?',
143
+ )}
144
+ {...field}
145
+ invalid={error?.message}
146
+ invalidText={error?.message}
147
+ className={styles.billingItem}>
148
+ <RadioButton labelText={t('yes', 'Yes')} value={BOOLEAN_YES} id="physicalAssault_yes" />
149
+ <RadioButton labelText={t('no', 'No')} value={BOOLEAN_NO} id="physicalAssault_no" />
150
+ </RadioButtonGroup>
151
+ )}
152
+ />
153
+ </Column>
154
+ <Column>
155
+ <Controller
156
+ control={form.control}
157
+ name="threatened"
158
+ render={({ field, fieldState: { error } }) => (
159
+ <RadioButtonGroup
160
+ id="threatened"
161
+ legendText={t('threatened', '2. Has he/she ever threatened to hurt you?')}
162
+ {...field}
163
+ invalid={error?.message}
164
+ invalidText={error?.message}
165
+ className={styles.billingItem}>
166
+ <RadioButton labelText={t('yes', 'Yes')} value={BOOLEAN_YES} id="threatened_yes" />
167
+ <RadioButton labelText={t('no', 'No')} value={BOOLEAN_NO} id="threatened_no" />
168
+ </RadioButtonGroup>
169
+ )}
170
+ />
171
+ </Column>
172
+ <Column>
173
+ <Controller
174
+ control={form.control}
175
+ name="sexualAssault"
176
+ render={({ field, fieldState: { error } }) => (
177
+ <RadioButtonGroup
178
+ id="sexualAssault"
179
+ legendText={t(
180
+ 'sexualAssault',
181
+ '3.Has he/she ever forced you to do something sexually that made you feel uncomfortable?',
182
+ )}
183
+ {...field}
184
+ invalid={error?.message}
185
+ invalidText={error?.message}
186
+ className={styles.billingItem}>
187
+ <RadioButton labelText={t('yes', 'Yes')} value={BOOLEAN_YES} id="sexualAssault_yes" />
188
+ <RadioButton labelText={t('no', 'No')} value={BOOLEAN_NO} id="sexualAssault_no" />
189
+ </RadioButtonGroup>
190
+ )}
191
+ />
192
+ </Column>
193
+ <span className={styles.sectionHeader}>{t('ipvOutcome', 'IPV Outcome')}</span>
194
+ <Column>
195
+ <Controller
196
+ control={form.control}
197
+ name="ipvOutCome"
198
+ render={({ field, fieldState: { error } }) => (
199
+ <>
200
+ {isLoading ? (
201
+ <SelectSkeleton />
202
+ ) : (
203
+ <Dropdown
204
+ ref={field.ref}
205
+ invalid={error?.message}
206
+ invalidText={error?.message}
207
+ id="ipvOutCome"
208
+ titleText={t('ipvOutCome', 'IPV Outcome')}
209
+ onChange={(e) => {
210
+ field.onChange(e.selectedItem);
211
+ }}
212
+ selectedItem={field.value}
213
+ label="Choose option"
214
+ items={contactIPVOutcomeOptions.map((r) => r.value)}
215
+ itemToString={(item) => {
216
+ return contactIPVOutcomeOptions.find((r) => r.value === item)?.label ?? '';
217
+ }}
218
+ />
219
+ )}
220
+ </>
221
+ )}
222
+ />
223
+ </Column>
224
+ </>
225
+ )}
226
+ <Column>
227
+ <Controller
228
+ control={form.control}
229
+ name="livingWithClient"
230
+ render={({ field, fieldState: { error } }) => (
231
+ <>
232
+ {isLoading ? (
233
+ <SelectSkeleton />
234
+ ) : (
235
+ <Dropdown
236
+ ref={field.ref}
237
+ invalid={!!error?.message}
238
+ invalidText={error?.message}
239
+ id="livingWithClient"
240
+ titleText={t('livingWithClient', 'Living with client')}
241
+ onChange={(e: { selectedItem: string }) => {
242
+ field.onChange(e.selectedItem);
243
+ }}
244
+ selectedItem={field.value}
245
+ label="Select"
246
+ items={contactLivingWithPatient.map((r) => r.value)}
247
+ itemToString={(item: string) => contactLivingWithPatient.find((r) => r.value === item)?.label ?? ''}
248
+ />
249
+ )}
250
+ </>
251
+ )}
252
+ />
253
+ </Column>
254
+ <span className={styles.sectionHeader}>{t('baselineInformation', 'Baseline Information')}</span>
255
+ <Column>
256
+ <Controller
257
+ control={form.control}
258
+ name="baselineStatus"
259
+ render={({ field, fieldState: { error } }) => (
260
+ <>
261
+ {isLoading ? (
262
+ <SelectSkeleton />
263
+ ) : (
264
+ <Dropdown
265
+ ref={field.ref}
266
+ invalid={!!error?.message}
267
+ invalidText={error?.message}
268
+ id="baselineStatus"
269
+ titleText={t('baselineStatus', 'HIV Status')}
270
+ onChange={(e: { selectedItem: string }) => {
271
+ field.onChange(e.selectedItem);
272
+ }}
273
+ selectedItem={field.value}
274
+ label="Select HIV Status"
275
+ items={hivStatus.map((r) => r.value)}
276
+ itemToString={(item: string) => hivStatus.find((r) => r.value === item)?.label ?? ''}
277
+ />
278
+ )}
279
+ </>
280
+ )}
281
+ />
282
+ </Column>
283
+ <Column>
284
+ <Controller
285
+ control={form.control}
286
+ name="preferedPNSAproach"
287
+ render={({ field, fieldState: { error } }) => (
288
+ <>
289
+ {isLoading ? (
290
+ <SelectSkeleton />
291
+ ) : (
292
+ <Dropdown
293
+ ref={field.ref}
294
+ invalid={!!error?.message}
295
+ invalidText={error?.message}
296
+ id="preferedPNSAproach"
297
+ titleText={t('preferedPNSAproach', 'Prefered PNS Aproach')}
298
+ onChange={(e: { selectedItem: string }) => {
299
+ field.onChange(e.selectedItem);
300
+ }}
301
+ selectedItem={field.value}
302
+ label="Select Aproach"
303
+ items={pnsAproach.map((r) => r.value)}
304
+ itemToString={(item: string) => pnsAproach.find((r) => r.value === item)?.label ?? ''}
305
+ />
306
+ )}
307
+ </>
308
+ )}
309
+ />
310
+ </Column>
311
+ </>
312
+ );
313
+ };
314
+
315
+ export default RelationshipBaselineInfoFormSection;
@@ -11,7 +11,7 @@ import {
11
11
  TextInput,
12
12
  } from '@carbon/react';
13
13
  import { Calculator } from '@carbon/react/icons';
14
- import { showModal } from '@openmrs/esm-framework';
14
+ import { showModal, useConfig } from '@openmrs/esm-framework';
15
15
  import React, { useMemo } from 'react';
16
16
  import { Controller, useFormContext } from 'react-hook-form';
17
17
  import { useTranslation } from 'react-i18next';
@@ -23,6 +23,7 @@ import { fetchPerson, relationshipFormSchema } from '../relationship.resources';
23
23
  import styles from './form.scss';
24
24
  import { MARITAL_STATUS_CONCEPT_UUID } from '../relationships-constants';
25
25
  import PatientSearchInfo from '../../autosuggest/patient-search-info.component';
26
+ import { ConfigObject } from '../../config-schema';
26
27
 
27
28
  type PatientSearchCreateProps = {};
28
29
 
@@ -33,6 +34,7 @@ const PatientSearchCreate: React.FC<PatientSearchCreateProps> = () => {
33
34
  const abortController = new AbortController();
34
35
  return await fetchPerson(query, abortController);
35
36
  };
37
+ const { requireMaritalStatusOnAgeGreaterThanOrEqualTo } = useConfig<ConfigObject>();
36
38
 
37
39
  const handleAdd = () => form.setValue('mode', 'create');
38
40
  const maritalStatus = useMemo(
@@ -52,6 +54,17 @@ const PatientSearchCreate: React.FC<PatientSearchCreateProps> = () => {
52
54
  };
53
55
 
54
56
  const mode = form.watch('mode');
57
+ const dobObservable = form.watch('personBInfo.birthdate');
58
+ const age = useMemo(() => {
59
+ if (!dobObservable) {
60
+ return '';
61
+ }
62
+ const dob = new Date(dobObservable);
63
+ const today = new Date();
64
+ const ageInMs = today.getTime() - dob.getTime();
65
+ const ageInYears = Math.floor(ageInMs / (1000 * 60 * 60 * 24 * 365.25));
66
+ return ageInYears.toString();
67
+ }, [dobObservable]);
55
68
 
56
69
  return (
57
70
  <>
@@ -198,28 +211,30 @@ const PatientSearchCreate: React.FC<PatientSearchCreateProps> = () => {
198
211
  {t('fromAge', 'From Age')}
199
212
  </Button>
200
213
  </Column>
201
- <Column>
202
- <Controller
203
- control={form.control}
204
- name="personBInfo.maritalStatus"
205
- render={({ field, fieldState: { error } }) => (
206
- <Dropdown
207
- ref={field.ref}
208
- invalid={error?.message}
209
- invalidText={error?.message}
210
- id="maritalStatus"
211
- titleText={t('maritalStatus', 'Marital status')}
212
- onChange={(e) => {
213
- field.onChange(e.selectedItem);
214
- }}
215
- initialSelectedItem={field.value}
216
- label="Choose option"
217
- items={maritalStatus.map((r) => r.value)}
218
- itemToString={(item) => maritalStatus.find((r) => r.value === item)?.label ?? ''}
219
- />
220
- )}
221
- />
222
- </Column>
214
+ {age && Number(age) >= requireMaritalStatusOnAgeGreaterThanOrEqualTo && (
215
+ <Column>
216
+ <Controller
217
+ control={form.control}
218
+ name="personBInfo.maritalStatus"
219
+ render={({ field, fieldState: { error } }) => (
220
+ <Dropdown
221
+ ref={field.ref}
222
+ invalid={error?.message}
223
+ invalidText={error?.message}
224
+ id="maritalStatus"
225
+ titleText={t('maritalStatus', 'Marital status')}
226
+ onChange={(e) => {
227
+ field.onChange(e.selectedItem);
228
+ }}
229
+ initialSelectedItem={field.value}
230
+ label="Choose option"
231
+ items={maritalStatus.map((r) => r.value)}
232
+ itemToString={(item) => maritalStatus.find((r) => r.value === item)?.label ?? ''}
233
+ />
234
+ )}
235
+ />
236
+ </Column>
237
+ )}
223
238
  <span className={styles.sectionHeader}>{t('contact', 'Contact')}</span>
224
239
  <Column>
225
240
  <Controller