Files
medreport_mrb2b/app/doctor/_components/analysis-doctor.tsx

162 lines
5.2 KiB
TypeScript

'use client';
import React, { ReactElement, ReactNode, useMemo, useState } from 'react';
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 { NestedAnalysisElement } from '@/packages/features/doctor/src/lib/server/schema/doctor-analysis-detail-view.schema';
import AnalysisLevelBar, {
AnalysisLevelBarSkeleton,
AnalysisResultLevel,
} from './analysis-level-bar';
export type AnalysisResultForDisplay = {
norm_status?: number | null;
response_value?: number | null;
unit?: string | null;
norm_lower_included?: boolean | null;
norm_upper_included?: boolean | null;
norm_lower?: number | null;
norm_upper?: number | null;
response_time?: string | null;
nestedElements?: NestedAnalysisElement[];
};
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;
const value = results?.response_value || 0;
const unit = results?.unit || '';
const normLower = results?.norm_lower ?? null;
const normUpper = results?.norm_upper ?? null;
const [showTooltip, setShowTooltip] = useState(false);
const analysisResultLevel = useMemo(() => {
if (!results || status === null || status === undefined) {
return null;
}
switch (status) {
case 1:
return AnalysisResultLevel.WARNING;
case 2:
return AnalysisResultLevel.CRITICAL;
case 0:
default:
return AnalysisResultLevel.NORMAL;
}
}, [results, status]);
const normRangeText = useMemo(() => {
if (normLower === null && normUpper === null) {
return null;
}
return `${normLower ?? '...'} - ${normUpper ?? '...'}`;
}, [normLower, normUpper]);
const nestedElements = results?.nestedElements ?? null;
const hasNestedElements =
Array.isArray(nestedElements) && nestedElements.length > 0;
const isAnalysisLevelBarHidden = isCancelled || !results || hasNestedElements;
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>
{isAnalysisLevelBarHidden ? null : (
<>
<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">
{normRangeText}
<div>
<Trans i18nKey="analysis-results:results.range.normal" />
</div>
</div>
<AnalysisLevelBar
results={results}
level={analysisResultLevel}
normRangeText={normRangeText}
/>
{endIcon || <div className="mx-2 w-4" />}
</>
)}
{(() => {
// If parent has nested elements, don't show anything
if (hasNestedElements) {
return null;
}
// If we're showing the level bar, don't show waiting
if (!isAnalysisLevelBarHidden) {
return null;
}
// If cancelled, don't show waiting
if (isCancelled) {
return null;
}
// Otherwise, show waiting for results
return (
<>
<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;