feat(MED-161): separate doctor analysis view
This commit is contained in:
149
app/doctor/_components/analysis-doctor.tsx
Normal file
149
app/doctor/_components/analysis-doctor.tsx
Normal file
@@ -0,0 +1,149 @@
|
||||
'use client';
|
||||
|
||||
import React, { ReactElement, ReactNode, useMemo, useState } from 'react';
|
||||
|
||||
import { UserAnalysisElement } from '@/packages/features/accounts/src/types/accounts';
|
||||
import { format } from 'date-fns';
|
||||
import { Info } from 'lucide-react';
|
||||
|
||||
import { Trans } from '@kit/ui/trans';
|
||||
import { cn } from '@kit/ui/utils';
|
||||
|
||||
import { AnalysisElement } from '~/lib/services/analysis-element.service';
|
||||
|
||||
import AnalysisLevelBar, {
|
||||
AnalysisLevelBarSkeleton,
|
||||
AnalysisResultLevel,
|
||||
} from './analysis-level-bar';
|
||||
|
||||
export type AnalysisResultForDisplay = Pick<
|
||||
UserAnalysisElement,
|
||||
| 'norm_status'
|
||||
| 'response_value'
|
||||
| 'unit'
|
||||
| 'norm_lower_included'
|
||||
| 'norm_upper_included'
|
||||
| 'norm_lower'
|
||||
| 'norm_upper'
|
||||
| 'response_time'
|
||||
>;
|
||||
|
||||
export enum AnalysisStatus {
|
||||
NORMAL = 0,
|
||||
MEDIUM = 1,
|
||||
HIGH = 2,
|
||||
}
|
||||
|
||||
const AnalysisDoctor = ({
|
||||
analysisElement,
|
||||
results,
|
||||
startIcon,
|
||||
endIcon,
|
||||
isCancelled,
|
||||
}: {
|
||||
analysisElement: Pick<AnalysisElement, 'analysis_name_lab'>;
|
||||
results?: AnalysisResultForDisplay;
|
||||
isCancelled?: boolean;
|
||||
startIcon?: ReactElement | null;
|
||||
endIcon?: ReactNode | null;
|
||||
}) => {
|
||||
const name = analysisElement.analysis_name_lab || '';
|
||||
const status = results?.norm_status || AnalysisStatus.NORMAL;
|
||||
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 [showTooltip, setShowTooltip] = useState(false);
|
||||
const analysisResultLevel = useMemo(() => {
|
||||
if (!results) {
|
||||
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;
|
||||
default:
|
||||
return AnalysisResultLevel.NORMAL;
|
||||
}
|
||||
}, [results, value, normLower]);
|
||||
|
||||
return (
|
||||
<div className="border-border rounded-lg border px-5">
|
||||
<div className="flex flex-col items-center justify-between gap-2 py-3 sm:h-[65px] sm:flex-row sm:gap-0">
|
||||
<div className="flex items-center gap-2 font-semibold">
|
||||
{startIcon || <div className="w-4" />}
|
||||
{name}
|
||||
{results?.response_time && (
|
||||
<div
|
||||
className="group/tooltip relative"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setShowTooltip(!showTooltip);
|
||||
}}
|
||||
onMouseLeave={() => setShowTooltip(false)}
|
||||
>
|
||||
<Info className="hover" />{' '}
|
||||
<div
|
||||
className={cn(
|
||||
'absolute bottom-full left-1/2 z-10 mb-2 hidden -translate-x-1/2 rounded border bg-white p-4 text-sm whitespace-nowrap group-hover/tooltip:block',
|
||||
{ block: showTooltip },
|
||||
)}
|
||||
>
|
||||
<Trans i18nKey="analysis-results:analysisDate" />
|
||||
{': '}
|
||||
{format(new Date(results.response_time), 'dd.MM.yyyy HH:mm')}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{results ? (
|
||||
<>
|
||||
<div className="flex items-center gap-3 sm:ml-auto">
|
||||
<div className="font-semibold">{value}</div>
|
||||
<div className="text-muted-foreground text-sm">{unit}</div>
|
||||
</div>
|
||||
<div className="text-muted-foreground mx-8 flex flex-col-reverse gap-2 text-center text-sm sm:block sm:gap-0">
|
||||
{normLower} - {normUpper}
|
||||
<div>
|
||||
<Trans i18nKey="analysis-results:results.range.normal" />
|
||||
</div>
|
||||
</div>
|
||||
<AnalysisLevelBar
|
||||
results={results}
|
||||
normLowerIncluded={normLowerIncluded}
|
||||
normUpperIncluded={normUpperIncluded}
|
||||
level={analysisResultLevel!}
|
||||
/>
|
||||
{endIcon || <div className="mx-2 w-4" />}
|
||||
</>
|
||||
) : (isCancelled ? null : (
|
||||
<>
|
||||
<div className="flex items-center gap-3 sm:ml-auto">
|
||||
<div className="font-semibold">
|
||||
<Trans i18nKey="analysis-results:waitingForResults" />
|
||||
</div>
|
||||
</div>
|
||||
<div className="mx-8 w-[60px]"></div>
|
||||
<AnalysisLevelBarSkeleton />
|
||||
</>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default AnalysisDoctor;
|
||||
Reference in New Issue
Block a user