import { useMemo } from 'react'; import { ArrowDown } from 'lucide-react'; import { cn } from '@kit/ui/utils'; import { AnalysisResultForDisplay } from './analysis-doctor'; export enum AnalysisResultLevel { NORMAL = 'NORMAL', WARNING = 'WARNING', CRITICAL = 'CRITICAL', } const Level = ({ isActive = false, color, isFirst = false, isLast = false, arrowLocation, normRangeText, }: { isActive?: boolean; color: 'destructive' | 'success' | 'warning' | 'gray-200'; isFirst?: boolean; isLast?: boolean; arrowLocation?: number; normRangeText?: string | null; }) => { return (
{isActive && (
92.5 && { left: '92.5%' }), ...(arrowLocation < 7.5 && { left: '7.5%' }), }, } : {})} >
)} {color === 'success' && typeof normRangeText === 'string' && (

{normRangeText}

)}
); }; export const AnalysisLevelBarSkeleton = () => { return (
); }; const AnalysisLevelBar = ({ level, results, normRangeText, }: { level: AnalysisResultLevel | null; results: AnalysisResultForDisplay; normRangeText: string | null; }) => { const { norm_lower: lower, norm_upper: upper, response_value: value, } = results; // Calculate arrow position based on value within normal range const arrowLocation = useMemo(() => { // If no response value, center the arrow if (value === null || value === undefined) { return 50; } // If no normal ranges defined, center the arrow if (lower === null && upper === null) { return 50; } // If only upper bound exists if (lower === null && upper !== null) { if (value <= upper!) { return Math.min(75, (value / upper!) * 75); // Show in left 75% of normal range } return 100; // Beyond upper bound } // If only lower bound exists if (upper === null && lower !== null) { if (value >= lower!) { // Value is in normal range (above lower bound) // Position proportionally in the normal range section const normalizedPosition = Math.min( (value - lower!) / (lower! * 0.5), 1, ); // Use 50% of lower as scale return normalizedPosition * 100; } // Value is below lower bound - position in the "below normal" section const belowPosition = Math.max(0, Math.min(1, value / lower!)); return belowPosition * 100; } // Both bounds exist if (lower !== null && upper !== null) { if (value < lower!) { return 0; // Below normal range } if (value > upper!) { return 100; // Above normal range } // Within normal range return ((value - lower!) / (upper! - lower!)) * 100; } return 50; // Fallback }, [value, upper, lower]); // Determine level states based on normStatus const isNormal = level === AnalysisResultLevel.NORMAL; const isWarning = level === AnalysisResultLevel.WARNING; const isCritical = level === AnalysisResultLevel.CRITICAL; const isPending = level === null; // Show appropriate levels based on available norm bounds const hasLowerBound = lower !== null; // Calculate level configuration (must be called before any returns) const [first, second, third] = useMemo(() => { const [warning, normal, critical] = [ { isActive: isWarning, color: 'warning', ...(isWarning ? { arrowLocation } : {}), }, { isActive: isNormal, color: 'success', normRangeText, ...(isNormal ? { arrowLocation } : {}), }, { isActive: isCritical, color: 'destructive', isLast: true, ...(isCritical ? { arrowLocation } : {}), }, ] as const; if (!hasLowerBound) { return [{ ...normal, isFirst: true }, warning, critical] as const; } return [ { ...warning, isFirst: true }, normal, { ...critical, isLast: true }, ] as const; }, [ arrowLocation, normRangeText, isNormal, isWarning, isCritical, hasLowerBound, ]); // If pending results, show gray bar if (isPending) { return (
); } return (
); }; export default AnalysisLevelBar;