133 lines
3.0 KiB
TypeScript
133 lines
3.0 KiB
TypeScript
import { useMemo } from 'react';
|
|
|
|
import { ArrowDown } from 'lucide-react';
|
|
|
|
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,
|
|
}
|
|
|
|
const Level = ({
|
|
isActive = false,
|
|
color,
|
|
isFirst = false,
|
|
isLast = false,
|
|
arrowLocation,
|
|
}: {
|
|
isActive?: boolean;
|
|
color: 'destructive' | 'success' | 'warning' | 'gray-200';
|
|
isFirst?: boolean;
|
|
isLast?: boolean;
|
|
arrowLocation?: number;
|
|
}) => {
|
|
return (
|
|
<div
|
|
className={cn(`bg-${color} relative h-3 flex-1`, {
|
|
'opacity-20': !isActive,
|
|
'rounded-l-lg': isFirst,
|
|
'rounded-r-lg': isLast,
|
|
})}
|
|
>
|
|
{isActive && (
|
|
<div
|
|
className="absolute top-[-14px] left-1/2 -translate-x-1/2 rounded-[10px] bg-white p-[2px]"
|
|
style={{ left: `${arrowLocation}%` }}
|
|
>
|
|
<ArrowDown strokeWidth={2} />
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export const AnalysisLevelBarSkeleton = () => {
|
|
return (
|
|
<div className="mt-4 flex h-3 w-[60%] max-w-[360px] gap-1 sm:mt-0 sm:w-[35%]">
|
|
<Level color="gray-200" />
|
|
</div>
|
|
);
|
|
};
|
|
|
|
const AnalysisLevelBar = ({
|
|
normLowerIncluded = true,
|
|
normUpperIncluded = true,
|
|
level,
|
|
results,
|
|
}: {
|
|
normLowerIncluded?: boolean;
|
|
normUpperIncluded?: boolean;
|
|
level: AnalysisResultLevel;
|
|
results: AnalysisResultForDisplay;
|
|
}) => {
|
|
const {
|
|
norm_lower: lower,
|
|
norm_upper: upper,
|
|
response_value: value,
|
|
} = results;
|
|
const arrowLocation = useMemo(() => {
|
|
if (value < lower!) {
|
|
return 0;
|
|
}
|
|
|
|
if (normLowerIncluded || normUpperIncluded) {
|
|
return 50;
|
|
}
|
|
|
|
const calculated = ((value - lower!) / (upper! - lower!)) * 100;
|
|
|
|
if (calculated > 100) {
|
|
return 100;
|
|
}
|
|
|
|
return calculated;
|
|
}, [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],
|
|
);
|
|
|
|
const hasAbnormalLevel = isVeryLow || isLow || isHigh || isVeryHigh;
|
|
|
|
return (
|
|
<div className="mt-4 flex h-3 w-[60%] max-w-[360px] gap-1 sm:mt-0 sm:w-[35%]">
|
|
{normLowerIncluded && (
|
|
<>
|
|
<Level isActive={isVeryLow} color="destructive" isFirst />
|
|
<Level isActive={isLow} color="warning" />
|
|
</>
|
|
)}
|
|
|
|
<Level
|
|
isFirst={!normLowerIncluded}
|
|
isLast={!normUpperIncluded}
|
|
{...(hasAbnormalLevel
|
|
? { color: 'warning', isActive: false }
|
|
: { color: 'success', isActive: true })}
|
|
arrowLocation={arrowLocation}
|
|
/>
|
|
|
|
{normUpperIncluded && (
|
|
<>
|
|
<Level isActive={isHigh} color="warning" />
|
|
<Level isActive={isVeryHigh} color="destructive" isLast />
|
|
</>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default AnalysisLevelBar;
|