From 63c2afea1f940ba9ba5960ad69755ea740fa64ec Mon Sep 17 00:00:00 2001 From: Manus Date: Thu, 8 Jan 2026 10:19:05 -0500 Subject: [PATCH] =?UTF-8?q?Checkpoint:=20Ajout=20des=20filtres=20par=20sta?= =?UTF-8?q?tut=20d'export,=20export=20Excel=20et=20page=20de=20d=C3=A9tail?= =?UTF-8?q?=20avec=20=C3=A9dition=20manuelle.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Nouvelles fonctionnalités : 1. Filtres par statut d'export : - Boutons de filtre rapide (Tous/Exportés/Non exportés/Erreurs) - Compteurs dynamiques pour chaque statut - Couleurs distinctives (vert/bleu/rouge) 2. Export Excel : - Nouveau bouton "Excel" dans la page Factures - Génération automatique de fichier .xlsx avec toutes les métadonnées - Colonnes : Fournisseur, N° Facture, Date, N° BL, N° Commande, Montant, Score, Statut, Dates 3. Page de détail de facture (/invoices/:id) : - Visualisation PDF intégrée dans iframe - Affichage des métadonnées extraites - Mode édition avec formulaire complet - Recalcul automatique du score de qualité après édition - Marquage "Modifié manuellement" - Badges de statut et qualité - Lien cliquable sur le nom du fournisseur dans la liste L'application offre maintenant une expérience complète de gestion des factures avec filtrage avancé, exports multiples et édition manuelle. --- client/src/App.tsx | 2 + client/src/pages/InvoiceDetail.tsx | 346 +++++++++++++++++++++++++++++ client/src/pages/Invoices.tsx | 125 ++++++++++- package.json | 1 + pnpm-lock.yaml | 72 ++++++ server/routers.ts | 30 +++ todo.md | 10 + 7 files changed, 582 insertions(+), 4 deletions(-) create mode 100644 client/src/pages/InvoiceDetail.tsx diff --git a/client/src/App.tsx b/client/src/App.tsx index 8fb5220..96a19d5 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -9,6 +9,7 @@ import Login from "./pages/Login"; import Dashboard from "./pages/Dashboard"; import Upload from "./pages/Upload"; import Invoices from "./pages/Invoices"; +import InvoiceDetail from "./pages/InvoiceDetail"; import Settings from "./pages/Settings"; import History from "./pages/History"; import Users from "./pages/Users"; @@ -21,6 +22,7 @@ function Router() { + diff --git a/client/src/pages/InvoiceDetail.tsx b/client/src/pages/InvoiceDetail.tsx new file mode 100644 index 0000000..462df76 --- /dev/null +++ b/client/src/pages/InvoiceDetail.tsx @@ -0,0 +1,346 @@ +import { useState } from "react"; +import { useParams, useLocation } from "wouter"; +import DashboardLayout from "@/components/DashboardLayout"; +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Badge } from "@/components/ui/badge"; +import { trpc } from "@/lib/trpc"; +import { ArrowLeft, Save, FileText } from "lucide-react"; +import { toast } from "sonner"; + +export default function InvoiceDetail() { + const { id } = useParams<{ id: string }>(); + const [, setLocation] = useLocation(); + const invoiceId = parseInt(id || "0"); + + const { data: invoice, isLoading } = trpc.invoices.getById.useQuery({ id: invoiceId }); + const utils = trpc.useUtils(); + + const [isEditing, setIsEditing] = useState(false); + const [formData, setFormData] = useState({ + supplierName: "", + invoiceNumber: "", + invoiceDate: "", + deliveryNoteNumber: "", + orderNumber: "", + totalAmount: "", + }); + + // Initialize form data when invoice loads + useState(() => { + if (invoice) { + setFormData({ + supplierName: invoice.supplierName || "", + invoiceNumber: invoice.invoiceNumber || "", + invoiceDate: invoice.invoiceDate + ? new Date(invoice.invoiceDate).toISOString().split('T')[0] + : "", + deliveryNoteNumber: invoice.deliveryNoteNumber || "", + orderNumber: invoice.orderNumber || "", + totalAmount: invoice.totalAmount ? invoice.totalAmount.toString() : "", + }); + } + }); + + const updateMutation = trpc.invoices.update.useMutation({ + onSuccess: () => { + toast.success("Facture mise à jour avec succès"); + setIsEditing(false); + utils.invoices.getById.invalidate({ id: invoiceId }); + utils.invoices.list.invalidate(); + }, + onError: (error) => { + toast.error(error.message || "Erreur lors de la mise à jour"); + }, + }); + + const handleSave = () => { + // Calculate new quality score based on filled fields + let filledFields = 0; + let totalFields = 6; + + if (formData.supplierName) filledFields++; + if (formData.invoiceNumber) filledFields++; + if (formData.invoiceDate) filledFields++; + if (formData.deliveryNoteNumber) filledFields++; + if (formData.orderNumber) filledFields++; + if (formData.totalAmount) filledFields++; + + const newQualityScore = Math.round((filledFields / totalFields) * 100); + + updateMutation.mutate({ + id: invoiceId, + data: { + supplierName: formData.supplierName || undefined, + invoiceNumber: formData.invoiceNumber || undefined, + invoiceDate: formData.invoiceDate ? new Date(formData.invoiceDate) : undefined, + deliveryNoteNumber: formData.deliveryNoteNumber || undefined, + orderNumber: formData.orderNumber || undefined, + totalAmount: formData.totalAmount ? formData.totalAmount : undefined, + }, + }); + }; + + const getStatusBadge = (status: string) => { + switch (status) { + case "exported": + return Exporté; + case "not_exported": + return Non exporté; + case "export_error": + return Erreur export; + default: + return {status}; + } + }; + + const getQualityBadge = (score: number | null) => { + if (score === null) return -; + if (score === 100) return {score}%; + if (score >= 80) return {score}%; + return {score}%; + }; + + if (isLoading) { + return ( + +
+
Chargement...
+
+
+ ); + } + + if (!invoice) { + return ( + +
+ +

Facture introuvable

+

Cette facture n'existe pas ou a été supprimée

+ +
+
+ ); + } + + return ( + +
+ {/* Header */} +
+
+ +
+

Détail de la facture

+

+ {invoice.supplierName || "Fournisseur inconnu"} • {invoice.invoiceNumber || "N° inconnu"} +

+
+
+
+ {isEditing ? ( + <> + + + + ) : ( + + )} +
+
+ +
+ {/* Left column - PDF Viewer */} +
+ + + Aperçu du document + Fichier PDF de la facture + + +
+