From ef348a2e677ee11ec330d7e3e30bb2070851158a Mon Sep 17 00:00:00 2001 From: lucille Date: Tue, 15 Oct 2024 18:31:15 +0300 Subject: [PATCH 1/3] Infant's name to display on the Mother banner --- .../mother-infant-tag.component.tsx | 53 ++++++++++++++++ .../patient-status-tag.component.tsx | 22 ++++++- .../banner-tags/usePatientFamilyNames.ts | 60 +++++++++++++++++++ 3 files changed, 134 insertions(+), 1 deletion(-) create mode 100644 packages/esm-commons-lib/src/components/banner-tags/mother-infant-tag.component.tsx create mode 100644 packages/esm-commons-lib/src/components/banner-tags/usePatientFamilyNames.ts diff --git a/packages/esm-commons-lib/src/components/banner-tags/mother-infant-tag.component.tsx b/packages/esm-commons-lib/src/components/banner-tags/mother-infant-tag.component.tsx new file mode 100644 index 000000000..8d313a8ca --- /dev/null +++ b/packages/esm-commons-lib/src/components/banner-tags/mother-infant-tag.component.tsx @@ -0,0 +1,53 @@ +import { useState, useEffect, useCallback } from 'react'; +import { fetchPatientRelationships } from '@ohri/openmrs-esm-ohri-commons-lib'; + +export const usePatientFamilyNames = (patientUuid: string) => { + const [childrenNames, setChildrenNames] = useState([]); + const [motherName, setMotherName] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const [isError, setIsError] = useState(false); + + const fetchFamilyData = useCallback(async () => { + try { + const relationships = await fetchPatientRelationships(patientUuid); + + if (!relationships || !Array.isArray(relationships)) { + console.warn('No valid relationships data:', relationships); + setChildrenNames([]); + setMotherName(null); + setIsLoading(false); + return; + } + + // Fetch child relationships + const childRelationships = relationships + .filter((relationship) => relationship.relationshipType?.displayBIsToA === 'Child') + .map((relationship) => relationship.personB?.display); + + setChildrenNames(childRelationships); + + // Fetch mother's relationship + const motherRelationship = relationships.find( + (relationship) => + relationship.relationshipType?.displayAIsToB === 'Mother' || + relationship.relationshipType?.displayBIsToA === 'Child', + ); + + setMotherName(motherRelationship?.personA?.display || 'Mother not found'); + + setIsLoading(false); + } catch (error) { + console.error('Error fetching family names:', error); + setIsError(true); + setIsLoading(false); + } + }, [patientUuid]); + + useEffect(() => { + if (patientUuid) { + fetchFamilyData(); + } + }, [fetchFamilyData, patientUuid]); + + return { childrenNames, motherName, isLoading, isError }; +}; diff --git a/packages/esm-commons-lib/src/components/banner-tags/patient-status-tag.component.tsx b/packages/esm-commons-lib/src/components/banner-tags/patient-status-tag.component.tsx index 1ef075df7..d65480733 100644 --- a/packages/esm-commons-lib/src/components/banner-tags/patient-status-tag.component.tsx +++ b/packages/esm-commons-lib/src/components/banner-tags/patient-status-tag.component.tsx @@ -1,16 +1,36 @@ -import React, { useEffect, useState } from 'react'; +import React from 'react'; import { Tag } from '@carbon/react'; import { useTranslation } from 'react-i18next'; import { usePatientHivStatus } from './patientHivStatus'; +import { usePatientFamilyNames } from './usePatientFamilyNames'; export function PatientStatusBannerTag({ patientUuid }) { const { t } = useTranslation(); const { hivStatus } = usePatientHivStatus(patientUuid); + const { childrenNames, motherName, patientAge, patientGender, isLoading, isError } = + usePatientFamilyNames(patientUuid); + + if (isLoading) { + return null; + } + + if (isError) { + return
Error fetching family information
; + } return ( <> + {/* HIV Status Display */} {hivStatus === 'positive' && {t('hivPositive', 'HIV Positive')}} {hivStatus === 'negative' && {t('hivNegative', 'HIV Negative')}} + + {/* Mother Name Display (if patient is under 10) */} + {patientAge !== null && patientAge <= 10 && motherName && Mother: {motherName}} + + {/* Children Names Display (if patient is female and over 10) */} + {patientAge !== null && patientAge > 10 && patientGender === 'F' && childrenNames.length > 0 && ( + Children: {childrenNames.join(', ')} + )} ); } diff --git a/packages/esm-commons-lib/src/components/banner-tags/usePatientFamilyNames.ts b/packages/esm-commons-lib/src/components/banner-tags/usePatientFamilyNames.ts new file mode 100644 index 000000000..dcd04e330 --- /dev/null +++ b/packages/esm-commons-lib/src/components/banner-tags/usePatientFamilyNames.ts @@ -0,0 +1,60 @@ +import { useState, useEffect, useCallback } from 'react'; +import { fetchPatientRelationships } from '@ohri/openmrs-esm-ohri-commons-lib'; + +export const usePatientFamilyNames = (patientUuid: string) => { + const [childrenNames, setChildrenNames] = useState([]); + const [motherName, setMotherName] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const [isError, setIsError] = useState(false); + const [patientAge, setPatientAge] = useState(null); + const [patientGender, setPatientGender] = useState(null); + + const fetchFamilyData = useCallback(async () => { + try { + // Fetch patient demographics (age and gender) + const response = await fetch(`/openmrs/ws/rest/v1/patient/${patientUuid}?v=full`); + const patient = await response.json(); + setPatientAge(patient.person.age); + setPatientGender(patient.person.gender); + + // Fetch relationships + const relationships = await fetchPatientRelationships(patientUuid); + + if (!relationships || !Array.isArray(relationships)) { + console.warn('No valid relationships data:', relationships); + setChildrenNames([]); + setMotherName(null); + setIsLoading(false); + return; + } + + const childRelationships = relationships + .filter((relationship) => relationship.relationshipType?.displayBIsToA === 'Child') + .map((relationship) => relationship.personB?.display); + + setChildrenNames(childRelationships); + + const motherRelationship = relationships.find( + (relationship) => + relationship.relationshipType?.displayAIsToB === 'Mother' || + relationship.relationshipType?.displayBIsToA === 'Child', + ); + + setMotherName(motherRelationship?.personA?.display || 'Mother not found'); + + setIsLoading(false); + } catch (error) { + console.error('Error fetching family names:', error); + setIsError(true); + setIsLoading(false); + } + }, [patientUuid]); + + useEffect(() => { + if (patientUuid) { + fetchFamilyData(); + } + }, [fetchFamilyData, patientUuid]); + + return { childrenNames, motherName, patientAge, patientGender, isLoading, isError }; +}; From 42763ba1bf0ab06f06ee04959bacd919783647d7 Mon Sep 17 00:00:00 2001 From: lucille Date: Wed, 16 Oct 2024 12:05:03 +0300 Subject: [PATCH 2/3] change display of childern names --- .../components/banner-tags/patient-status-tag.component.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/esm-commons-lib/src/components/banner-tags/patient-status-tag.component.tsx b/packages/esm-commons-lib/src/components/banner-tags/patient-status-tag.component.tsx index d65480733..049c04451 100644 --- a/packages/esm-commons-lib/src/components/banner-tags/patient-status-tag.component.tsx +++ b/packages/esm-commons-lib/src/components/banner-tags/patient-status-tag.component.tsx @@ -25,11 +25,11 @@ export function PatientStatusBannerTag({ patientUuid }) { {hivStatus === 'negative' && {t('hivNegative', 'HIV Negative')}} {/* Mother Name Display (if patient is under 10) */} - {patientAge !== null && patientAge <= 10 && motherName && Mother: {motherName}} + {patientAge !== null && patientAge <= 14 && motherName && Mother: {motherName}} {/* Children Names Display (if patient is female and over 10) */} - {patientAge !== null && patientAge > 10 && patientGender === 'F' && childrenNames.length > 0 && ( - Children: {childrenNames.join(', ')} + {patientAge !== null && patientAge > 14 && patientGender === 'F' && childrenNames.length > 0 && ( + Children: {childrenNames.join(' || ')} )} ); From 9eb09642be1cae8118a604746b446d27fcb34794 Mon Sep 17 00:00:00 2001 From: lucille Date: Wed, 16 Oct 2024 23:18:36 +0300 Subject: [PATCH 3/3] faillling test and remove redundant code --- .../banner-tags/.patient-status-tag.test.tsx | 114 +++++++++++------- .../mother-infant-tag.component.tsx | 53 -------- 2 files changed, 73 insertions(+), 94 deletions(-) delete mode 100644 packages/esm-commons-lib/src/components/banner-tags/mother-infant-tag.component.tsx diff --git a/packages/esm-commons-lib/src/components/banner-tags/.patient-status-tag.test.tsx b/packages/esm-commons-lib/src/components/banner-tags/.patient-status-tag.test.tsx index b371a66b2..af6c214ad 100644 --- a/packages/esm-commons-lib/src/components/banner-tags/.patient-status-tag.test.tsx +++ b/packages/esm-commons-lib/src/components/banner-tags/.patient-status-tag.test.tsx @@ -1,91 +1,123 @@ import React from 'react'; -import { render, act, screen } from '@testing-library/react'; +import { render, screen } from '@testing-library/react'; import '@testing-library/jest-dom'; import { PatientStatusBannerTag } from './patient-status-tag.component'; import { usePatientHivStatus } from './patientHivStatus'; +import { usePatientFamilyNames } from './usePatientFamilyNames'; -jest.mock('./patientHivStatus'); +jest.mock('./patientHivStatus', () => ({ + usePatientHivStatus: jest.fn(), +})); -const mockusePatientHivStatus = usePatientHivStatus as jest.Mock; +jest.mock('./usePatientFamilyNames', () => ({ + usePatientFamilyNames: jest.fn(), +})); describe('PatientStatusBannerTag', () => { + const hivPositiveSampleUuid = '138571AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; + beforeEach(() => { jest.clearAllMocks(); }); - const hivPositiveSampleUuid = '138571AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'; - - it('renders red tag when patient is HIV positive', async () => { - mockusePatientHivStatus.mockReturnValue({ - hivStatus: 'positive', - isLoading: false, + it('should not render anything while loading', () => { + (usePatientHivStatus as jest.Mock).mockReturnValue({ + hivStatus: null, + isLoading: true, isError: false, }); - await act(async () => { - render(); + (usePatientFamilyNames as jest.Mock).mockReturnValue({ + childrenNames: [], + motherName: null, + patientAge: null, + patientGender: null, + isLoading: true, + isError: false, }); - expect(screen.getByText(/HIV Positive/i)).toBeInTheDocument(); + const { container } = render(); + + expect(container.firstChild).toBeNull(); }); - it('renders green tag when patient is HIV negative', async () => { - mockusePatientHivStatus.mockReturnValue({ - hivStatus: 'negative', + it('should display the correct tag for HIV positive status', () => { + (usePatientHivStatus as jest.Mock).mockReturnValue({ + hivStatus: 'positive', isLoading: false, isError: false, }); - await act(async () => { - render(); + (usePatientFamilyNames as jest.Mock).mockReturnValue({ + childrenNames: [], + motherName: null, + patientAge: null, + patientGender: null, + isLoading: false, + isError: false, }); - expect(screen.getByText(/HIV Negative/i)).toBeInTheDocument(); + render(); + expect(screen.getByText('HIV Positive')).toBeInTheDocument(); }); - it('does not render any tag when patient HIV status is not positive or negative', async () => { - mockusePatientHivStatus.mockReturnValue({ - hivStatus: 'other', + it('should display the correct tag for HIV negative status', () => { + (usePatientHivStatus as jest.Mock).mockReturnValue({ + hivStatus: 'negative', isLoading: false, isError: false, }); - await act(async () => { - render(); + (usePatientFamilyNames as jest.Mock).mockReturnValue({ + childrenNames: [], + motherName: null, + patientAge: null, + patientGender: null, + isLoading: false, + isError: false, }); - expect(screen.queryByText(/HIV Positive/i)).not.toBeInTheDocument(); - expect(screen.queryByText(/HIV Negative/i)).not.toBeInTheDocument(); + render(); + expect(screen.getByText('HIV Negative')).toBeInTheDocument(); }); - it('shows loading state initially', async () => { - mockusePatientHivStatus.mockReturnValue({ - hivStatus: null, - isLoading: true, + it('should display mother’s name on the Infant banner', () => { + (usePatientHivStatus as jest.Mock).mockReturnValue({ + hivStatus: 'negative', + isLoading: false, isError: false, }); - await act(async () => { - render(); + (usePatientFamilyNames as jest.Mock).mockReturnValue({ + childrenNames: [], + motherName: 'Jane Doe', + patientAge: 10, + patientGender: 'M', + isLoading: false, + isError: false, }); - expect(screen.queryByText(/HIV Positive/i)).not.toBeInTheDocument(); - expect(screen.queryByText(/HIV Negative/i)).not.toBeInTheDocument(); + render(); + expect(screen.getByText('Mother: Jane Doe')).toBeInTheDocument(); }); - it('handles error state', async () => { - mockusePatientHivStatus.mockReturnValue({ + it('should show an error message when there is an error fetching data', () => { + (usePatientHivStatus as jest.Mock).mockReturnValue({ hivStatus: null, isLoading: false, - isError: true, + isError: false, }); - await act(async () => { - render(); + (usePatientFamilyNames as jest.Mock).mockReturnValue({ + childrenNames: [], + motherName: null, + patientAge: null, + patientGender: null, + isLoading: false, + isError: true, }); - expect(screen.queryByText(/HIV Positive/i)).not.toBeInTheDocument(); - expect(screen.queryByText(/HIV Negative/i)).not.toBeInTheDocument(); - // Optionally check for an error message if your component shows one + render(); + expect(screen.getByText('Error fetching family information')).toBeInTheDocument(); }); }); diff --git a/packages/esm-commons-lib/src/components/banner-tags/mother-infant-tag.component.tsx b/packages/esm-commons-lib/src/components/banner-tags/mother-infant-tag.component.tsx deleted file mode 100644 index 8d313a8ca..000000000 --- a/packages/esm-commons-lib/src/components/banner-tags/mother-infant-tag.component.tsx +++ /dev/null @@ -1,53 +0,0 @@ -import { useState, useEffect, useCallback } from 'react'; -import { fetchPatientRelationships } from '@ohri/openmrs-esm-ohri-commons-lib'; - -export const usePatientFamilyNames = (patientUuid: string) => { - const [childrenNames, setChildrenNames] = useState([]); - const [motherName, setMotherName] = useState(null); - const [isLoading, setIsLoading] = useState(true); - const [isError, setIsError] = useState(false); - - const fetchFamilyData = useCallback(async () => { - try { - const relationships = await fetchPatientRelationships(patientUuid); - - if (!relationships || !Array.isArray(relationships)) { - console.warn('No valid relationships data:', relationships); - setChildrenNames([]); - setMotherName(null); - setIsLoading(false); - return; - } - - // Fetch child relationships - const childRelationships = relationships - .filter((relationship) => relationship.relationshipType?.displayBIsToA === 'Child') - .map((relationship) => relationship.personB?.display); - - setChildrenNames(childRelationships); - - // Fetch mother's relationship - const motherRelationship = relationships.find( - (relationship) => - relationship.relationshipType?.displayAIsToB === 'Mother' || - relationship.relationshipType?.displayBIsToA === 'Child', - ); - - setMotherName(motherRelationship?.personA?.display || 'Mother not found'); - - setIsLoading(false); - } catch (error) { - console.error('Error fetching family names:', error); - setIsError(true); - setIsLoading(false); - } - }, [patientUuid]); - - useEffect(() => { - if (patientUuid) { - fetchFamilyData(); - } - }, [fetchFamilyData, patientUuid]); - - return { childrenNames, motherName, isLoading, isError }; -};