Files
medreport_mrb2b/app/doctor/_components/results-table.tsx
Helena 3498406a0c MED-88: add doctor email notifications (#65)
* MED-88: add doctor email notifications

* add logging, send open jobs notification on partial analysis response

* update permissions

* fix import, permissions

* casing, let email be null

* unused import
2025-09-02 12:14:01 +03:00

244 lines
7.4 KiB
TypeScript

'use client';
import { useTransition } from 'react';
import Link from 'next/link';
import { format } from 'date-fns';
import { Eye } from 'lucide-react';
import { getResultSetName } from '@kit/doctor/lib/helpers';
import { ResponseTable } from '@kit/doctor/schema/doctor-analysis.schema';
import { pathsConfig } from '@kit/shared/config';
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 { toast } from '@kit/ui/sonner';
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow,
} from '@kit/ui/table';
import { Trans } from '@kit/ui/trans';
import DoctorJobSelect from './doctor-job-select';
import { capitalize } from 'lodash';
export default function ResultsTable({
results = [],
pagination = {
currentPage: 1,
totalPages: 1,
totalCount: 0,
pageSize: 10,
},
fetchAction,
onJobUpdate,
}: {
results: ResponseTable[] | null;
pagination?: {
currentPage: number;
totalPages: number;
totalCount: number;
pageSize: number;
};
fetchAction: ({
page,
pageSize,
}: {
page: number;
pageSize: number;
}) => Promise<{
success: boolean;
data: null;
}>;
onJobUpdate: () => void;
}) {
const [isPending, startTransition] = useTransition();
const { data: currentUser } = useUser();
const languageNames = useCurrentLocaleLanguageNames();
const fetchPage = async (page: number) => {
startTransition(async () => {
const result = await fetchAction({
page,
pageSize: pagination.pageSize,
});
if (!result.success) {
toast.error('common.genericServerError');
}
});
};
const handleNextPage = () => {
if (pagination.currentPage < pagination.totalPages) {
fetchPage(pagination.currentPage + 1);
}
};
const handlePrevPage = () => {
if (pagination.currentPage > 1) {
fetchPage(pagination.currentPage - 1);
}
};
const handleJobUpdate = () => {
onJobUpdate();
fetchPage(pagination.currentPage);
};
if (!results?.length) {
return (
<p>
<Trans i18nKey="common:noData" />.
</p>
);
}
return (
<>
<Table className="border-separate rounded-lg border">
<TableHeader className="text-ui-fg-subtle txt-medium-plus">
<TableRow>
<TableHead className="w-6"></TableHead>
<TableHead className="w-20">
<Trans i18nKey="doctor:resultsTable.patientName" />
</TableHead>
<TableHead className="w-20">
<Trans i18nKey="doctor:resultsTable.serviceName" />
</TableHead>
<TableHead className="w-20">
<Trans i18nKey="doctor:resultsTable.orderNr" />
</TableHead>
<TableHead className="w-20">
<Trans i18nKey="doctor:resultsTable.time" />
</TableHead>
<TableHead className="w-20">
<Trans i18nKey="doctor:resultsTable.resultsStatus" />
</TableHead>
<TableHead className="w-20">
<Trans i18nKey="doctor:resultsTable.language" />
</TableHead>
<TableHead className="w-20">
<Trans i18nKey="doctor:resultsTable.assignedTo" />
</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{results
?.sort((a, b) =>
(a.created_at ?? '') > (b.created_at ?? '') ? -1 : 1,
)
.map((result) => {
const isCompleted = result.feedback?.status === 'COMPLETED';
const isCurrentDoctorJob =
!!result.doctor?.primary_owner_user_id &&
result.doctor?.primary_owner_user_id === currentUser?.id;
const resultsReceived = result.elements.length || 0;
const elementsInOrder = Array.from(
new Set(result.analysis_order_id.analysis_element_ids),
)?.length;
return (
<TableRow key={result.order_number}>
<TableCell className="text-center">
<Link
href={`/${pathsConfig.app.analysisDetails}/${result.id}`}
>
<Eye />
</Link>
</TableCell>
<TableCell>
{getFullName(
result.patient?.name,
result.patient?.last_name,
)}
</TableCell>
<TableCell>
<Trans
i18nKey={getResultSetName(
result.order?.title ?? '-',
result.order?.isPackage,
result.elements?.length || 0,
)}
/>
</TableCell>
<TableCell>{result.order_number}</TableCell>
<TableCell>
{result.firstSampleGivenAt
? format(result.firstSampleGivenAt, 'dd.MM.yyyy HH:mm')
: '-'}
</TableCell>
<TableCell>
<Trans
i18nKey={
resultsReceived === elementsInOrder
? 'doctor:resultsTable.responsesReceived'
: 'doctor:resultsTable.waitingForNr'
}
values={{
nr: elementsInOrder - resultsReceived,
}}
/>
</TableCell>
<TableCell>
{capitalize(
languageNames.of(result?.patient?.preferred_locale ?? 'et'),
)}
</TableCell>
<TableCell>
<DoctorJobSelect
doctorUserId={result.doctor?.primary_owner_user_id}
doctorName={getFullName(
result.doctor?.name,
result.doctor?.last_name,
)}
analysisOrderId={result.analysis_order_id?.id}
userId={result.patient?.id}
isRemovable={!isCompleted && isCurrentDoctorJob}
onJobUpdate={handleJobUpdate}
linkTo={`/${pathsConfig.app.analysisDetails}/${result.id}`}
/>
</TableCell>
</TableRow>
);
})}
</TableBody>
</Table>
<div className="mt-4 flex items-center justify-between">
<Button
onClick={handlePrevPage}
disabled={pagination.currentPage === 1 || isPending}
variant="outline"
>
<Trans i18nKey="common:previous" />
</Button>
<span className="text-sm text-gray-600">
<Trans
i18nKey={'common:pageOfPages'}
values={{
page: pagination.currentPage,
total: pagination.totalPages,
}}
/>
</span>
<Button
onClick={handleNextPage}
disabled={
pagination.currentPage === pagination.totalPages || isPending
}
variant="outline"
>
<Trans i18nKey="common:next" />
</Button>
</div>
</>
);
}