"use client"; import { useState, useMemo, useEffect } from "react"; import { useSearchParams } from "next/navigation"; import { Sidebar } from "@/components/dashboard/sidebar"; import { useBankingData } from "@/lib/hooks"; import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Badge } from "@/components/ui/badge"; import { Checkbox } from "@/components/ui/checkbox"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, DropdownMenuSeparator, } from "@/components/ui/dropdown-menu"; import { OFXImportDialog } from "@/components/import/ofx-import-dialog"; import { CategoryIcon } from "@/components/ui/category-icon"; import { Search, CheckCircle2, Circle, MoreVertical, Tags, Upload, RefreshCw, ArrowUpDown, Check, } from "lucide-react"; import { cn } from "@/lib/utils"; type SortField = "date" | "amount" | "description"; type SortOrder = "asc" | "desc"; export default function TransactionsPage() { const searchParams = useSearchParams(); const { data, isLoading, refresh, update } = useBankingData(); const [searchQuery, setSearchQuery] = useState(""); const [selectedAccount, setSelectedAccount] = useState("all"); // Initialize account filter from URL params useEffect(() => { const accountId = searchParams.get("accountId"); if (accountId) { setSelectedAccount(accountId); } }, [searchParams]); const [selectedCategory, setSelectedCategory] = useState("all"); const [showReconciled, setShowReconciled] = useState("all"); const [sortField, setSortField] = useState("date"); const [sortOrder, setSortOrder] = useState("desc"); const [selectedTransactions, setSelectedTransactions] = useState>( new Set(), ); const filteredTransactions = useMemo(() => { if (!data) return []; let transactions = [...data.transactions]; // Filter by search if (searchQuery) { const query = searchQuery.toLowerCase(); transactions = transactions.filter( (t) => t.description.toLowerCase().includes(query) || t.memo?.toLowerCase().includes(query), ); } // Filter by account if (selectedAccount !== "all") { transactions = transactions.filter( (t) => t.accountId === selectedAccount, ); } // Filter by category if (selectedCategory !== "all") { if (selectedCategory === "uncategorized") { transactions = transactions.filter((t) => !t.categoryId); } else { transactions = transactions.filter( (t) => t.categoryId === selectedCategory, ); } } // Filter by reconciliation status if (showReconciled !== "all") { const isReconciled = showReconciled === "reconciled"; transactions = transactions.filter( (t) => t.isReconciled === isReconciled, ); } // Sort transactions.sort((a, b) => { let comparison = 0; switch (sortField) { case "date": comparison = new Date(a.date).getTime() - new Date(b.date).getTime(); break; case "amount": comparison = a.amount - b.amount; break; case "description": comparison = a.description.localeCompare(b.description); break; } return sortOrder === "asc" ? comparison : -comparison; }); return transactions; }, [ data, searchQuery, selectedAccount, selectedCategory, showReconciled, sortField, sortOrder, ]); if (isLoading || !data) { return (
); } const formatCurrency = (amount: number) => { return new Intl.NumberFormat("fr-FR", { style: "currency", currency: "EUR", }).format(amount); }; const formatDate = (dateStr: string) => { return new Date(dateStr).toLocaleDateString("fr-FR", { day: "2-digit", month: "short", year: "numeric", }); }; const toggleReconciled = (transactionId: string) => { const updatedTransactions = data.transactions.map((t) => t.id === transactionId ? { ...t, isReconciled: !t.isReconciled } : t, ); update({ ...data, transactions: updatedTransactions }); }; const setCategory = (transactionId: string, categoryId: string | null) => { const updatedTransactions = data.transactions.map((t) => t.id === transactionId ? { ...t, categoryId } : t, ); update({ ...data, transactions: updatedTransactions }); }; const bulkReconcile = (reconciled: boolean) => { const updatedTransactions = data.transactions.map((t) => selectedTransactions.has(t.id) ? { ...t, isReconciled: reconciled } : t, ); update({ ...data, transactions: updatedTransactions }); setSelectedTransactions(new Set()); }; const bulkSetCategory = (categoryId: string | null) => { const updatedTransactions = data.transactions.map((t) => selectedTransactions.has(t.id) ? { ...t, categoryId } : t, ); update({ ...data, transactions: updatedTransactions }); setSelectedTransactions(new Set()); }; const toggleSelectAll = () => { if (selectedTransactions.size === filteredTransactions.length) { setSelectedTransactions(new Set()); } else { setSelectedTransactions(new Set(filteredTransactions.map((t) => t.id))); } }; const toggleSelectTransaction = (id: string) => { const newSelected = new Set(selectedTransactions); if (newSelected.has(id)) { newSelected.delete(id); } else { newSelected.add(id); } setSelectedTransactions(newSelected); }; const getCategory = (categoryId: string | null) => { if (!categoryId) return null; return data.categories.find((c) => c.id === categoryId); }; const getAccount = (accountId: string) => { return data.accounts.find((a) => a.id === accountId); }; return (

Transactions

{filteredTransactions.length} transaction {filteredTransactions.length > 1 ? "s" : ""}

{/* Filters */}
setSearchQuery(e.target.value)} className="pl-9" />
{/* Bulk actions */} {selectedTransactions.size > 0 && (
{selectedTransactions.size} sélectionnée {selectedTransactions.size > 1 ? "s" : ""} bulkSetCategory(null)}> Aucune catégorie {data.categories.map((cat) => ( bulkSetCategory(cat.id)} > {cat.name} ))}
)} {/* Transactions list */} {filteredTransactions.length === 0 ? (

Aucune transaction trouvée

) : (
{filteredTransactions.map((transaction) => { const category = getCategory(transaction.categoryId); const account = getAccount(transaction.accountId); return ( ); })}
0 } onCheckedChange={toggleSelectAll} /> Compte Catégorie Pointé
toggleSelectTransaction(transaction.id) } /> {formatDate(transaction.date)}

{transaction.description}

{transaction.memo && (

{transaction.memo}

)}
{account?.name || "-"} setCategory(transaction.id, null) } > Aucune catégorie {data.categories.map((cat) => ( setCategory(transaction.id, cat.id) } > {cat.name} {transaction.categoryId === cat.id && ( )} ))} = 0 ? "text-emerald-600" : "text-red-600", )} > {transaction.amount >= 0 ? "+" : ""} {formatCurrency(transaction.amount)} toggleReconciled(transaction.id) } > {transaction.isReconciled ? "Dépointer" : "Pointer"}
)}
); }