import React, { useCallback, useMemo } from 'react'; import { Accordion, AccordionItem, InlineNotification, NumberInput, Select, SelectItem, TextInput, } from '@carbon/react'; import { useTranslation } from 'react-i18next'; import { type Control, Controller } from 'react-hook-form'; import { useLayoutType } from '@openmrs/esm-framework'; import { isCoded, isNumeric, isPanel, isText, type LabOrderConcept } from './lab-results.resource'; import { type Observation } from '../types/encounter'; import styles from './lab-results-form.scss'; interface ResultFormFieldProps { concept: LabOrderConcept; control: Control>; defaultValue: Observation; } const ResultFormField: React.FC = ({ concept, control, defaultValue }) => { const { t } = useTranslation(); const isTablet = useLayoutType() === 'tablet'; const responsiveFieldSize = isTablet ? 'lg' : 'sm'; // TODO: Reference ranges should be dynamically adjusted based on patient demographics: // - Age-specific ranges (e.g., pediatric vs adult values) // - Gender-specific ranges where applicable const formatLabRange = (concept: LabOrderConcept) => { const hl7Abbreviation = concept?.datatype?.hl7Abbreviation; if (hl7Abbreviation !== 'NM') { return ''; } const { hiAbsolute, lowAbsolute, units } = concept; const displayUnit = units ? ` ${units}` : ''; const hasLowerLimit = lowAbsolute != null; const hasUpperLimit = hiAbsolute != null; if (hasLowerLimit && hasUpperLimit) { return ` (${lowAbsolute} - ${hiAbsolute} ${displayUnit})`; } else if (hasUpperLimit) { return ` (<= ${hiAbsolute} ${displayUnit})`; } else if (hasLowerLimit) { return ` (>= ${lowAbsolute} ${displayUnit})`; } return units ? ` (${displayUnit})` : ''; }; const getSavedMemberValue = useCallback( (conceptUuid: string, hl7Abbreviation: string) => { const value = defaultValue?.groupMembers?.find((member) => member.concept.uuid === conceptUuid)?.value; return hl7Abbreviation === 'CWE' && value && typeof value === 'object' && 'uuid' in value ? value.uuid : value; }, [defaultValue], ); const getSavedMemberDefaultObservation = useCallback( (conceptUuid: string) => defaultValue?.groupMembers?.find((member) => member.concept.uuid === conceptUuid), [defaultValue], ); const labelText = useMemo(() => { const displayText = concept.display?.trim() || concept.name?.name?.trim() || concept.name?.display?.trim() || '--'; return `${displayText} ${formatLabRange(concept)}`.trim(); }, [concept]); const { isTextField, isNumericField, isCodedField, isPanelField } = useMemo( () => ({ isTextField: isText(concept), isNumericField: isNumeric(concept), isCodedField: isCoded(concept), isPanelField: isPanel(concept), }), [concept], ); if (isTextField || isNumericField || isCodedField || isPanelField) { return (
isTextField ? ( ) : isNumericField ? ( field.onChange(value !== '' ? value : undefined)} step={concept.allowDecimal ? 0.01 : 1} value={typeof field.value === 'string' || typeof field.value === 'number' ? field.value : ''} invalidText={error?.message} invalid={Boolean(error?.message)} size={responsiveFieldSize} /> ) : isCodedField ? ( ) : null } /> {isPanelField ? ( {concept.setMembers.map((member) => ( ))} ) : null}
); } return (
{labelText}
); }; export default ResultFormField;