155 lines
5.0 KiB
TypeScript
155 lines
5.0 KiB
TypeScript
import AutoAwesomeIcon from "@mui/icons-material/AutoAwesome";
|
|
import RefreshIcon from "@mui/icons-material/Refresh";
|
|
import {
|
|
Alert,
|
|
Box,
|
|
Button,
|
|
Chip,
|
|
Paper,
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableHead,
|
|
TableRow,
|
|
Typography
|
|
} from "@mui/material";
|
|
import { useQuery } from "@tanstack/react-query";
|
|
import { useTranslation } from "react-i18next";
|
|
import { useNavigate } from "react-router-dom";
|
|
|
|
import { fetchAiReviews } from "../api/aiReviews";
|
|
import { fetchServerConfig } from "../api/config";
|
|
import PageHeader from "../components/PageHeader";
|
|
import { formatDate } from "../utils/date";
|
|
|
|
function statusColor(status: string): "default" | "primary" | "success" | "warning" | "error" {
|
|
if (status === "approved") return "success";
|
|
if (status === "needs_review") return "primary";
|
|
if (status === "failed") return "error";
|
|
if (status === "analyzing" || status === "pending") return "warning";
|
|
return "default";
|
|
}
|
|
|
|
export default function AiReviewList() {
|
|
const { t } = useTranslation();
|
|
const navigate = useNavigate();
|
|
const {
|
|
data: reviews,
|
|
isLoading,
|
|
isError,
|
|
refetch,
|
|
isFetching
|
|
} = useQuery({
|
|
queryKey: ["ai-reviews"],
|
|
queryFn: () => fetchAiReviews()
|
|
});
|
|
const { data: serverConfig } = useQuery({
|
|
queryKey: ["server-config"],
|
|
queryFn: fetchServerConfig
|
|
});
|
|
|
|
return (
|
|
<>
|
|
<PageHeader
|
|
title={t("aiReviews.title")}
|
|
subtitle={t("aiReviews.subtitle")}
|
|
action={
|
|
<Button
|
|
variant="outlined"
|
|
startIcon={<RefreshIcon />}
|
|
onClick={() => refetch()}
|
|
disabled={isFetching}
|
|
>
|
|
{t("aiReviews.refresh")}
|
|
</Button>
|
|
}
|
|
/>
|
|
|
|
{!serverConfig?.aiConfigured && (
|
|
<Alert severity="warning" sx={{ mb: 2 }}>
|
|
{t("aiReviews.aiNotConfigured")}
|
|
</Alert>
|
|
)}
|
|
{!serverConfig?.paperlessWebhookConfigured && (
|
|
<Alert severity="info" sx={{ mb: 2 }}>
|
|
{t("aiReviews.webhookNotConfigured")}
|
|
</Alert>
|
|
)}
|
|
|
|
<Paper variant="outlined" sx={{ borderRadius: 3, p: 2.5 }}>
|
|
<Table>
|
|
<TableHead>
|
|
<TableRow>
|
|
<TableCell>{t("aiReviews.columns.document")}</TableCell>
|
|
<TableCell>{t("aiReviews.columns.status")}</TableCell>
|
|
<TableCell>{t("aiReviews.columns.confidence")}</TableCell>
|
|
<TableCell>{t("aiReviews.columns.updated")}</TableCell>
|
|
<TableCell align="right">{t("aiReviews.columns.action")}</TableCell>
|
|
</TableRow>
|
|
</TableHead>
|
|
<TableBody>
|
|
{isLoading && (
|
|
<TableRow>
|
|
<TableCell colSpan={5}>{t("messages.loading")}</TableCell>
|
|
</TableRow>
|
|
)}
|
|
{isError && (
|
|
<TableRow>
|
|
<TableCell colSpan={5}>
|
|
<Typography color="error">{t("aiReviews.loadError")}</Typography>
|
|
</TableCell>
|
|
</TableRow>
|
|
)}
|
|
{!isLoading && !isError && (reviews ?? []).length === 0 && (
|
|
<TableRow>
|
|
<TableCell colSpan={5}>
|
|
<Box display="flex" alignItems="center" gap={1}>
|
|
<AutoAwesomeIcon color="disabled" />
|
|
<Typography color="text.secondary">{t("aiReviews.empty")}</Typography>
|
|
</Box>
|
|
</TableCell>
|
|
</TableRow>
|
|
)}
|
|
{(reviews ?? []).map((review) => (
|
|
<TableRow
|
|
hover
|
|
key={review.id}
|
|
sx={{ cursor: "pointer" }}
|
|
onClick={() => navigate(`/ai-reviews/${review.id}`)}
|
|
>
|
|
<TableCell>
|
|
<Typography fontWeight={600}>
|
|
{review.documentTitle ?? `${t("aiReviews.document")} #${review.paperlessDocumentId}`}
|
|
</Typography>
|
|
<Typography variant="caption" color="text.secondary">
|
|
Paperless #{review.paperlessDocumentId}
|
|
</Typography>
|
|
</TableCell>
|
|
<TableCell>
|
|
<Chip
|
|
size="small"
|
|
color={statusColor(review.status)}
|
|
label={t(`aiReviews.status.${review.status}`)}
|
|
/>
|
|
</TableCell>
|
|
<TableCell>
|
|
{review.confidence !== null ? `${Math.round(review.confidence * 100)}%` : "-"}
|
|
</TableCell>
|
|
<TableCell>{formatDate(review.updatedAt, "dd.MM.yyyy HH:mm")}</TableCell>
|
|
<TableCell align="right">
|
|
<Button size="small" onClick={(event) => {
|
|
event.stopPropagation();
|
|
navigate(`/ai-reviews/${review.id}`);
|
|
}}>
|
|
{t("aiReviews.review")}
|
|
</Button>
|
|
</TableCell>
|
|
</TableRow>
|
|
))}
|
|
</TableBody>
|
|
</Table>
|
|
</Paper>
|
|
</>
|
|
);
|
|
}
|