Files
medreport_mrb2b/app/doctor/_components/analysis-view.tsx
2025-10-28 16:09:06 +02:00

205 lines
6.1 KiB
TypeScript

'use client';
import { useMemo, useState } from 'react';
import { useQueryClient } from '@tanstack/react-query';
import { capitalize } from 'lodash';
import {
getDOBWithAgeStringFromPersonalCode,
getResultSetName,
} from '@kit/doctor/lib/helpers';
import {
AnalysisResponse,
DoctorFeedback,
Order,
Patient,
} from '@kit/doctor/schema/doctor-analysis-detail-view.schema';
import { useCurrentLocaleLanguageNames } from '@kit/shared/hooks';
import { getFullName } from '@kit/shared/utils';
import { useUser } from '@kit/supabase/hooks/use-user';
import { Trans } from '@kit/ui/trans';
import { bmiFromMetric } from '~/lib/utils';
import AnalysisFeedback from './analysis-feedback';
import DoctorAnalysisWrapper from './doctor-analysis-wrapper';
import DoctorJobSelect from './doctor-job-select';
import DoctorRecommendedAnalyses from './doctor-recommended-analyses';
export default function AnalysisView({
patient,
order,
analyses,
feedback,
aiDoctorFeedback,
recommendations,
availableAnalyses,
timestamp,
}: {
patient: Patient;
order: Order;
analyses: AnalysisResponse[];
feedback?: DoctorFeedback;
aiDoctorFeedback?: string;
recommendations?: string[];
availableAnalyses?: string[];
timestamp?: string;
}) {
const { data: user } = useUser();
const queryClient = useQueryClient();
const [recommendedAnalyses, setRecommendedAnalyses] = useState<string[]>(
recommendations ?? [],
);
const isRecommendationsEdited = useMemo(() => {
if (recommendedAnalyses.length !== recommendations?.length) return true;
const sa = new Set(recommendedAnalyses),
sb = new Set(recommendations);
if (sa.size !== sb.size) return true;
for (const v of sa) if (!sb.has(v)) return true;
return false;
}, [recommendations, recommendedAnalyses]);
const languageNames = useCurrentLocaleLanguageNames();
const isInProgress = !!(
!!feedback?.status &&
feedback?.doctor_user_id &&
feedback?.status !== 'COMPLETED'
);
const isCurrentDoctorJob =
!!feedback?.doctor_user_id && feedback?.doctor_user_id === user?.id;
if (!patient || !order || !analyses) {
return null;
}
return (
<>
<div className="xs:flex xs:justify-between">
<h3>
<Trans
i18nKey={getResultSetName(
order.title,
order.isPackage,
Object.keys(analyses)?.length,
)}
/>
</h3>
<div className="xs:flex hidden">
<DoctorJobSelect
analysisOrderId={order.analysisOrderId}
userId={patient.userId}
doctorUserId={feedback?.doctor_user_id}
isRemovable={isCurrentDoctorJob && isInProgress}
onJobUpdate={() =>
queryClient.invalidateQueries({
predicate: (query) => query.queryKey.includes('doctor-jobs'),
})
}
/>
</div>
</div>
<div className="xs:grid-cols-2 grid">
<div className="font-bold">
<Trans i18nKey="doctor:name" />
</div>
<div>{getFullName(patient.firstName, patient.lastName)}</div>
<div className="font-bold">
<Trans i18nKey="doctor:personalCode" />
</div>
<div>{patient.personalCode ?? ''}</div>
<div className="font-bold">
<Trans i18nKey="doctor:dobAndAge" />
</div>
<div>{getDOBWithAgeStringFromPersonalCode(patient.personalCode)}</div>
<div className="font-bold">
<Trans i18nKey="doctor:height" />
</div>
<div>{patient.height}</div>
<div className="font-bold">
<Trans i18nKey="doctor:weight" />
</div>
<div>{patient.weight}</div>
<div className="font-bold">
<Trans i18nKey="doctor:bmi" />
</div>
<div>
{patient?.weight && patient?.height
? bmiFromMetric(patient.weight, patient.height)
: '-'}
</div>
<div className="font-bold">
<Trans i18nKey="doctor:smoking" />
</div>
<div>-</div>
<div className="font-bold">
<Trans i18nKey="doctor:phone" />
</div>
<div>{patient.phone}</div>
<div className="font-bold">
<Trans i18nKey="doctor:email" />
</div>
<div>{patient.email}</div>
<div className="font-bold">
<Trans i18nKey="common:language" />
</div>
<div>
{capitalize(languageNames.of(patient.preferred_locale ?? 'et'))}
</div>
</div>
<div className="xs:hidden block">
<DoctorJobSelect
className="w-full"
analysisOrderId={order.analysisOrderId}
userId={patient.userId}
doctorUserId={feedback?.doctor_user_id}
isRemovable={isCurrentDoctorJob && isInProgress}
onJobUpdate={() =>
queryClient.invalidateQueries({
predicate: (query) => query.queryKey.includes('doctor-jobs'),
})
}
/>
</div>
<h3>
<Trans i18nKey="doctor:results" />
</h3>
<div className="flex flex-col gap-2">
{analyses.map((analysisData) => {
return (
<DoctorAnalysisWrapper
key={analysisData.id}
analysisData={analysisData}
/>
);
})}
</div>
{order.isPackage && (
<>
<h3>
<Trans i18nKey="doctor:feedback" />
</h3>
<p>{feedback?.value ?? '-'}</p>
<div className="flex flex-col gap-4 lg:flex-row">
<AnalysisFeedback
order={order}
patient={patient}
feedback={feedback}
aiDoctorFeedback={aiDoctorFeedback}
timestamp={timestamp}
recommendations={recommendedAnalyses}
isRecommendationsEdited={isRecommendationsEdited}
/>
<DoctorRecommendedAnalyses
recommendedAnalyses={recommendedAnalyses}
availableAnalyses={availableAnalyses}
setRecommendedAnalyses={setRecommendedAnalyses}
/>
</div>
</>
)}
</>
);
}