From 021c28eba5d490298b01da55333663a3dea80b4d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 18 Oct 2024 11:58:59 +0300 Subject: [PATCH] (refactor) O3-4059 Fix Inaccurate Test Result Time Stamps --- .../test-results/filter/filter-context.tsx | 47 +++++++++++++ .../src/test-results/filter/filter-types.ts | 1 + .../individual-results-table.component.tsx | 32 ++++----- .../individual-results-table.test.tsx | 67 +++++++++---------- .../panel-timeline-component.tsx | 1 + .../tree-view/tree-view.component.tsx | 64 ++++++++---------- packages/esm-patient-tests-app/src/types.ts | 46 +++++++++++++ 7 files changed, 167 insertions(+), 91 deletions(-) diff --git a/packages/esm-patient-tests-app/src/test-results/filter/filter-context.tsx b/packages/esm-patient-tests-app/src/test-results/filter/filter-context.tsx index 321d0ebdd..87e521785 100644 --- a/packages/esm-patient-tests-app/src/test-results/filter/filter-context.tsx +++ b/packages/esm-patient-tests-app/src/test-results/filter/filter-context.tsx @@ -9,6 +9,7 @@ import { type TimelineData, } from './filter-types'; import reducer from './filter-reducer'; +import { type MappedObservation, type TestResult, type GroupedObservation, type Observation } from '../../types'; const initialState: ReducerState = { checkboxes: {}, @@ -22,6 +23,7 @@ const initialContext = { state: initialState, ...initialState, timelineData: null, + tableData: null, trendlineData: null, activeTests: [], someChecked: false, @@ -93,6 +95,50 @@ const FilterProvider = ({ roots, children }: FilterProviderProps) => { }; }, [activeTests, state.tests]); + const tableData = useMemo(() => { + const flattenedObs: Observation[] = []; + + for (const key in state.tests) { + const test = state.tests[key] as TestResult; + if (test.obs && Array.isArray(test.obs)) { + test.obs.forEach((obs) => { + const flattenedEntry = { + ...obs, + key: key, + ...test, + }; + flattenedObs.push(flattenedEntry); + }); + } + } + + const groupedObs: Record = {}; + + flattenedObs.forEach((curr: MappedObservation) => { + const flatNameParts = curr.flatName.split('-'); + const groupKey = flatNameParts.length > 1 ? flatNameParts[1].trim() : flatNameParts[0].trim(); + const dateKey = new Date(curr.obsDatetime).toISOString().split('T')[0]; + + const compositeKey = `${groupKey}__${dateKey}`; + if (!groupedObs[compositeKey]) { + groupedObs[compositeKey] = { + key: groupKey, + date: dateKey, + flatName: curr.flatName, + entries: [], + }; + } + + groupedObs[compositeKey].entries.push(curr); + }); + + const resultArray = Object.values(groupedObs).sort( + (a, b) => new Date(b.date).getTime() - new Date(a.date).getTime(), + ); + + return resultArray; + }, [state.tests]); + useEffect(() => { if (roots?.length && !Object.keys(state?.parents).length) { actions.initialize(roots); @@ -113,6 +159,7 @@ const FilterProvider = ({ roots, children }: FilterProviderProps) => { value={{ ...state, timelineData, + tableData, activeTests, someChecked, totalResultsCount, diff --git a/packages/esm-patient-tests-app/src/test-results/filter/filter-types.ts b/packages/esm-patient-tests-app/src/test-results/filter/filter-types.ts index 10bf6d4eb..e72d8cadb 100644 --- a/packages/esm-patient-tests-app/src/test-results/filter/filter-types.ts +++ b/packages/esm-patient-tests-app/src/test-results/filter/filter-types.ts @@ -101,6 +101,7 @@ export interface TimelineData { export interface FilterContextProps extends ReducerState { timelineData: TimelineData; + tableData?: any; activeTests: string[]; someChecked: boolean; totalResultsCount: number; diff --git a/packages/esm-patient-tests-app/src/test-results/individual-results-table/individual-results-table.component.tsx b/packages/esm-patient-tests-app/src/test-results/individual-results-table/individual-results-table.component.tsx index 934c784ee..0977f87db 100644 --- a/packages/esm-patient-tests-app/src/test-results/individual-results-table/individual-results-table.component.tsx +++ b/packages/esm-patient-tests-app/src/test-results/individual-results-table/individual-results-table.component.tsx @@ -13,18 +13,16 @@ import { TableHeader, TableRow, } from '@carbon/react'; -import { ArrowRightIcon, showModal, useLayoutType, isDesktop, formatDate } from '@openmrs/esm-framework'; +import { ArrowRightIcon, showModal, useLayoutType, isDesktop, formatDatetime } from '@openmrs/esm-framework'; import { getPatientUuidFromUrl, type OBSERVATION_INTERPRETATION } from '@openmrs/esm-patient-common-lib'; -import { type RowData } from '../filter/filter-types'; import styles from './individual-results-table.scss'; +import { type GroupedObservation } from '../../types'; interface IndividualResultsTableProps { isLoading: boolean; - parent: { - display: string; - }; - subRows: Array; + subRows: GroupedObservation; index: number; + title: string; } const getClasses = (interpretation: OBSERVATION_INTERPRETATION) => { @@ -53,12 +51,12 @@ const getClasses = (interpretation: OBSERVATION_INTERPRETATION) => { } }; -const IndividualResultsTable: React.FC = ({ isLoading, parent, subRows, index }) => { +const IndividualResultsTable: React.FC = ({ isLoading, subRows, index, title }) => { const { t } = useTranslation(); const layout = useLayoutType(); const patientUuid = getPatientUuidFromUrl(); - const headerTitle = t(parent.display); + const headerTitle = t(title); const launchResultsDialog = useCallback( (title: string, testUuid: string) => { @@ -85,9 +83,9 @@ const IndividualResultsTable: React.FC = ({ isLoadi const tableRows = useMemo( () => - subRows.map((row, i) => { - const { units = '', range = '', obs: values } = row; - const isString = isNaN(parseFloat(values?.[0]?.value)); + subRows.entries.map((row, i) => { + const { units = '', range = '' } = row; + const isString = isNaN(parseFloat(row.value)); return { ...row, @@ -107,8 +105,8 @@ const IndividualResultsTable: React.FC = ({ isLoadi ), value: { - value: `${row.obs[0]?.value ?? ''} ${row.units ?? ''}`, - interpretation: row.obs[0]?.interpretation, + value: `${row.value} ${row.units ?? ''}`, + interpretation: row?.interpretation, }, referenceRange: `${range || '--'} ${units || '--'}`, }; @@ -118,7 +116,7 @@ const IndividualResultsTable: React.FC = ({ isLoadi if (isLoading) return ; - if (subRows?.length) { + if (subRows.entries?.length) { return ( {({ rows, headers, getHeaderProps, getTableProps }) => ( @@ -126,11 +124,7 @@ const IndividualResultsTable: React.FC = ({ isLoadi

{headerTitle}

- - {subRows[0]?.obs[0]?.obsDatetime - ? formatDate(new Date(subRows[0]?.obs[0]?.obsDatetime), { mode: 'standard' }) - : ''} - + {subRows.date ?? ''}
) : view === 'over-time' ? ( - panels.map((panel) => ( -
- -
- )) + ) : null}
diff --git a/packages/esm-patient-tests-app/src/types.ts b/packages/esm-patient-tests-app/src/types.ts index 4830297eb..60842080e 100644 --- a/packages/esm-patient-tests-app/src/types.ts +++ b/packages/esm-patient-tests-app/src/types.ts @@ -137,3 +137,49 @@ export interface ObservationSet { } export type viewOpts = 'individual-test' | 'over-time' | 'full'; + +export type Observation = { + obsDatetime: string; + value: string; + interpretation: OBSERVATION_INTERPRETATION; +}; + +export type TestResult = { + obs: Observation[]; + datatype: string; + lowAbsolute: number; + display: string; + conceptUuid: string; + lowNormal: number; + units: string; + lowCritical: number; + hiNormal: number; + flatName: string; + hasData: boolean; + range: string; +}; + +export type MappedObservation = { + datatype: string; + lowAbsolute: number; + display: string; + conceptUuid: string; + lowNormal: number; + units: string; + lowCritical: number; + hiNormal: number; + flatName: string; + hasData: boolean; + range: string; + obsDatetime: string; + value: string; + interpretation: OBSERVATION_INTERPRETATION; + key: string; +}; + +export interface GroupedObservation { + key: string; + date: string; + flatName: string; + entries: MappedObservation[]; +}