import { useState, useMemo } from "react"; import DashboardLayout from "@/components/DashboardLayout"; import { Card, CardContent } from "@/components/ui/card"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { Badge } from "@/components/ui/badge"; import { trpc } from "@/lib/trpc"; import { History as HistoryIcon, FileText, Trash2, Eye, CheckCircle2, XCircle, AlertTriangle, CalendarDays, RotateCcw, Package, X, } from "lucide-react"; import { Button } from "@/components/ui/button"; import { toast } from "sonner"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, AlertDialogTrigger, } from "@/components/ui/alert-dialog"; import { Dialog, DialogContent, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; type StatusFilter = "all" | "imported" | "not_imported" | "duplicates" | "errors"; const MONTHS = [ { value: "1", label: "Janvier" }, { value: "2", label: "Février" }, { value: "3", label: "Mars" }, { value: "4", label: "Avril" }, { value: "5", label: "Mai" }, { value: "6", label: "Juin" }, { value: "7", label: "Juillet" }, { value: "8", label: "Août" }, { value: "9", label: "Septembre" }, { value: "10", label: "Octobre" }, { value: "11", label: "Novembre" }, { value: "12", label: "Décembre" }, ]; const YEARS = Array.from({ length: 5 }, (_, i) => String(new Date().getFullYear() - i)); export default function History() { const { data: logs, isLoading } = trpc.importLogs.getByUser.useQuery(); const deleteAllMutation = trpc.importLogs.deleteAll.useMutation(); const utils = trpc.useUtils(); // Filtres const [statusFilter, setStatusFilter] = useState("all"); const [yearFilter, setYearFilter] = useState(String(new Date().getFullYear())); const [monthFilter, setMonthFilter] = useState("all"); // Dialog visualisation détails const [detailDialogOpen, setDetailDialogOpen] = useState(false); const [selectedLog, setSelectedLog] = useState<(typeof logs extends (infer T)[] | undefined ? T : never) | null>(null); const handleDeleteAll = async () => { try { await deleteAllMutation.mutateAsync(); await utils.importLogs.getByUser.invalidate(); toast.success("Logs supprimés", { description: "Tous les logs d'import ont été supprimés avec succès.", }); } catch { toast.error("Erreur", { description: "Impossible de supprimer les logs d'import.", }); } }; // Filtrage par période const periodFiltered = useMemo(() => { if (!logs) return []; return logs.filter((log) => { const d = new Date(log.importedAt); if (yearFilter !== "all" && d.getFullYear() !== Number(yearFilter)) return false; if (monthFilter !== "all" && (d.getMonth() + 1) !== Number(monthFilter)) return false; return true; }); }, [logs, yearFilter, monthFilter]); // Compteurs sur la période filtrée const counts = useMemo(() => { const totalImported = periodFiltered.reduce((s, l) => s + l.invoicesImported, 0); const totalDuplicates = periodFiltered.reduce((s, l) => s + l.duplicatesIgnored, 0); const totalErrors = periodFiltered.reduce((s, l) => s + l.errors, 0); const totalDetected = periodFiltered.reduce((s, l) => s + l.totalInvoicesDetected, 0); const totalNotImported = totalDuplicates + totalErrors; return { totalImported, totalDuplicates, totalErrors, totalDetected, totalNotImported }; }, [periodFiltered]); // Filtrage par statut const filteredLogs = useMemo(() => { return periodFiltered.filter((log) => { if (statusFilter === "all") return true; if (statusFilter === "imported") return log.invoicesImported > 0; if (statusFilter === "not_imported") return log.duplicatesIgnored > 0 || log.errors > 0; if (statusFilter === "duplicates") return log.duplicatesIgnored > 0; if (statusFilter === "errors") return log.errors > 0; return true; }); }, [periodFiltered, statusFilter]); const hasPeriodFilter = yearFilter !== "all" || monthFilter !== "all"; const filterButtons: { key: StatusFilter; label: string; count: number; color: string; activeColor: string }[] = [ { key: "all", label: "Tous", count: periodFiltered.length, color: "bg-gray-100 text-gray-700 hover:bg-gray-200", activeColor: "bg-gray-700 text-white" }, { key: "imported", label: "Importées", count: counts.totalImported, color: "bg-green-50 text-green-700 hover:bg-green-100 border border-green-200", activeColor: "bg-green-600 text-white" }, { key: "not_imported", label: "Non importées", count: counts.totalNotImported, color: "bg-orange-50 text-orange-700 hover:bg-orange-100 border border-orange-200", activeColor: "bg-orange-500 text-white" }, { key: "duplicates", label: "Doublons", count: counts.totalDuplicates, color: "bg-yellow-50 text-yellow-700 hover:bg-yellow-100 border border-yellow-200", activeColor: "bg-yellow-500 text-white" }, { key: "errors", label: "Erreurs", count: counts.totalErrors, color: "bg-red-50 text-red-700 hover:bg-red-100 border border-red-200", activeColor: "bg-red-600 text-white" }, ]; return (
{/* En-tête */}

Historique des imports

Consultez l'historique de tous vos imports de factures

{logs && logs.length > 0 && ( Êtes-vous sûr ? Cette action supprimera définitivement tous les logs d'import. Cette opération est irréversible. Annuler {deleteAllMutation.isPending ? "Suppression..." : "Supprimer"} )}
{/* Compteurs */}

{counts.totalImported}

Factures importées

{counts.totalNotImported}

Non importées

{counts.totalDuplicates}

Doublons ignorés

{counts.totalErrors}

Erreurs

{/* Filtres */}
{/* Filtres statut */}
{filterButtons.map((btn) => ( ))}
{/* Séparateur */}
{/* Filtre Période */}
{hasPeriodFilter && ( )}
{/* Résumé */} {filteredLogs.length} import{filteredLogs.length > 1 ? "s" : ""} — {counts.totalDetected} facture{counts.totalDetected > 1 ? "s" : ""} détectée{counts.totalDetected > 1 ? "s" : ""}
{/* Tableau */} {isLoading ? (

Chargement...

) : filteredLogs.length > 0 ? ( Fichier importé Date d'import Détectées Importées Doublons Erreurs Détails {filteredLogs.map((log) => (
{log.fileName}
{new Date(log.importedAt).toLocaleString("fr-FR")} {log.totalInvoicesDetected} {log.invoicesImported > 0 ? ( {log.invoicesImported} ) : ( 0 )} {log.duplicatesIgnored > 0 ? ( {log.duplicatesIgnored} ) : ( - )} {log.errors > 0 ? ( {log.errors} ) : ( - )}
))}
) : (

Aucun import trouvé

Modifiez les filtres pour afficher plus de résultats

)}
{/* Dialog visualisation détails */} Détails de l'import {selectedLog && (
{/* Infos générales */}

Fichier

{selectedLog.fileName}

Date d'import

{new Date(selectedLog.importedAt).toLocaleString("fr-FR")}

{/* Statistiques */}

{selectedLog.totalInvoicesDetected}

Détectées

{selectedLog.invoicesImported}

Importées

{selectedLog.duplicatesIgnored}

Doublons

{selectedLog.errors}

Erreurs

{/* Détails doublons */} {selectedLog.duplicateDetails && (() => { try { const details = JSON.parse(selectedLog.duplicateDetails); if (Array.isArray(details) && details.length > 0) { return (

Doublons ignorés ({details.length})

{details.map((d: string | { supplier?: string; invoiceNumber?: string; reason?: string }, i: number) => (
{typeof d === "string" ? d : `${d.supplier || ""} — ${d.invoiceNumber || ""} ${d.reason ? `(${d.reason})` : ""}`}
))}
); } } catch { /* JSON invalide */ } return null; })()} {/* Détails erreurs */} {selectedLog.errorDetails && (() => { try { const details = JSON.parse(selectedLog.errorDetails); if (Array.isArray(details) && details.length > 0) { return (

Erreurs ({details.length})

{details.map((d: string | { message?: string; file?: string }, i: number) => (
{typeof d === "string" ? d : `${d.file ? `[${d.file}] ` : ""}${d.message || ""}`}
))}
); } } catch { /* JSON invalide */ } return null; })()}
)}
); }