205 lines
6.1 KiB
TypeScript
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>
|
|
</>
|
|
)}
|
|
</>
|
|
);
|
|
}
|