Files
medreport_mrb2b/app/doctor/_components/analysis-view.tsx
Danel Kungla 0c2cfe6d18 prettier fix
2025-09-19 17:22:36 +03:00

288 lines
8.3 KiB
TypeScript

'use client';
import { useState } from 'react';
import { zodResolver } from '@hookform/resolvers/zod';
import { useQueryClient } from '@tanstack/react-query';
import { capitalize } from 'lodash';
import { useForm } from 'react-hook-form';
import { giveFeedbackAction } from '@kit/doctor/actions/doctor-server-actions';
import {
getDOBWithAgeStringFromPersonalCode,
getResultSetName,
} from '@kit/doctor/lib/helpers';
import {
AnalysisResponse,
DoctorFeedback,
Order,
Patient,
} from '@kit/doctor/schema/doctor-analysis-detail-view.schema';
import {
DoctorAnalysisFeedbackForm,
doctorAnalysisFeedbackFormSchema,
} from '@kit/doctor/schema/doctor-analysis.schema';
import ConfirmationModal from '@kit/shared/components/confirmation-modal';
import { useCurrentLocaleLanguageNames } from '@kit/shared/hooks';
import { getFullName } from '@kit/shared/utils';
import { useUser } from '@kit/supabase/hooks/use-user';
import { Button } from '@kit/ui/button';
import {
Form,
FormControl,
FormField,
FormItem,
FormMessage,
} from '@kit/ui/form';
import { toast } from '@kit/ui/sonner';
import { Textarea } from '@kit/ui/textarea';
import { Trans } from '@kit/ui/trans';
import { bmiFromMetric } from '~/lib/utils';
import DoctorAnalysisWrapper from './doctor-analysis-wrapper';
import DoctorJobSelect from './doctor-job-select';
export default function AnalysisView({
patient,
order,
analyses,
feedback,
}: {
patient: Patient;
order: Order;
analyses: AnalysisResponse[];
feedback?: DoctorFeedback;
}) {
const [isConfirmOpen, setIsConfirmOpen] = useState(false);
const [isDraftSubmitting, setIsDraftSubmitting] = useState(false);
const { data: user } = useUser();
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;
const isReadOnly =
!isInProgress ||
(!!feedback?.doctor_user_id && feedback?.doctor_user_id !== user?.id);
const form = useForm({
resolver: zodResolver(doctorAnalysisFeedbackFormSchema),
reValidateMode: 'onChange',
defaultValues: {
feedbackValue: feedback?.value ?? '',
userId: patient.userId,
},
});
const queryClient = useQueryClient();
if (!patient || !order || !analyses) {
return null;
}
const onSubmit = async (
data: DoctorAnalysisFeedbackForm,
status: 'DRAFT' | 'COMPLETED',
) => {
const result = await giveFeedbackAction({
...data,
analysisOrderId: order.analysisOrderId,
status,
});
if (!result.success) {
return toast.error(<Trans i18nKey="common:genericServerError" />);
}
queryClient.invalidateQueries({
predicate: (query) => query.queryKey.includes('doctor-jobs'),
});
toast.success(<Trans i18nKey={'doctor:updateFeedbackSuccess'} />);
return setIsConfirmOpen(false);
};
const handleDraftSubmit = async (e: React.FormEvent) => {
setIsDraftSubmitting(true);
e.preventDefault();
form.formState.errors.feedbackValue = undefined;
const formData = form.getValues();
await onSubmit(formData, 'DRAFT');
setIsDraftSubmitting(false);
};
const handleCompleteSubmit = form.handleSubmit(async () => {
setIsConfirmOpen(true);
});
const confirmComplete = form.handleSubmit(async (data) => {
await onSubmit(data, 'COMPLETED');
});
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>
<h3>
<Trans i18nKey="doctor:feedback" />
</h3>
<p>{feedback?.value ?? '-'}</p>
{!isReadOnly && (
<Form {...form}>
<form className="space-y-4 lg:w-1/2">
<FormField
control={form.control}
name="feedbackValue"
render={({ field }) => (
<FormItem>
<FormControl>
<Textarea {...field} disabled={isReadOnly} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className="xs:flex block justify-end gap-2 space-y-2">
<Button
type="button"
variant="outline"
onClick={handleDraftSubmit}
disabled={
isReadOnly || isDraftSubmitting || form.formState.isSubmitting
}
className="xs:w-1/4 w-full"
>
<Trans i18nKey="common:saveAsDraft" />
</Button>
<Button
type="button"
onClick={handleCompleteSubmit}
disabled={
isReadOnly || isDraftSubmitting || form.formState.isSubmitting
}
className="xs:w-1/4 w-full"
>
<Trans i18nKey="common:save" />
</Button>
</div>
</form>
</Form>
)}
<ConfirmationModal
isOpen={isConfirmOpen}
onClose={() => setIsConfirmOpen(false)}
onConfirm={confirmComplete}
titleKey="doctor:confirmFeedbackModal.title"
descriptionKey="doctor:confirmFeedbackModal.description"
/>
</>
);
}