diff --git a/packages/esm-patient-clinical-view-app/src/case-management/workspace/case-management.resource.ts b/packages/esm-patient-clinical-view-app/src/case-management/workspace/case-management.resource.ts index 3498bef0f..76b32c270 100644 --- a/packages/esm-patient-clinical-view-app/src/case-management/workspace/case-management.resource.ts +++ b/packages/esm-patient-clinical-view-app/src/case-management/workspace/case-management.resource.ts @@ -14,9 +14,11 @@ interface CaseManagerResponse { results: CaseManager[]; } -interface RelationshipType { +export interface RelationshipType { uuid: string; display: string; + displayAIsToB: string; + displayBIsToA: string; } export interface RelationshipTypeResponse { @@ -49,14 +51,6 @@ export const useCaseManagers = () => { return { data, error }; }; -export const useRelationshipType = () => { - const customRepresentation = 'custom:(uuid,display)&q=Case manager'; - const url = `/ws/rest/v1/relationshiptype?v=${customRepresentation}`; - const { data, error } = useSWRImmutable<{ data: RelationshipTypeResponse }>(url, openmrsFetch); - - return { data, error }; -}; - export const saveRelationship = (payload) => { const url = `/ws/rest/v1/relationship`; return openmrsFetch(url, { diff --git a/packages/esm-patient-clinical-view-app/src/case-management/workspace/case-management.workspace.tsx b/packages/esm-patient-clinical-view-app/src/case-management/workspace/case-management.workspace.tsx index 2beeb9282..c5c6dc123 100644 --- a/packages/esm-patient-clinical-view-app/src/case-management/workspace/case-management.workspace.tsx +++ b/packages/esm-patient-clinical-view-app/src/case-management/workspace/case-management.workspace.tsx @@ -1,25 +1,26 @@ -import React, { useMemo, useState } from 'react'; +import React, { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { Column, TextArea, Form, Stack, ButtonSet, ComboBox, Button, DatePicker, DatePickerInput } from '@carbon/react'; -import { useForm, Controller } from 'react-hook-form'; +import { useForm, Controller, SubmitHandler } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; import styles from './case-management.scss'; -import { ExtensionSlot, navigate, showSnackbar, useSession } from '@openmrs/esm-framework'; -import { saveRelationship, useActivecases, useCaseManagers, useRelationshipType } from './case-management.resource'; +import { ExtensionSlot, showSnackbar, useConfig, useSession } from '@openmrs/esm-framework'; +import { saveRelationship, useActivecases, useCaseManagers } from './case-management.resource'; import { extractNameString, uppercaseText } from '../../utils/expression-helper'; import PatientInfo from './patient-info.component'; -import { caseManagementConceptMap } from './case-management-concept-map'; +import { useMappedRelationshipTypes } from '../../family-partner-history/relationships.resource'; const schema = z.object({ caseManager: z.string().nonempty({ message: 'Case Manager is required' }), relationship: z.string().nonempty({ message: 'Relationship is required' }), startDate: z.date({ required_error: 'Start Date is required' }), - // reasons: z.string().nonempty({ message: 'At least one reason is required' }), endDate: z.date().optional(), notes: z.string().optional(), }); +type FormData = z.infer<typeof schema>; + type CaseManagementProp = { closeWorkspace: () => void; }; @@ -29,9 +30,17 @@ const CaseManagementForm: React.FC<CaseManagementProp> = ({ closeWorkspace }) => const { user } = useSession(); const [patientUuid, setPatientUuid] = useState(''); const [patientSelected, setPatientSelected] = useState(false); + const { data } = useCaseManagers(); + const { data: relationshipTypes } = useMappedRelationshipTypes(); + + const caseManagerRelationshipTypeMapped = + relationshipTypes + .filter((relationshipType) => relationshipType.display === 'Case manager') + ?.map((relationship) => ({ + id: relationship.uuid, + text: relationship.display, + })) || []; - const { data, error } = useCaseManagers(); - const { data: relationshipTypesData } = useRelationshipType(); const caseManagerUuid = user?.person.uuid; const { mutate: fetchCases } = useActivecases(caseManagerUuid); @@ -41,29 +50,16 @@ const CaseManagementForm: React.FC<CaseManagementProp> = ({ closeWorkspace }) => text: manager.display, })) || []; - const caseManagerRlshipType = - relationshipTypesData?.data.results.map((relationship) => ({ - id: relationship.uuid, - text: relationship.display, - })) || []; - - const conceptReasons = useMemo(() => { - return Object.keys(caseManagementConceptMap.answers).map((key) => ({ - id: key, - text: caseManagementConceptMap.answers[key], - })); - }, []); - const { control, handleSubmit, formState: { isValid }, - } = useForm({ + } = useForm<FormData>({ mode: 'onChange', resolver: zodResolver(schema), }); - const onSubmit = async (data) => { + const onSubmit: SubmitHandler<FormData> = async (data) => { const payload = { personA: data.caseManager, relationshipType: data.relationship, @@ -71,6 +67,7 @@ const CaseManagementForm: React.FC<CaseManagementProp> = ({ closeWorkspace }) => startDate: data.startDate.toISOString(), endDate: data.endDate ? data.endDate.toISOString() : null, }; + try { await saveRelationship(payload); await fetchCases(); @@ -82,10 +79,8 @@ const CaseManagementForm: React.FC<CaseManagementProp> = ({ closeWorkspace }) => isLowContrast: true, }); - // Navigate or handle closing the form after submission closeWorkspace(); } catch (err) { - console.error(err); showSnackbar({ kind: 'error', title: t('RlshipError', 'Relationship Error'), @@ -157,7 +152,7 @@ const CaseManagementForm: React.FC<CaseManagementProp> = ({ closeWorkspace }) => id="relationship_name" titleText={t('relationship', 'Relationship')} placeholder="Select Relationship" - items={caseManagerRlshipType} + items={caseManagerRelationshipTypeMapped} itemToString={(item) => (item ? uppercaseText(item.text) : '')} onChange={(e) => field.onChange(e.selectedItem?.id)} invalid={!!fieldState.error} @@ -205,25 +200,6 @@ const CaseManagementForm: React.FC<CaseManagementProp> = ({ closeWorkspace }) => /> </Column> - {/* <Column> - <Controller - name="reasons" - control={control} - render={({ field, fieldState }) => ( - <ComboBox - id="reasons" - placeholder="Select Reason for Assignment" - titleText={t('reasons', 'Reason for Assignment')} - items={conceptReasons} - itemToString={(item) => (item ? uppercaseText(item.text) : '')} - onChange={(e) => field.onChange(e.selectedItem?.id)} - invalid={!!fieldState.error} - invalidText={fieldState.error?.message} - /> - )} - /> - </Column> */} - <Column className={styles.textbox}> <Controller name="notes" diff --git a/packages/esm-patient-clinical-view-app/src/config-schema.ts b/packages/esm-patient-clinical-view-app/src/config-schema.ts index 0f512ace4..98960c20e 100644 --- a/packages/esm-patient-clinical-view-app/src/config-schema.ts +++ b/packages/esm-patient-clinical-view-app/src/config-schema.ts @@ -1,4 +1,5 @@ import { Type } from '@openmrs/esm-framework'; +import _default from 'react-hook-form/dist/logic/appendErrors'; export const configSchema = { encounterTypes: { diff --git a/packages/esm-patient-clinical-view-app/src/family-partner-history/family-history.component.tsx b/packages/esm-patient-clinical-view-app/src/family-partner-history/family-history.component.tsx index 123810ab2..871ee30cc 100644 --- a/packages/esm-patient-clinical-view-app/src/family-partner-history/family-history.component.tsx +++ b/packages/esm-patient-clinical-view-app/src/family-partner-history/family-history.component.tsx @@ -25,7 +25,7 @@ import { useLayoutType, usePagination, } from '@openmrs/esm-framework'; -import { useRelationships } from './relationships.resource'; +import { usePatientRelationships } from './relationships.resource'; import ConceptObservations from './concept-obs.component'; import type { ConfigObject } from '../config-schema'; import styles from './family-history.scss'; @@ -40,7 +40,8 @@ const FamilyHistory: React.FC<FamilyHistoryProps> = ({ patientUuid }) => { const { concepts, familyRelationshipsTypeList } = config; const layout = useLayoutType(); const [pageSize, setPageSize] = useState(10); - const { relationships, error, isLoading, isValidating } = useRelationships(patientUuid); + const { relationships, error, isLoading, isValidating } = usePatientRelationships(patientUuid); + const familyRelationshipTypeUUIDs = new Set(familyRelationshipsTypeList.map((type) => type.uuid)); const familyRelationships = relationships.filter((r) => familyRelationshipTypeUUIDs.has(r.relationshipTypeUUID)); diff --git a/packages/esm-patient-clinical-view-app/src/family-partner-history/family-relationship.workspace.tsx b/packages/esm-patient-clinical-view-app/src/family-partner-history/family-relationship.workspace.tsx index 0477bda60..691c3bff8 100644 --- a/packages/esm-patient-clinical-view-app/src/family-partner-history/family-relationship.workspace.tsx +++ b/packages/esm-patient-clinical-view-app/src/family-partner-history/family-relationship.workspace.tsx @@ -10,7 +10,7 @@ import { uppercaseText } from '../utils/expression-helper'; import { saveRelationship } from '../case-management/workspace/case-management.resource'; import PatientInfo from '../case-management/workspace/patient-info.component'; import { mutate } from 'swr'; -import { useAllRelationshipTypes, useRelationships } from './relationships.resource'; +import { useMappedRelationshipTypes, usePatientRelationships } from './relationships.resource'; import { ConfigObject } from '../config-schema'; const schema = z.object({ @@ -29,10 +29,13 @@ type RelationshipFormProps = { const FamilyRelationshipForm: React.FC<RelationshipFormProps> = ({ closeWorkspace, rootPersonUuid }) => { const { t } = useTranslation(); const [relatedPersonUuid, setRelatedPersonUuid] = useState<string | undefined>(undefined); - const { relationshipsUrl } = useRelationships(rootPersonUuid); + const { relationshipsUrl } = usePatientRelationships(rootPersonUuid); + const { data: mappedRelationshipTypes } = useMappedRelationshipTypes(); const { familyRelationshipsTypeList } = useConfig<ConfigObject>(); + const familyRelationshipTypesUUIDs = new Set(familyRelationshipsTypeList.map((r) => r.uuid)); + const familyRelationshipTypes = mappedRelationshipTypes.filter((type) => familyRelationshipTypesUUIDs.has(type.uuid)); - const relationshipTypes = familyRelationshipsTypeList.map((relationship) => ({ + const relationshipTypes = familyRelationshipTypes.map((relationship) => ({ id: relationship.uuid, text: relationship.display, })); @@ -107,7 +110,7 @@ const FamilyRelationshipForm: React.FC<RelationshipFormProps> = ({ closeWorkspac <ComboBox id="relationship_name" titleText={t('relationship', 'Relationship')} - placeholder="Select Relationship" + placeholder="Relationship to patient" items={relationshipTypes} itemToString={(item) => (item ? uppercaseText(item.text) : '')} onChange={(e) => field.onChange(e.selectedItem?.id)} diff --git a/packages/esm-patient-clinical-view-app/src/family-partner-history/relationships.resource.tsx b/packages/esm-patient-clinical-view-app/src/family-partner-history/relationships.resource.tsx index 6794173da..2bb3c03d7 100644 --- a/packages/esm-patient-clinical-view-app/src/family-partner-history/relationships.resource.tsx +++ b/packages/esm-patient-clinical-view-app/src/family-partner-history/relationships.resource.tsx @@ -1,8 +1,9 @@ import { useMemo } from 'react'; import useSWR from 'swr'; -import { type FetchResponse, openmrsFetch, FHIRResource, restBaseUrl } from '@openmrs/esm-framework'; +import { type FetchResponse, openmrsFetch, FHIRResource, restBaseUrl, useConfig } from '@openmrs/esm-framework'; import useSWRImmutable from 'swr/immutable'; import { RelationshipTypeResponse } from '../case-management/workspace/case-management.resource'; +import { ConfigObject } from '../config-schema'; interface RelationshipsResponse { results: Array<Relationship>; @@ -48,6 +49,12 @@ type FHIRResourceResponse = { entry: Array<FHIRResource>; }; +interface RelationshipType { + uuid: string; + display: string; + direction: string; +} + export const useCodedConceptObservations = (patientUuid: string, conceptUuid: string) => { const url = `/ws/fhir2/R4/Observation?subject:Patient=${patientUuid}&code=${conceptUuid}&_summary=data&_sort=-date&_count=100`; @@ -79,14 +86,44 @@ function mapObservations(obsData) { } export const useAllRelationshipTypes = () => { - const customRepresentation = 'custom:(uuid,display)'; - const url = `${restBaseUrl}/relationshiptype?v=${customRepresentation}`; + const url = `${restBaseUrl}/relationshiptype?v=default`; const { data, error } = useSWRImmutable<{ data: RelationshipTypeResponse }>(url, openmrsFetch); return { data, error }; }; -export function useRelationships(patientUuid: string) { +export const useMappedRelationshipTypes = () => { + const url = `${restBaseUrl}/relationshiptype?v=default`; + const { data, error } = useSWRImmutable<{ data?: RelationshipTypeResponse }>(url, openmrsFetch); + + const relations: RelationshipType[] = []; + + data?.data.results.forEach((type) => { + const aIsToB = { + display: type.displayAIsToB ? type.displayAIsToB : type.displayBIsToA, + uuid: type.uuid, + direction: 'aIsToB', + }; + const bIsToA = { + display: type.displayBIsToA ? type.displayBIsToA : type.displayAIsToB, + uuid: type.uuid, + direction: 'bIsToA', + }; + aIsToB.display === bIsToA.display + ? relations.push(aIsToB) + : bIsToA.display === 'Patient' + ? relations.push(aIsToB, { + display: `Patient (${aIsToB.display})`, + uuid: type.uuid, + direction: 'bIsToA', + }) + : relations.push(aIsToB, bIsToA); + }); + + return { data: relations, error }; +}; + +export function usePatientRelationships(patientUuid: string) { const customRepresentation = 'custom:(display,uuid,personA:(uuid,age,display,dead,causeOfDeath),personB:(uuid,age,display,dead,causeOfDeath),relationshipType:(uuid,display,description,aIsToB,bIsToA))'; @@ -145,6 +182,8 @@ function extractRelationshipData( relativeUuid: r.personA.uuid, dead: r.personA.dead, relationshipType: r.relationshipType.aIsToB, + relationshipTypeDisplay: r.relationshipType.display, + relationshipTypeUUID: r.relationshipType.uuid, patientUuid: r.personA.uuid, }); } diff --git a/packages/esm-patient-clinical-view-app/src/other-relationships/other-relationships.component.tsx b/packages/esm-patient-clinical-view-app/src/other-relationships/other-relationships.component.tsx index b1a3fb026..1104fa9f6 100644 --- a/packages/esm-patient-clinical-view-app/src/other-relationships/other-relationships.component.tsx +++ b/packages/esm-patient-clinical-view-app/src/other-relationships/other-relationships.component.tsx @@ -27,7 +27,7 @@ import { } from '@openmrs/esm-framework'; import type { ConfigObject } from '../config-schema'; import styles from './other-relationships.scss'; -import { useRelationships } from '../family-partner-history/relationships.resource'; +import { usePatientRelationships } from '../family-partner-history/relationships.resource'; import ConceptObservations from '../family-partner-history/concept-obs.component'; interface OtherRelationshipsProps { @@ -41,7 +41,7 @@ export const OtherRelationships: React.FC<OtherRelationshipsProps> = ({ patientU const { concepts, familyRelationshipsTypeList } = config; const [pageSize, setPageSize] = useState(10); - const { relationships, error, isLoading, isValidating } = useRelationships(patientUuid); + const { relationships, error, isLoading, isValidating } = usePatientRelationships(patientUuid); const familyRelationshipTypeUUIDs = new Set(familyRelationshipsTypeList.map((type) => type.uuid)); const nonFamilyRelationships = relationships.filter((r) => !familyRelationshipTypeUUIDs.has(r.relationshipTypeUUID)); diff --git a/packages/esm-patient-clinical-view-app/src/other-relationships/other-relationships.workspace.tsx b/packages/esm-patient-clinical-view-app/src/other-relationships/other-relationships.workspace.tsx index 8dc403f08..feb982185 100644 --- a/packages/esm-patient-clinical-view-app/src/other-relationships/other-relationships.workspace.tsx +++ b/packages/esm-patient-clinical-view-app/src/other-relationships/other-relationships.workspace.tsx @@ -10,7 +10,7 @@ import { uppercaseText } from '../utils/expression-helper'; import { saveRelationship } from '../case-management/workspace/case-management.resource'; import PatientInfo from '../case-management/workspace/patient-info.component'; import { mutate } from 'swr'; -import { useAllRelationshipTypes, useRelationships } from '../family-partner-history/relationships.resource'; +import { useMappedRelationshipTypes, usePatientRelationships } from '../family-partner-history/relationships.resource'; import { ConfigObject } from '../config-schema'; const schema = z.object({ @@ -29,18 +29,16 @@ type OtherRelationshipsFormProps = { export const OtherRelationshipsForm: React.FC<OtherRelationshipsFormProps> = ({ closeWorkspace, rootPersonUuid }) => { const { t } = useTranslation(); const [relatedPersonUuid, setRelatedPersonUuid] = useState<string | undefined>(undefined); - const { relationshipsUrl } = useRelationships(rootPersonUuid); - const { data: relationshipTypesData } = useAllRelationshipTypes(); + const { relationshipsUrl } = usePatientRelationships(rootPersonUuid); + const { data: mappedRelationshipTypes } = useMappedRelationshipTypes(); const { familyRelationshipsTypeList } = useConfig<ConfigObject>(); - const familyRelationshipTypeUUIDs = new Set(familyRelationshipsTypeList.map((type) => type.uuid)); + const familyRelationshipTypesUUIDs = new Set(familyRelationshipsTypeList.map((r) => r.uuid)); + const otherRelationshipTypes = mappedRelationshipTypes.filter((type) => !familyRelationshipTypesUUIDs.has(type.uuid)); - const relationshipTypes = - relationshipTypesData?.data.results - .map((relationship) => ({ - id: relationship.uuid, - text: relationship.display, - })) - .filter((r) => !familyRelationshipTypeUUIDs.has(r.id)) || []; + const relationshipTypes = otherRelationshipTypes.map((relationship) => ({ + id: relationship.uuid, + text: relationship.display, + })); const { control,