import React, { useCallback, useMemo } from 'react'; import classNames from 'classnames'; import { useTranslation } from 'react-i18next'; import { formatRangeWithUnits } from '../grouped-timeline/reference-range-helpers'; import { DataTable, DataTableSkeleton, Table, TableBody, TableCell, TableContainer, TableHead, TableHeader, TableRow, } from '@carbon/react'; import { showModal, useLayoutType, formatDate, parseDate } from '@openmrs/esm-framework'; import { type OBSERVATION_INTERPRETATION } from '@openmrs/esm-patient-common-lib'; import { type GroupedObservation } from '../../types'; import styles from './individual-results-table.scss'; import { formatResultDate } from '../helpers'; interface IndividualResultsTableProps { patientUuid: string; isLoading: boolean; subRows: GroupedObservation; index: number; title: string; } const getClasses = (interpretation: OBSERVATION_INTERPRETATION) => { switch (interpretation) { case 'OFF_SCALE_HIGH': return styles['off-scale-high']; case 'CRITICALLY_HIGH': return styles['critically-high']; case 'HIGH': return styles['high']; case 'OFF_SCALE_LOW': return styles['off-scale-low']; case 'CRITICALLY_LOW': return styles['critically-low']; case 'LOW': return styles['low']; case 'NORMAL': default: return ''; } }; const IndividualResultsTable: React.FC = ({ patientUuid, isLoading, subRows, index, title, }) => { const { t } = useTranslation(); const layout = useLayoutType(); const isDesktop = layout === 'small-desktop' || layout === 'large-desktop'; const getColumnClass = (columnKey: string) => styles[`col-${columnKey}`]; const headerTitle = t(title); const launchResultsDialog = useCallback( (title: string, testUuid: string) => { const dispose = showModal('timeline-results-modal', { closeDeleteModal: () => dispose(), patientUuid, testUuid, title, }); }, [patientUuid], ); const tableHeaders = useMemo(() => { return [ { key: 'testName', header: t('testName', 'Test name') }, { key: 'resultDate', header: t('resultDate', 'Result date') }, { key: 'value', header: t('value', 'Value'), }, { key: 'referenceRange', header: t('referenceRange', 'Reference range') }, ]; }, [t]); const tableRows = useMemo( () => subRows?.entries?.map((row, i) => { // Use observation-level range/units if available, otherwise fallback to node-level // MappedObservation has range and units fields, but they may come from node-level const displayRange = row.range ?? ''; const displayUnits = row.units ?? ''; const isString = isNaN(parseFloat(row.value)); const referenceRangeDisplay = formatRangeWithUnits(displayRange, displayUnits); return { ...row, id: `${i}-${index}`, testName: ( {!isString ? ( launchResultsDialog(row.display, row.conceptUuid)} > {row.display} ) : ( {row.display} )} ), resultDate: formatResultDate(row.obsDatetime), value: { value: `${row.value} ${displayUnits}`, interpretation: row?.interpretation, }, referenceRange: referenceRangeDisplay, }; }) ?? [], [index, subRows, launchResultsDialog], ); if (isLoading) { return ; } if (subRows.entries?.length) { return ( {({ rows, headers, getHeaderProps, getTableProps }) => (

{headerTitle}

{formatDate(parseDate(subRows.date), { mode: 'standard', time: false })}
{headers.map((header) => { const headerProps = getHeaderProps({ header }); return ( {header.header} ); })} {rows.map((row) => ( {row.cells.map((cell) => cell?.value?.interpretation ? (

{cell?.value?.value ?? cell?.value}

) : (

{cell?.value}

), )}
))}
)}
); } }; export default IndividualResultsTable;