Skip to content

Commit

Permalink
Fix: Correct relationship display on workspace forms (#291)
Browse files Browse the repository at this point in the history
* fix: better display names for relationship types

* fix: correct relationship type for both bidirectional display types

* Apply suggestions from code review

Co-authored-by: Donald Kibet <[email protected]>

* refactor: avoid code duplication on family and other forms

* refactor: renamed hooks to be more explicit

---------

Co-authored-by: Donald Kibet <[email protected]>
  • Loading branch information
amosmachora and donaldkibet authored Aug 1, 2024
1 parent 778bdb8 commit 159019b
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ interface CaseManagerResponse {
results: CaseManager[];
}

interface RelationshipType {
export interface RelationshipType {
uuid: string;
display: string;
displayAIsToB: string;
displayBIsToA: string;
}

export interface RelationshipTypeResponse {
Expand Down Expand Up @@ -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, {
Expand Down
Original file line number Diff line number Diff line change
@@ -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;
};
Expand All @@ -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);

Expand All @@ -41,36 +50,24 @@ 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,
personB: patientUuid,
startDate: data.startDate.toISOString(),
endDate: data.endDate ? data.endDate.toISOString() : null,
};

try {
await saveRelationship(payload);
await fetchCases();
Expand All @@ -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'),
Expand Down Expand Up @@ -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}
Expand Down Expand Up @@ -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"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Type } from '@openmrs/esm-framework';
import _default from 'react-hook-form/dist/logic/appendErrors';

export const configSchema = {
encounterTypes: {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand All @@ -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,
}));
Expand Down Expand Up @@ -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)}
Expand Down
Original file line number Diff line number Diff line change
@@ -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>;
Expand Down Expand Up @@ -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`;

Expand Down Expand Up @@ -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))';

Expand Down Expand Up @@ -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,
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -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));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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({
Expand All @@ -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,
Expand Down

0 comments on commit 159019b

Please sign in to comment.