@kenyaemr/esm-patient-clinical-view-app 5.4.2-pre.2223 → 5.4.2-pre.2234
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 +7 -7
- package/dist/317.js +1 -1
- package/dist/317.js.map +1 -1
- package/dist/{746.js → 577.js} +2 -2
- package/dist/{746.js.map → 577.js.map} +1 -1
- package/dist/kenyaemr-esm-patient-clinical-view-app.js +1 -1
- package/dist/kenyaemr-esm-patient-clinical-view-app.js.buildmanifest.json +32 -32
- package/dist/main.js +1 -1
- package/dist/main.js.map +1 -1
- package/dist/routes.json +1 -1
- package/package.json +1 -1
- package/src/contact-list/contact-list.resource.tsx +59 -34
- package/src/contact-list/forms/contact-list-update.workspace.tsx +58 -102
- package/src/hooks/useRelationshipTypes.ts +6 -3
- package/src/relationships/forms/baseline-info-form-section.component.tsx +61 -48
- package/src/relationships/relationship.resources.ts +14 -17
- /package/dist/{746.js.LICENSE.txt → 577.js.LICENSE.txt} +0 -0
package/dist/routes.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"kenyaemr":"^19.0.0"},"pages":[],"extensions":[{"name":"clinical-view-section","component":"clinicalViewPatientDashboard","slot":"patient-chart-dashboard-slot"},{"name":"family-history","slot":"patient-chart-family-history-slot","component":"familyHistory","order":0,"online":true,"offline":false},{"name":"relationships-link","component":"relationshipsLink","slot":"patient-chart-dashboard-slot","order":14,"meta":{"columns":1,"columnSpan":1,"slot":"patient-chart-relationships-slot","path":"relationships","layoutMode":"anchored"}},{"name":"relationships","slot":"patient-chart-relationships-slot","component":"relationships","order":0,"online":true,"offline":false},{"name":"contact-list-form","component":"contactListForm"},{"name":"maternal-and-child-health-dashboard-group-link","slot":"clinical-view-section","component":"maternalAndChildHealthSideNavGroup"},{"name":"antenatal-care-dashboard-link","component":"antenatalCareLink","slot":"maternal-and-child-health-slot","meta":{"columns":1,"columnSpan":1,"slot":"patient-chart-antenatal-care-dashboard-slot","path":"antenatal-care-dashboard","layoutMode":"anchored"}},{"name":"antenatal-care-dashboard","slot":"patient-chart-antenatal-care-dashboard-slot","component":"antenatalCare"},{"name":"postnatal-care-dashboard-link","component":"postnatalCareLink","slot":"maternal-and-child-health-slot","meta":{"columns":1,"columnSpan":1,"slot":"patient-chart-postnatal-care-dashboard-slot","path":"postnatal-care-dashboard","layoutMode":"anchored"}},{"name":"postnatal-care-dashboard","slot":"patient-chart-postnatal-care-dashboard-slot","component":"postnatalCare"},{"name":"labour-and-delivery-dashboard-link","component":"labourAndDeliveryLink","slot":"maternal-and-child-health-slot","meta":{"fullWidth":true,"slot":"patient-chart-labour-and-delivery-dashboard-slot","path":"labour-and-delivery-dashboard","layoutMode":"anchored"}},{"name":"labour-and-delivery-dashboard","slot":"patient-chart-labour-and-delivery-dashboard-slot","component":"labourAndDelivery","meta":{"fullWidth":true}},{"name":"hiv-care-and-treatment-dashboard-group-link","slot":"special-clinics-slot","component":"hivCareAndTreatMentSideNavGroup"},{"name":"genericNavLinks","slot":"special-clinics-slot","component":"genericNavLinks","meta":{"columns":1,"columnSpan":1,"slot":"patient-chart-special-clinic-dashboard-slot","path":"special-clinics-dashboard","layoutMode":"anchored"}},{"name":"patient-chart-special-clinic-dashboard-slot","slot":"patient-chart-special-clinic-dashboard-slot","component":"genericDashboard"},{"name":"hts-dashboard-link","component":"htsDashboardLink","slot":"hiv-care-and-treatment-slot","meta":{"columns":1,"columnSpan":1,"slot":"patient-chart-hts-dashboard-slot","path":"hts-dashboard","layoutMode":"anchored"}},{"name":"hts-clinical-view","slot":"patient-chart-hts-dashboard-slot","component":"htsClinicalView","order":2,"online":true,"offline":false},{"name":"defaulter-tracing-dashboard-link","component":"defaulterTracingLink","slot":"hiv-care-and-treatment-slot","meta":{"columns":1,"columnSpan":1,"slot":"patient-chart-defaulter-tracing-dashboard-slot","path":"defaulter-tracing-dashboard","layoutMode":"anchored"}},{"name":"defaulter-tracing-dashboard","slot":"patient-chart-defaulter-tracing-dashboard-slot","component":"defaulterTracing","order":3,"online":true,"offline":false},{"name":"special-clinics-dashboard-group-link","slot":"clinical-view-section","component":"specialClinicsSideNavGroup"},{"name":"clinical-encounter-link","component":"inPatientClinicalEncounterLink","slot":"clinical-view-section","order":40,"meta":{"columns":1,"columnSpan":1,"slot":"patient-chart-clinical-encounter-slot","path":"clinical-encounter","layoutMode":"anchored"}},{"name":"clinical-encounter","slot":"patient-chart-clinical-encounter-slot","component":"inPatientClinicalEncounter","order":0,"online":true,"offline":false},{"component":"caseManagementDashboardLink","name":"case-management-dashboard-link","meta":{"name":"case-management","title":"Case Management","slot":"case-management-dashboard-slot","path":"/case-management"}},{"name":"in-patient-dashboard-link","component":"inPatientChartLink","slot":"patient-chart-dashboard-slot","order":7,"meta":{"slot":"patient-chart-in-patient-dashboard-slot","path":"in-patient","layoutMode":"anchored","columns":1,"columnSpan":1}},{"name":"in-patient-dashboard","slot":"patient-chart-in-patient-dashboard-slot","component":"inPatientChartDashboard","meta":{"fullWidth":false}},{"name":"wrap-component-view","slot":"case-management-dashboard-slot","component":"wrapComponent","order":2,"online":true,"offline":false},{"name":"case-encounter-link","component":"caseEncounterDashboardLink","slot":"patient-chart-dashboard-slot","order":14,"meta":{"columns":1,"columnSpan":1,"slot":"patient-chart-case-encounter-slot","path":"case-management-encounters","layoutMode":"anchored"}},{"name":"case-encounter-table","slot":"patient-chart-case-encounter-slot","component":"caseEncounterTable","order":0,"online":true,"offline":false},{"component":"peerCalendarDashboardLink","name":"peer-calendar-dashboard-link","meta":{"name":"peer-calendar","title":"Peer Calendar","slot":"peer-calendar-dashboard-slot","path":"peer-management"}},{"name":"peer-calendar","slot":"peer-calendar-dashboard-slot","component":"peerCalendar","order":0,"online":true,"offline":false},{"name":"deceased-panel-dashboard-link","component":"deceasedPanelDashboardLink","slot":"patient-chart-dashboard-slot","order":15,"meta":{"columns":1,"columnSpan":1,"slot":"patient-chart-deceased-panel-slot","path":"deceased-panel","layoutMode":"anchored"}},{"name":"deceased-details-tabs","slot":"patient-chart-deceased-panel-slot","component":"deceasedDetailsTabs","order":0,"online":true,"offline":false},{"name":"parto-graph-chart","slot":"patient-chart-labour-and-delivery-dashboard-slot","component":"partograph","meta":{"fullWidth":true}}],"modals":[{"name":"birth-date-calculator","component":"birthDateCalculator"},{"name":"relationship-delete-confirm-dialog","component":"relationshipDeleteConfirmialog"},{"name":"end-relationship-dialog","component":"endRelationshipModal"}],"workspaces":[{"name":"case-management-form","component":"caseManagementForm","title":"Case Management Form","type":"form"},{"name":"family-relationship-form","component":"familyRelationshipForm","title":"Family Relationship Form","type":"form"},{"name":"peers-form","component":"peersForm","title":"Add New Peer","type":"form"},{"name":"kenyaemr-cusom-form-entry-workspace","component":"peerCalendarFormEntry","title":"KVP Peer Educator Outreach Calendar","type":"form","width":"extra-wide","canMaximize":true,"canHide":true},{"name":"contact-list-update-form","component":"contactListUpdateForm","title":"Contact List Update Form","type":"form"},{"name":"other-relationship-form","component":"otherRelationshipsForm","title":"Other Relationships Form","type":"form"},{"name":"end-relationship-form","component":"endRelationshipWorkspace","title":"Discontinue relationship form","type":"form"}],"version":"5.4.2-pre.
|
|
1
|
+
{"$schema":"https://json.openmrs.org/routes.schema.json","backendDependencies":{"kenyaemr":"^19.0.0"},"pages":[],"extensions":[{"name":"clinical-view-section","component":"clinicalViewPatientDashboard","slot":"patient-chart-dashboard-slot"},{"name":"family-history","slot":"patient-chart-family-history-slot","component":"familyHistory","order":0,"online":true,"offline":false},{"name":"relationships-link","component":"relationshipsLink","slot":"patient-chart-dashboard-slot","order":14,"meta":{"columns":1,"columnSpan":1,"slot":"patient-chart-relationships-slot","path":"relationships","layoutMode":"anchored"}},{"name":"relationships","slot":"patient-chart-relationships-slot","component":"relationships","order":0,"online":true,"offline":false},{"name":"contact-list-form","component":"contactListForm"},{"name":"maternal-and-child-health-dashboard-group-link","slot":"clinical-view-section","component":"maternalAndChildHealthSideNavGroup"},{"name":"antenatal-care-dashboard-link","component":"antenatalCareLink","slot":"maternal-and-child-health-slot","meta":{"columns":1,"columnSpan":1,"slot":"patient-chart-antenatal-care-dashboard-slot","path":"antenatal-care-dashboard","layoutMode":"anchored"}},{"name":"antenatal-care-dashboard","slot":"patient-chart-antenatal-care-dashboard-slot","component":"antenatalCare"},{"name":"postnatal-care-dashboard-link","component":"postnatalCareLink","slot":"maternal-and-child-health-slot","meta":{"columns":1,"columnSpan":1,"slot":"patient-chart-postnatal-care-dashboard-slot","path":"postnatal-care-dashboard","layoutMode":"anchored"}},{"name":"postnatal-care-dashboard","slot":"patient-chart-postnatal-care-dashboard-slot","component":"postnatalCare"},{"name":"labour-and-delivery-dashboard-link","component":"labourAndDeliveryLink","slot":"maternal-and-child-health-slot","meta":{"fullWidth":true,"slot":"patient-chart-labour-and-delivery-dashboard-slot","path":"labour-and-delivery-dashboard","layoutMode":"anchored"}},{"name":"labour-and-delivery-dashboard","slot":"patient-chart-labour-and-delivery-dashboard-slot","component":"labourAndDelivery","meta":{"fullWidth":true}},{"name":"hiv-care-and-treatment-dashboard-group-link","slot":"special-clinics-slot","component":"hivCareAndTreatMentSideNavGroup"},{"name":"genericNavLinks","slot":"special-clinics-slot","component":"genericNavLinks","meta":{"columns":1,"columnSpan":1,"slot":"patient-chart-special-clinic-dashboard-slot","path":"special-clinics-dashboard","layoutMode":"anchored"}},{"name":"patient-chart-special-clinic-dashboard-slot","slot":"patient-chart-special-clinic-dashboard-slot","component":"genericDashboard"},{"name":"hts-dashboard-link","component":"htsDashboardLink","slot":"hiv-care-and-treatment-slot","meta":{"columns":1,"columnSpan":1,"slot":"patient-chart-hts-dashboard-slot","path":"hts-dashboard","layoutMode":"anchored"}},{"name":"hts-clinical-view","slot":"patient-chart-hts-dashboard-slot","component":"htsClinicalView","order":2,"online":true,"offline":false},{"name":"defaulter-tracing-dashboard-link","component":"defaulterTracingLink","slot":"hiv-care-and-treatment-slot","meta":{"columns":1,"columnSpan":1,"slot":"patient-chart-defaulter-tracing-dashboard-slot","path":"defaulter-tracing-dashboard","layoutMode":"anchored"}},{"name":"defaulter-tracing-dashboard","slot":"patient-chart-defaulter-tracing-dashboard-slot","component":"defaulterTracing","order":3,"online":true,"offline":false},{"name":"special-clinics-dashboard-group-link","slot":"clinical-view-section","component":"specialClinicsSideNavGroup"},{"name":"clinical-encounter-link","component":"inPatientClinicalEncounterLink","slot":"clinical-view-section","order":40,"meta":{"columns":1,"columnSpan":1,"slot":"patient-chart-clinical-encounter-slot","path":"clinical-encounter","layoutMode":"anchored"}},{"name":"clinical-encounter","slot":"patient-chart-clinical-encounter-slot","component":"inPatientClinicalEncounter","order":0,"online":true,"offline":false},{"component":"caseManagementDashboardLink","name":"case-management-dashboard-link","meta":{"name":"case-management","title":"Case Management","slot":"case-management-dashboard-slot","path":"/case-management"}},{"name":"in-patient-dashboard-link","component":"inPatientChartLink","slot":"patient-chart-dashboard-slot","order":7,"meta":{"slot":"patient-chart-in-patient-dashboard-slot","path":"in-patient","layoutMode":"anchored","columns":1,"columnSpan":1}},{"name":"in-patient-dashboard","slot":"patient-chart-in-patient-dashboard-slot","component":"inPatientChartDashboard","meta":{"fullWidth":false}},{"name":"wrap-component-view","slot":"case-management-dashboard-slot","component":"wrapComponent","order":2,"online":true,"offline":false},{"name":"case-encounter-link","component":"caseEncounterDashboardLink","slot":"patient-chart-dashboard-slot","order":14,"meta":{"columns":1,"columnSpan":1,"slot":"patient-chart-case-encounter-slot","path":"case-management-encounters","layoutMode":"anchored"}},{"name":"case-encounter-table","slot":"patient-chart-case-encounter-slot","component":"caseEncounterTable","order":0,"online":true,"offline":false},{"component":"peerCalendarDashboardLink","name":"peer-calendar-dashboard-link","meta":{"name":"peer-calendar","title":"Peer Calendar","slot":"peer-calendar-dashboard-slot","path":"peer-management"}},{"name":"peer-calendar","slot":"peer-calendar-dashboard-slot","component":"peerCalendar","order":0,"online":true,"offline":false},{"name":"deceased-panel-dashboard-link","component":"deceasedPanelDashboardLink","slot":"patient-chart-dashboard-slot","order":15,"meta":{"columns":1,"columnSpan":1,"slot":"patient-chart-deceased-panel-slot","path":"deceased-panel","layoutMode":"anchored"}},{"name":"deceased-details-tabs","slot":"patient-chart-deceased-panel-slot","component":"deceasedDetailsTabs","order":0,"online":true,"offline":false},{"name":"parto-graph-chart","slot":"patient-chart-labour-and-delivery-dashboard-slot","component":"partograph","meta":{"fullWidth":true}}],"modals":[{"name":"birth-date-calculator","component":"birthDateCalculator"},{"name":"relationship-delete-confirm-dialog","component":"relationshipDeleteConfirmialog"},{"name":"end-relationship-dialog","component":"endRelationshipModal"}],"workspaces":[{"name":"case-management-form","component":"caseManagementForm","title":"Case Management Form","type":"form"},{"name":"family-relationship-form","component":"familyRelationshipForm","title":"Family Relationship Form","type":"form"},{"name":"peers-form","component":"peersForm","title":"Add New Peer","type":"form"},{"name":"kenyaemr-cusom-form-entry-workspace","component":"peerCalendarFormEntry","title":"KVP Peer Educator Outreach Calendar","type":"form","width":"extra-wide","canMaximize":true,"canHide":true},{"name":"contact-list-update-form","component":"contactListUpdateForm","title":"Contact List Update Form","type":"form"},{"name":"other-relationship-form","component":"otherRelationshipsForm","title":"Other Relationships Form","type":"form"},{"name":"end-relationship-form","component":"endRelationshipWorkspace","title":"Discontinue relationship form","type":"form"}],"version":"5.4.2-pre.2234"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kenyaemr/esm-patient-clinical-view-app",
|
|
3
|
-
"version": "5.4.2-pre.
|
|
3
|
+
"version": "5.4.2-pre.2234",
|
|
4
4
|
"description": "Patient clinical view microfrontend for the OpenMRS SPA",
|
|
5
5
|
"browser": "dist/kenyaemr-esm-patient-clinical-view-app.js",
|
|
6
6
|
"main": "src/index.ts",
|
|
@@ -167,48 +167,73 @@ export const updatePersonAttributes = (payload: any, personUuid: string, attribu
|
|
|
167
167
|
});
|
|
168
168
|
};
|
|
169
169
|
|
|
170
|
+
const ATTRIBUTE_MAPPINGS = [
|
|
171
|
+
{ key: 'baselineStatus', configPath: 'baselineHIVStatus', cleanValue: true },
|
|
172
|
+
{ key: 'preferedPNSAproach', configPath: 'preferedPnsAproach', cleanValue: true },
|
|
173
|
+
{ key: 'livingWithClient', configPath: 'livingWithContact', cleanValue: true },
|
|
174
|
+
{ key: 'ipvOutCome', configPath: 'contactIPVOutcome', cleanValue: false },
|
|
175
|
+
];
|
|
176
|
+
|
|
177
|
+
const createAttribute = (
|
|
178
|
+
attributeType: string,
|
|
179
|
+
value: string,
|
|
180
|
+
existingAttributes: Person['attributes'],
|
|
181
|
+
cleanValue: boolean = true,
|
|
182
|
+
) => ({
|
|
183
|
+
attributeType,
|
|
184
|
+
value: cleanValue ? replaceAll(value, 'A', '') : value,
|
|
185
|
+
attribute: existingAttributes.find((a) => a.attributeType.uuid === attributeType)?.uuid,
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
const buildAttributes = (
|
|
189
|
+
attributes: ContactAttributeData,
|
|
190
|
+
config: ConfigObject,
|
|
191
|
+
existingAttributes: Person['attributes'] = [],
|
|
192
|
+
) => {
|
|
193
|
+
if (!attributes) {
|
|
194
|
+
return [];
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
return ATTRIBUTE_MAPPINGS.filter((m) => attributes[m.key]).map((m) =>
|
|
198
|
+
createAttribute(
|
|
199
|
+
config.contactPersonAttributesUuid[m.configPath],
|
|
200
|
+
attributes[m.key],
|
|
201
|
+
existingAttributes,
|
|
202
|
+
m.cleanValue,
|
|
203
|
+
),
|
|
204
|
+
);
|
|
205
|
+
};
|
|
206
|
+
|
|
170
207
|
export const updateContactAttributes = async (
|
|
171
208
|
personUuid: string,
|
|
172
|
-
|
|
209
|
+
attributes: ContactAttributeData,
|
|
173
210
|
config: ConfigObject,
|
|
174
211
|
existingAttributes: Person['attributes'] = [],
|
|
175
212
|
) => {
|
|
176
213
|
try {
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
},
|
|
190
|
-
{
|
|
191
|
-
attributeType: config?.contactPersonAttributesUuid?.contactIPVOutcome,
|
|
192
|
-
value: attributeData?.ipvOutCome,
|
|
193
|
-
},
|
|
194
|
-
].filter((attr) => attr?.value !== undefined && attr?.value !== null && attr?.value !== '');
|
|
195
|
-
|
|
196
|
-
await Promise.allSettled(
|
|
197
|
-
updatableAttributes?.map((attr) => {
|
|
198
|
-
const existingAttribute = existingAttributes?.find((at) => at?.attributeType?.uuid === attr?.attributeType);
|
|
199
|
-
|
|
200
|
-
const payload = {
|
|
201
|
-
attributeType: attr?.attributeType,
|
|
202
|
-
value: attr?.value,
|
|
203
|
-
};
|
|
204
|
-
|
|
205
|
-
if (!existingAttribute?.uuid) {
|
|
206
|
-
return createPersonAttribute(payload, personUuid);
|
|
207
|
-
}
|
|
208
|
-
return updatePersonAttributes(payload, personUuid, existingAttribute.uuid);
|
|
209
|
-
}),
|
|
214
|
+
const attrs = buildAttributes(attributes, config, existingAttributes);
|
|
215
|
+
|
|
216
|
+
const results = await Promise.allSettled(
|
|
217
|
+
attrs.map((attr) =>
|
|
218
|
+
openmrsFetch(`${restBaseUrl}/person/${personUuid}/attribute/${attr.attribute ?? ''}`, {
|
|
219
|
+
method: 'POST',
|
|
220
|
+
headers: {
|
|
221
|
+
'Content-Type': 'application/json',
|
|
222
|
+
},
|
|
223
|
+
body: JSON.stringify(omit(attr, ['attribute'])),
|
|
224
|
+
}),
|
|
225
|
+
),
|
|
210
226
|
);
|
|
211
227
|
|
|
228
|
+
if (results.length && results.every((r) => r.status === 'fulfilled')) {
|
|
229
|
+
showSnackbar({ title: 'Success ', kind: 'success', subtitle: 'Patient attributes updated succesfully' });
|
|
230
|
+
}
|
|
231
|
+
results.forEach((res) => {
|
|
232
|
+
if (res.status === 'rejected') {
|
|
233
|
+
showSnackbar({ title: 'Error updating patient attribute', kind: 'error', subtitle: res.reason?.message });
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
|
|
212
237
|
mutate((key) => typeof key === 'string' && key.startsWith(`${restBaseUrl}/person`));
|
|
213
238
|
} catch (error) {
|
|
214
239
|
showSnackbar({
|
|
@@ -9,39 +9,30 @@ import {
|
|
|
9
9
|
InlineLoading,
|
|
10
10
|
Stack,
|
|
11
11
|
Tile,
|
|
12
|
-
RadioButtonGroup,
|
|
13
|
-
RadioButton,
|
|
14
12
|
} from '@carbon/react';
|
|
15
13
|
import { zodResolver } from '@hookform/resolvers/zod';
|
|
16
14
|
import { DefaultWorkspaceProps, parseDate, restBaseUrl, showSnackbar, useConfig } from '@openmrs/esm-framework';
|
|
17
|
-
import
|
|
15
|
+
import dayjs from 'dayjs';
|
|
16
|
+
import React, { useEffect, useMemo, useRef } from 'react';
|
|
18
17
|
import { Controller, FormProvider, useForm } from 'react-hook-form';
|
|
19
18
|
import { useTranslation } from 'react-i18next';
|
|
20
19
|
import { mutate } from 'swr';
|
|
21
20
|
import { z } from 'zod';
|
|
21
|
+
import PatientInfo from '../../case-management/workspace/patient-info.component';
|
|
22
|
+
import { ConfigObject } from '../../config-schema';
|
|
23
|
+
import { updateContactAttributes } from '../../contact-list/contact-list.resource';
|
|
24
|
+
import usePersonAttributes from '../../hooks/usePersonAttributes';
|
|
22
25
|
import useRelationship from '../../hooks/useRelationship';
|
|
23
26
|
import useRelationshipTypes from '../../hooks/useRelationshipTypes';
|
|
24
|
-
import
|
|
25
|
-
import PatientInfo from '../../case-management/workspace/patient-info.component';
|
|
26
|
-
import usePerson, { contactIPVOutcomeOptions, updateContactAttributes } from '../../contact-list/contact-list.resource';
|
|
27
|
+
import RelationshipBaselineInfoFormSection from '../../relationships/forms/baseline-info-form-section.component';
|
|
27
28
|
import {
|
|
28
|
-
BOOLEAN_NO,
|
|
29
|
-
BOOLEAN_YES,
|
|
30
29
|
relationshipFormSchema,
|
|
31
30
|
relationshipUpdateFormSchema,
|
|
32
31
|
updateRelationship,
|
|
33
32
|
usePatientBirthdate,
|
|
34
33
|
} from '../../relationships/relationship.resources';
|
|
35
34
|
import { type Contact } from '../../types';
|
|
36
|
-
import
|
|
37
|
-
import { contactListConceptMap } from '../../contact-list/contact-list-concept-map';
|
|
38
|
-
import {
|
|
39
|
-
LIVING_WITH_PATIENT_CONCEPT_UUID,
|
|
40
|
-
PARTNER_HIV_STATUS_CONCEPT_UUID,
|
|
41
|
-
PNS_APROACH_CONCEPT_UUID,
|
|
42
|
-
} from '../../relationships/relationships-constants';
|
|
43
|
-
import dayjs from 'dayjs';
|
|
44
|
-
import RelationshipBaselineInfoFormSection from '../../relationships/forms/baseline-info-form-section.component';
|
|
35
|
+
import styles from './contact-list-update.scss';
|
|
45
36
|
|
|
46
37
|
interface ContactListUpdateFormProps extends DefaultWorkspaceProps {
|
|
47
38
|
relation: Contact;
|
|
@@ -54,35 +45,19 @@ type ContactListUpdateFormType = z.infer<typeof relationshipFormSchema>;
|
|
|
54
45
|
const ContactListUpdateForm: React.FC<ContactListUpdateFormProps> = ({ closeWorkspace, relation, patientUuid }) => {
|
|
55
46
|
const { error, isLoading, relationship } = useRelationship(relation?.uuid);
|
|
56
47
|
const { isLoading: typesLoading, error: typesError, relationshipTypes } = useRelationshipTypes();
|
|
57
|
-
const
|
|
48
|
+
const personUuid = relationship?.personB?.uuid;
|
|
49
|
+
const { attributes } = usePersonAttributes(personUuid);
|
|
58
50
|
const config = useConfig<ConfigObject>();
|
|
59
|
-
|
|
60
51
|
const { t } = useTranslation();
|
|
61
|
-
|
|
62
|
-
const pnsAproach = useMemo(
|
|
63
|
-
() =>
|
|
64
|
-
Object.entries(contactListConceptMap[PNS_APROACH_CONCEPT_UUID].answers).map(([uuid, display]) => ({
|
|
65
|
-
label: display,
|
|
66
|
-
value: uuid,
|
|
67
|
-
})),
|
|
68
|
-
[],
|
|
69
|
-
);
|
|
70
|
-
|
|
71
|
-
const contactLivingWithPatient = useMemo(
|
|
72
|
-
() =>
|
|
73
|
-
Object.entries(contactListConceptMap[LIVING_WITH_PATIENT_CONCEPT_UUID].answers).map(([uuid, display]) => ({
|
|
74
|
-
label: display,
|
|
75
|
-
value: uuid,
|
|
76
|
-
})),
|
|
77
|
-
[],
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
const form = useForm<ContactListUpdateFormType>({
|
|
81
|
-
defaultValues: {},
|
|
52
|
+
const form = useForm<z.infer<typeof relationshipUpdateFormSchema>>({
|
|
82
53
|
resolver: zodResolver(relationshipUpdateFormSchema),
|
|
54
|
+
mode: 'all',
|
|
83
55
|
});
|
|
56
|
+
const { setValue } = form;
|
|
57
|
+
const { isLoading: isPatientLoading, birthdate } = usePatientBirthdate(relationship?.personB?.uuid);
|
|
84
58
|
|
|
85
|
-
|
|
59
|
+
// Use ref to track if initial values have been set
|
|
60
|
+
const initialValuesSet = useRef(false);
|
|
86
61
|
|
|
87
62
|
const patientAgeMonths = useMemo(() => {
|
|
88
63
|
let birthDate = birthdate ? parseDate(birthdate) : null;
|
|
@@ -93,81 +68,54 @@ const ContactListUpdateForm: React.FC<ContactListUpdateFormProps> = ({ closeWork
|
|
|
93
68
|
}, [birthdate]);
|
|
94
69
|
|
|
95
70
|
useEffect(() => {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
form.setValue('endDate', new Date(relationship.endDate));
|
|
99
|
-
}
|
|
71
|
+
// Only set initial values once when data is available and not already set
|
|
72
|
+
if (relationshipTypes.length > 0 && !initialValuesSet.current && relationship) {
|
|
100
73
|
if (relationship.startDate) {
|
|
101
|
-
form.setValue('startDate', new Date(relationship.startDate));
|
|
102
|
-
}
|
|
103
|
-
if (relationship.relationshipType) {
|
|
104
|
-
form.setValue('relationshipType', relationship.relationshipType.uuid);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}, [relationship, relationshipTypes, form]);
|
|
108
|
-
|
|
109
|
-
useEffect(() => {
|
|
110
|
-
if (relation) {
|
|
111
|
-
if (relation.startDate) {
|
|
112
74
|
try {
|
|
113
|
-
const startDate =
|
|
114
|
-
|
|
75
|
+
const startDate = parseDate(relationship.startDate);
|
|
76
|
+
setValue('startDate', startDate);
|
|
115
77
|
} catch (e) {
|
|
116
|
-
console.warn('Invalid start date format:',
|
|
78
|
+
console.warn('Invalid start date format:', relationship.startDate);
|
|
117
79
|
}
|
|
118
80
|
}
|
|
119
81
|
|
|
120
|
-
if (
|
|
82
|
+
if (relationship.endDate) {
|
|
121
83
|
try {
|
|
122
|
-
const endDate =
|
|
123
|
-
|
|
84
|
+
const endDate = parseDate(relationship.endDate);
|
|
85
|
+
setValue('endDate', endDate);
|
|
124
86
|
} catch (e) {
|
|
125
|
-
console.warn('Invalid end date format:',
|
|
87
|
+
console.warn('Invalid end date format:', relationship.endDate);
|
|
126
88
|
}
|
|
127
89
|
}
|
|
128
90
|
|
|
129
|
-
if (
|
|
130
|
-
|
|
131
|
-
(type) =>
|
|
132
|
-
type.displayBIsToA === relation.relationshipType || type.displayAIsToB === relation.relationshipType,
|
|
133
|
-
);
|
|
134
|
-
if (relType) {
|
|
135
|
-
form.setValue('relationshipType', relType.uuid);
|
|
136
|
-
}
|
|
91
|
+
if (relationship.relationshipType?.uuid) {
|
|
92
|
+
setValue('relationshipType', relationship.relationshipType.uuid);
|
|
137
93
|
}
|
|
94
|
+
|
|
95
|
+
// Mark that initial values have been set
|
|
96
|
+
initialValuesSet.current = true;
|
|
138
97
|
}
|
|
139
|
-
}, [
|
|
98
|
+
}, [relationshipTypes, setValue, relationship]);
|
|
140
99
|
|
|
141
100
|
const onSubmit = async (values: ContactListUpdateFormType) => {
|
|
142
101
|
try {
|
|
143
|
-
const data = form.getValues();
|
|
144
|
-
|
|
145
102
|
await updateRelationship(relationship.uuid, values);
|
|
146
|
-
|
|
147
103
|
await updateContactAttributes(
|
|
148
|
-
|
|
104
|
+
personUuid,
|
|
149
105
|
{
|
|
150
|
-
baselineStatus:
|
|
151
|
-
preferedPNSAproach:
|
|
152
|
-
livingWithClient:
|
|
153
|
-
ipvOutCome:
|
|
106
|
+
baselineStatus: values?.baselineStatus,
|
|
107
|
+
preferedPNSAproach: values?.preferedPNSAproach,
|
|
108
|
+
livingWithClient: values?.livingWithClient,
|
|
109
|
+
ipvOutCome: values?.ipvOutCome,
|
|
154
110
|
},
|
|
155
111
|
config,
|
|
156
|
-
|
|
157
|
-
uuid: attr.uuid,
|
|
158
|
-
display: attr.display ?? '',
|
|
159
|
-
value: attr.value,
|
|
160
|
-
attributeType: {
|
|
161
|
-
uuid: attr.attributeType.uuid,
|
|
162
|
-
display: attr.attributeType.display ?? '',
|
|
163
|
-
},
|
|
164
|
-
})),
|
|
112
|
+
attributes,
|
|
165
113
|
);
|
|
166
114
|
|
|
167
115
|
showSnackbar({
|
|
168
116
|
title: 'Success',
|
|
169
117
|
kind: 'success',
|
|
170
|
-
subtitle: t('relationshipUpdated', '
|
|
118
|
+
subtitle: t('relationshipUpdated', 'Relationship updated successfully'),
|
|
171
119
|
});
|
|
172
120
|
|
|
173
121
|
mutate((key) => {
|
|
@@ -178,13 +126,13 @@ const ContactListUpdateForm: React.FC<ContactListUpdateFormProps> = ({ closeWork
|
|
|
178
126
|
} catch (error) {
|
|
179
127
|
showSnackbar({
|
|
180
128
|
title: 'Error',
|
|
181
|
-
subtitle: 'Failure updating relationship! ' + JSON.stringify(error),
|
|
129
|
+
subtitle: 'Failure updating relationship! ' + (error?.message || JSON.stringify(error)),
|
|
182
130
|
kind: 'error',
|
|
183
131
|
});
|
|
184
132
|
}
|
|
185
133
|
};
|
|
186
134
|
|
|
187
|
-
if (isLoading || typesLoading) {
|
|
135
|
+
if (isLoading || typesLoading || isPatientLoading) {
|
|
188
136
|
return (
|
|
189
137
|
<div className={styles.loading}>
|
|
190
138
|
<InlineLoading status="active" iconDescription="Loading" description="Loading form..." />
|
|
@@ -203,6 +151,17 @@ const ContactListUpdateForm: React.FC<ContactListUpdateFormProps> = ({ closeWork
|
|
|
203
151
|
);
|
|
204
152
|
}
|
|
205
153
|
|
|
154
|
+
if (!relationship) {
|
|
155
|
+
return (
|
|
156
|
+
<div className={styles.error}>
|
|
157
|
+
<Tile id="no-relationship">
|
|
158
|
+
<strong>Error:</strong>
|
|
159
|
+
<p>{t('noRelationshipFound', 'No relationship data found')}</p>
|
|
160
|
+
</Tile>
|
|
161
|
+
</div>
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
|
|
206
165
|
return (
|
|
207
166
|
<FormProvider {...form}>
|
|
208
167
|
<Form onSubmit={form.handleSubmit(onSubmit)}>
|
|
@@ -210,7 +169,6 @@ const ContactListUpdateForm: React.FC<ContactListUpdateFormProps> = ({ closeWork
|
|
|
210
169
|
<Column>
|
|
211
170
|
<PatientInfo patientUuid={relationship?.personB?.uuid || relation?.relativeUuid} />
|
|
212
171
|
</Column>
|
|
213
|
-
|
|
214
172
|
<span className={styles.sectionHeader}>{t('relationship', 'Relationship')}</span>
|
|
215
173
|
<Column>
|
|
216
174
|
<Controller
|
|
@@ -221,7 +179,8 @@ const ContactListUpdateForm: React.FC<ContactListUpdateFormProps> = ({ closeWork
|
|
|
221
179
|
className={styles.datePickerInput}
|
|
222
180
|
dateFormat="d/m/Y"
|
|
223
181
|
datePickerType="single"
|
|
224
|
-
{
|
|
182
|
+
value={field.value}
|
|
183
|
+
onChange={(v) => field.onChange(v[0])}
|
|
225
184
|
ref={undefined}
|
|
226
185
|
invalid={!!error?.message}
|
|
227
186
|
invalidText={error?.message}>
|
|
@@ -247,15 +206,15 @@ const ContactListUpdateForm: React.FC<ContactListUpdateFormProps> = ({ closeWork
|
|
|
247
206
|
className={styles.datePickerInput}
|
|
248
207
|
dateFormat="d/m/Y"
|
|
249
208
|
datePickerType="single"
|
|
250
|
-
{
|
|
251
|
-
|
|
209
|
+
value={field.value}
|
|
210
|
+
onChange={(dates) => field.onChange(dates[0])}
|
|
252
211
|
invalid={!!error?.message}
|
|
253
212
|
invalidText={error?.message}>
|
|
254
213
|
<DatePickerInput
|
|
255
214
|
id="enddate-input"
|
|
256
215
|
invalid={!!error?.message}
|
|
257
216
|
invalidText={error?.message}
|
|
258
|
-
placeholder="mm/
|
|
217
|
+
placeholder="dd/mm/yyyy"
|
|
259
218
|
labelText={t('endDate', 'End Date')}
|
|
260
219
|
size="lg"
|
|
261
220
|
/>
|
|
@@ -287,10 +246,7 @@ const ContactListUpdateForm: React.FC<ContactListUpdateFormProps> = ({ closeWork
|
|
|
287
246
|
/>
|
|
288
247
|
</Column>
|
|
289
248
|
|
|
290
|
-
<RelationshipBaselineInfoFormSection
|
|
291
|
-
patientAgeMonths={patientAgeMonths}
|
|
292
|
-
patientUuid={relationship?.personB?.uuid}
|
|
293
|
-
/>
|
|
249
|
+
<RelationshipBaselineInfoFormSection patientAgeMonths={patientAgeMonths} patientUuid={personUuid} />
|
|
294
250
|
</Stack>
|
|
295
251
|
|
|
296
252
|
<ButtonSet className={styles.buttonSet}>
|
|
@@ -298,7 +254,7 @@ const ContactListUpdateForm: React.FC<ContactListUpdateFormProps> = ({ closeWork
|
|
|
298
254
|
{t('discard', 'Discard')}
|
|
299
255
|
</Button>
|
|
300
256
|
<Button className={styles.button} kind="primary" type="submit" disabled={form.formState.isSubmitting}>
|
|
301
|
-
{t('submit', 'Submit')}
|
|
257
|
+
{form.formState.isSubmitting ? t('submitting', 'Submitting...') : t('submit', 'Submit')}
|
|
302
258
|
</Button>
|
|
303
259
|
</ButtonSet>
|
|
304
260
|
</Form>
|
|
@@ -1,16 +1,19 @@
|
|
|
1
|
-
import { openmrsFetch } from '@openmrs/esm-framework';
|
|
1
|
+
import { openmrsFetch, useConfig } from '@openmrs/esm-framework';
|
|
2
2
|
import useSWR from 'swr';
|
|
3
3
|
import { RelationShipType } from '../types';
|
|
4
|
+
import { ConfigObject } from '../config-schema';
|
|
4
5
|
|
|
5
6
|
const useRelationshipTypes = () => {
|
|
6
7
|
const customeRepresentation = 'custom:(uuid,displayAIsToB,displayBIsToA)';
|
|
7
8
|
const url = `/ws/rest/v1/relationshiptype?v=${customeRepresentation}`;
|
|
8
|
-
|
|
9
|
+
const { relationshipTypesList } = useConfig<ConfigObject>();
|
|
9
10
|
const { data, error, isLoading } = useSWR<{ data: { results: RelationShipType[] } }>(url, openmrsFetch);
|
|
10
11
|
return {
|
|
11
12
|
error,
|
|
12
13
|
isLoading,
|
|
13
|
-
relationshipTypes: data?.data?.results ?? []
|
|
14
|
+
relationshipTypes: (data?.data?.results ?? []).filter(
|
|
15
|
+
(r) => relationshipTypesList.findIndex((rl) => rl.uuid === r.uuid) !== -1,
|
|
16
|
+
),
|
|
14
17
|
};
|
|
15
18
|
};
|
|
16
19
|
|
|
@@ -45,6 +45,7 @@ const RelationshipBaselineInfoFormSection: FC<RelationshipBaselineInfoFormSectio
|
|
|
45
45
|
const config = useConfig<ConfigObject>();
|
|
46
46
|
const { setValue } = form;
|
|
47
47
|
const { attributes, isLoading } = usePersonAttributes(patientUuid);
|
|
48
|
+
|
|
48
49
|
const pnsAproach = useMemo(
|
|
49
50
|
() =>
|
|
50
51
|
Object.entries(contactListConceptMap[PNS_APROACH_CONCEPT_UUID].answers).map(([uuid, display]) => ({
|
|
@@ -92,6 +93,7 @@ const RelationshipBaselineInfoFormSection: FC<RelationshipBaselineInfoFormSectio
|
|
|
92
93
|
[patientAgeMonths],
|
|
93
94
|
);
|
|
94
95
|
|
|
96
|
+
// useEffect for IPV outcome logic
|
|
95
97
|
useEffect(() => {
|
|
96
98
|
if ([observablePhysicalAssault, observableThreatened, observableSexualAssault].includes(BOOLEAN_YES)) {
|
|
97
99
|
form.setValue('ipvOutCome', 'True');
|
|
@@ -103,68 +105,79 @@ const RelationshipBaselineInfoFormSection: FC<RelationshipBaselineInfoFormSectio
|
|
|
103
105
|
if (!showIPVRelatedFields) {
|
|
104
106
|
form.setValue('ipvOutCome', undefined);
|
|
105
107
|
}
|
|
106
|
-
}, [
|
|
107
|
-
observablePhysicalAssault,
|
|
108
|
-
observableThreatened,
|
|
109
|
-
observableSexualAssault,
|
|
110
|
-
observableRelationship,
|
|
111
|
-
form,
|
|
112
|
-
showIPVRelatedFields,
|
|
113
|
-
]);
|
|
108
|
+
}, [observablePhysicalAssault, observableThreatened, observableSexualAssault, form, showIPVRelatedFields]);
|
|
114
109
|
|
|
110
|
+
// Set initial values from attributes only once
|
|
115
111
|
useEffect(() => {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
if (
|
|
123
|
-
|
|
112
|
+
// Only set values once when attributes are loaded and not already set
|
|
113
|
+
if (attributes.length > 0 || hivStatusPersonB) {
|
|
114
|
+
if (attributes.length > 0) {
|
|
115
|
+
const hivStatusAttribute = attributes.find(
|
|
116
|
+
(a) => a.attributeType.uuid === config.contactPersonAttributesUuid.baselineHIVStatus,
|
|
117
|
+
);
|
|
118
|
+
if (hivStatusAttribute) {
|
|
119
|
+
const value = hivStatus.find((r) => r.value.startsWith(hivStatusAttribute.value))?.value;
|
|
120
|
+
if (value) {
|
|
121
|
+
setValue('baselineStatus', value);
|
|
122
|
+
}
|
|
124
123
|
}
|
|
125
|
-
|
|
126
|
-
const
|
|
127
|
-
|
|
128
|
-
|
|
124
|
+
|
|
125
|
+
const pnsAproachAttribute = attributes.find(
|
|
126
|
+
(a) => a.attributeType.uuid === config.contactPersonAttributesUuid.preferedPnsAproach,
|
|
127
|
+
);
|
|
128
|
+
if (pnsAproachAttribute) {
|
|
129
|
+
const value = pnsAproach.find((r) => r.value.startsWith(pnsAproachAttribute.value))?.value;
|
|
130
|
+
if (value) {
|
|
131
|
+
setValue('preferedPNSAproach', value);
|
|
132
|
+
}
|
|
129
133
|
}
|
|
130
|
-
}
|
|
131
134
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
135
|
+
const livingWithPatientAttribute = attributes.find(
|
|
136
|
+
(a) => a.attributeType.uuid === config.contactPersonAttributesUuid.livingWithContact,
|
|
137
|
+
);
|
|
138
|
+
if (livingWithPatientAttribute) {
|
|
139
|
+
const value = contactLivingWithPatient.find((r) =>
|
|
140
|
+
r.value.startsWith(livingWithPatientAttribute.value),
|
|
141
|
+
)?.value;
|
|
142
|
+
if (value) {
|
|
143
|
+
setValue('livingWithClient', value);
|
|
144
|
+
}
|
|
139
145
|
}
|
|
140
|
-
}
|
|
141
146
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
147
|
+
const ipvAttr = attributes.find(
|
|
148
|
+
(a) => a.attributeType.uuid === config.contactPersonAttributesUuid.contactIPVOutcome,
|
|
149
|
+
);
|
|
150
|
+
if (ipvAttr) {
|
|
151
|
+
const value = contactIPVOutcomeOptions.find((r) => r.value.startsWith(ipvAttr.value))?.value;
|
|
152
|
+
if (value) {
|
|
153
|
+
setValue('ipvOutCome', value as any);
|
|
154
|
+
}
|
|
149
155
|
}
|
|
150
156
|
}
|
|
151
157
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
158
|
+
// Handle hivStatusPersonB when no attributes but HIV status is available
|
|
159
|
+
if (
|
|
160
|
+
hivStatusPersonB &&
|
|
161
|
+
!attributes.find((a) => a.attributeType.uuid === config.contactPersonAttributesUuid.baselineHIVStatus)
|
|
162
|
+
) {
|
|
163
|
+
const value = hivStatus.find((r) => r.label.toLowerCase().includes(hivStatusPersonB.toLowerCase()))?.value;
|
|
157
164
|
if (value) {
|
|
158
|
-
setValue('
|
|
165
|
+
setValue('baselineStatus', value);
|
|
159
166
|
}
|
|
160
167
|
}
|
|
161
|
-
} else if (hivStatusPersonB) {
|
|
162
|
-
const value = hivStatus.find((r) => r.label.toLowerCase().includes(hivStatusPersonB.toLowerCase()))?.value;
|
|
163
|
-
if (value) {
|
|
164
|
-
setValue('baselineStatus', value);
|
|
165
|
-
}
|
|
166
168
|
}
|
|
167
|
-
}, [
|
|
169
|
+
}, [
|
|
170
|
+
attributes,
|
|
171
|
+
contactLivingWithPatient,
|
|
172
|
+
hivStatus,
|
|
173
|
+
hivStatusPersonB,
|
|
174
|
+
pnsAproach,
|
|
175
|
+
setValue,
|
|
176
|
+
config.contactPersonAttributesUuid.baselineHIVStatus,
|
|
177
|
+
config.contactPersonAttributesUuid.preferedPnsAproach,
|
|
178
|
+
config.contactPersonAttributesUuid.livingWithContact,
|
|
179
|
+
config.contactPersonAttributesUuid.contactIPVOutcome,
|
|
180
|
+
]);
|
|
168
181
|
|
|
169
182
|
return (
|
|
170
183
|
<>
|