diff --git a/client/src/pages/Invoices.tsx b/client/src/pages/Invoices.tsx index 16c5285..60a7790 100644 --- a/client/src/pages/Invoices.tsx +++ b/client/src/pages/Invoices.tsx @@ -34,7 +34,7 @@ import { } from "@/components/ui/table"; import { trpc } from "@/lib/trpc"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; -import { Search, FileText, Download, FileSpreadsheet, Trash2, Edit, Trash, Filter } from "lucide-react"; +import { Search, FileText, Download, FileSpreadsheet, Trash2, Edit, Trash, Filter, LayoutList, AlignJustify, ArrowUpDown, ChevronUp, ChevronDown } from "lucide-react"; import * as XLSX from 'xlsx'; import { toast } from "sonner"; import { useLocation } from "wouter"; @@ -53,6 +53,9 @@ export default function Invoices() { const [pendingInvoiceId, setPendingInvoiceId] = useState(null); const [textDialogOpen, setTextDialogOpen] = useState(false); const [selectedInvoiceText, setSelectedInvoiceText] = useState(null); + const [viewMode, setViewMode] = useState<"compact" | "detail">("compact"); + const [sortField, setSortField] = useState<"createdAt" | "invoiceDate">("createdAt"); + const [sortDir, setSortDir] = useState<"asc" | "desc">("desc"); const { user } = useAuth(); const isAdmin = user?.role === 'admin'; const { data: invoices, isLoading } = trpc.invoices.list.useQuery(); @@ -218,7 +221,28 @@ export default function Invoices() { return true; }); - + + // Tri + const sortedInvoices = filteredInvoices ? [...filteredInvoices].sort((a, b) => { + const aVal = sortField === "createdAt" ? new Date(a.createdAt).getTime() : (a.invoiceDate ? new Date(a.invoiceDate).getTime() : 0); + const bVal = sortField === "createdAt" ? new Date(b.createdAt).getTime() : (b.invoiceDate ? new Date(b.invoiceDate).getTime() : 0); + return sortDir === "asc" ? aVal - bVal : bVal - aVal; + }) : []; + + const handleSort = (field: "createdAt" | "invoiceDate") => { + if (sortField === field) { + setSortDir(d => d === "asc" ? "desc" : "asc"); + } else { + setSortField(field); + setSortDir("desc"); + } + }; + + const SortIcon = ({ field }: { field: "createdAt" | "invoiceDate" }) => { + if (sortField !== field) return ; + return sortDir === "asc" ? : ; + }; + const statusCounts = { all: invoices?.length || 0, exported: invoices?.filter(inv => inv.exportStatus === "exported").length || 0, @@ -301,8 +325,8 @@ export default function Invoices() { return invoice && isEligibleForExport(invoice); }); - const allEligibleSelected = (filteredInvoices?.length || 0) > 0 && - (filteredInvoices || []).filter(inv => isEligibleForExport(inv)).every(inv => selectedIds.includes(inv.id)); + const allEligibleSelected = sortedInvoices.length > 0 && + sortedInvoices.filter(inv => isEligibleForExport(inv)).every(inv => selectedIds.includes(inv.id)); return ( @@ -389,6 +413,55 @@ export default function Invoices() { + {/* Barre de tri + mode d'affichage */} +
+
+ Trier par : + + +
+
+ + +
+
+ {/* Status Filters */}
- ) : filteredInvoices && filteredInvoices.length > 0 ? ( + ) : sortedInvoices && sortedInvoices.length > 0 ? (
@@ -438,21 +511,30 @@ export default function Invoices() { onCheckedChange={handleSelectAll} /> - {isAdmin && Utilisateur} + Fournisseur - Destinataire + {viewMode === "detail" && Destinataire} N° Facture - Date + + + + + + Montant - Service - Abonnement + {viewMode === "detail" && Service} + {viewMode === "detail" && Abonnement} Score Statut Actions - {filteredInvoices.map((invoice) => { + {sortedInvoices.map((invoice) => { return ( @@ -461,11 +543,7 @@ export default function Invoices() { onCheckedChange={(checked) => handleSelectOne(invoice.id, checked as boolean)} /> - {isAdmin && ( - - {allUsers?.find(u => u.id === invoice.userId)?.name || `#${invoice.userId}`} - - )} + - {(invoice as any).recipientName || "-"} + {viewMode === "detail" && {(invoice as any).recipientName || "-"}} {invoice.invoiceNumber || "-"} {invoice.invoiceDate ? new Date(invoice.invoiceDate).toLocaleDateString("fr-FR") : "-"} + + {invoice.createdAt + ? new Date(invoice.createdAt).toLocaleDateString("fr-FR") + : "-"} + {invoice.totalAmount ? `${parseFloat(invoice.totalAmount as string).toFixed(2)} €` : "-"} - - { + if (e.target.value === "__ADD_NEW__") { + setPendingInvoiceId(invoice.id); + setAddDialogType("service"); + setAddDialogOpen(true); + e.target.value = invoice.serviceConcerne || ""; + } else { + updateFieldMutation.mutate({ + id: invoice.id, + data: { serviceConcerne: e.target.value || undefined }, + }); + } + }} + className="w-full px-2 py-1 border rounded text-sm focus:outline-none focus:ring-2 focus:ring-blue-500" + > + + {departments?.map((dept) => ( + + ))} + + + + )} + {viewMode === "detail" && ( + + - - - - + }} + className="w-full px-2 py-1 border rounded text-sm focus:outline-none focus:ring-2 focus:ring-blue-500" + > + + + + + )} {getQualityBadge(invoice.qualityScore)} {getExportStatusBadge(invoice.exportStatus || "not_exported")}