diff --git a/app/doctor/_components/analysis-doctor.tsx b/app/doctor/_components/analysis-doctor.tsx
index b618107..4b7e4ee 100644
--- a/app/doctor/_components/analysis-doctor.tsx
+++ b/app/doctor/_components/analysis-doctor.tsx
@@ -28,12 +28,6 @@ export type AnalysisResultForDisplay = Pick<
| 'response_time'
>;
-export enum AnalysisStatus {
- NORMAL = 0,
- MEDIUM = 1,
- HIGH = 2,
-}
-
const AnalysisDoctor = ({
analysisElement,
results,
@@ -48,38 +42,35 @@ const AnalysisDoctor = ({
endIcon?: ReactNode | null;
}) => {
const name = analysisElement.analysis_name_lab || '';
- const status = results?.norm_status || AnalysisStatus.NORMAL;
+ const status = results?.norm_status;
const value = results?.response_value || 0;
const unit = results?.unit || '';
- const normLowerIncluded = results?.norm_lower_included || false;
- const normUpperIncluded = results?.norm_upper_included || false;
- const normLower = results?.norm_lower || 0;
- const normUpper = results?.norm_upper || 0;
+ const normLower = results?.norm_lower ?? null;
+ const normUpper = results?.norm_upper ?? null;
const [showTooltip, setShowTooltip] = useState(false);
const analysisResultLevel = useMemo(() => {
- if (!results) {
+ if (!results || status === null || status === undefined) {
return null;
}
- const isUnderNorm = value < normLower;
- if (isUnderNorm) {
- switch (status) {
- case AnalysisStatus.MEDIUM:
- return AnalysisResultLevel.LOW;
- default:
- return AnalysisResultLevel.VERY_LOW;
- }
- }
switch (status) {
- case AnalysisStatus.MEDIUM:
- return AnalysisResultLevel.HIGH;
- case AnalysisStatus.HIGH:
- return AnalysisResultLevel.VERY_HIGH;
+ case 1:
+ return AnalysisResultLevel.WARNING;
+ case 2:
+ return AnalysisResultLevel.CRITICAL;
+ case 0:
default:
return AnalysisResultLevel.NORMAL;
}
- }, [results, value, normLower]);
+ }, [results, status]);
+
+ const normRangeText = useMemo(() => {
+ if (normLower === null && normUpper === null) {
+ return null;
+ }
+ return `${normLower ?? '...'} - ${normUpper ?? '...'}`;
+ }, [normLower, normUpper]);
return (
@@ -117,16 +108,15 @@ const AnalysisDoctor = ({
{unit}
- {normLower} - {normUpper}
+ {normRangeText}
{endIcon || }
>
diff --git a/app/doctor/_components/analysis-level-bar.tsx b/app/doctor/_components/analysis-level-bar.tsx
index ace79d1..c22e357 100644
--- a/app/doctor/_components/analysis-level-bar.tsx
+++ b/app/doctor/_components/analysis-level-bar.tsx
@@ -7,11 +7,9 @@ import { cn } from '@kit/ui/utils';
import { AnalysisResultForDisplay } from './analysis-doctor';
export enum AnalysisResultLevel {
- VERY_LOW = 0,
- LOW = 1,
- NORMAL = 2,
- HIGH = 3,
- VERY_HIGH = 4,
+ NORMAL = 'NORMAL',
+ WARNING = 'WARNING',
+ CRITICAL = 'CRITICAL',
}
const Level = ({
@@ -20,17 +18,19 @@ const Level = ({
isFirst = false,
isLast = false,
arrowLocation,
+ normRangeText,
}: {
isActive?: boolean;
color: 'destructive' | 'success' | 'warning' | 'gray-200';
isFirst?: boolean;
isLast?: boolean;
arrowLocation?: number;
+ normRangeText?: string | null;
}) => {
return (
92.5 && { left: '92.5%' }),
+ ...(arrowLocation < 7.5 && { left: '7.5%' }),
+ },
+ }
+ : {})}
>
)}
+
+ {color === 'success' && typeof normRangeText === 'string' && (
+
+ {normRangeText}
+
+ )}
);
};
@@ -50,81 +71,144 @@ const Level = ({
export const AnalysisLevelBarSkeleton = () => {
return (
-
+
);
};
const AnalysisLevelBar = ({
- normLowerIncluded = true,
- normUpperIncluded = true,
level,
results,
+ normRangeText,
}: {
- normLowerIncluded?: boolean;
- normUpperIncluded?: boolean;
- level: AnalysisResultLevel;
+ level: AnalysisResultLevel | null;
results: AnalysisResultForDisplay;
+ normRangeText: string | null;
}) => {
const {
norm_lower: lower,
norm_upper: upper,
response_value: value,
} = results;
- const arrowLocation = useMemo(() => {
- if (value < lower!) {
- return 0;
- }
- if (normLowerIncluded || normUpperIncluded) {
+ // 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;
}
- const calculated = ((value - lower!) / (upper! - lower!)) * 100;
-
- if (calculated > 100) {
- return 100;
+ // If no normal ranges defined, center the arrow
+ if (lower === null && upper === null) {
+ return 50;
}
- return calculated;
+ // 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]);
- const [isVeryLow, isLow, isHigh, isVeryHigh] = useMemo(
- () => [
- level === AnalysisResultLevel.VERY_LOW,
- level === AnalysisResultLevel.LOW,
- level === AnalysisResultLevel.HIGH,
- level === AnalysisResultLevel.VERY_HIGH,
- ],
- [level, 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;
- const hasAbnormalLevel = isVeryLow || isLow || isHigh || isVeryHigh;
+ // If pending results, show gray bar
+ if (isPending) {
+ return (
+
+
+
+ );
+ }
+
+ // Show appropriate levels based on available norm bounds
+ const hasLowerBound = lower !== null;
+
+ 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,
+ ]);
return (
-
- {normLowerIncluded && (
- <>
-
-
- >
- )}
-
-
-
- {normUpperIncluded && (
- <>
-
-
- >
+
+
+
+
);
};