"use client"; import { useCallback, useMemo } from "react"; import { PageLayout, PageHeader } from "@/components/layout"; import { RefreshCw, Receipt, Euro, ChevronDown, ChevronUp, CheckCircle2, Tag, } from "lucide-react"; import { TransactionFilters, TransactionBulkActions, TransactionTable, TransactionPagination, formatCurrency, formatDate, } from "@/components/transactions"; import { RuleCreateDialog } from "@/components/rules"; import { OFXImportDialog } from "@/components/import/ofx-import-dialog"; import { MonthlyChart } from "@/components/statistics"; import { useQueryClient } from "@tanstack/react-query"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Collapsible, CollapsibleContent, CollapsibleTrigger, } from "@/components/ui/collapsible"; import { Upload } from "lucide-react"; import { useTransactionsPage } from "@/hooks/use-transactions-page"; import { useTransactionMutations } from "@/hooks/use-transaction-mutations"; import { useTransactionRules } from "@/hooks/use-transaction-rules"; import { useTransactionsChartData } from "@/hooks/use-transactions-chart-data"; import { useTransactionsForAccountFilter } from "@/hooks/use-transactions-for-account-filter"; import { useTransactionsForCategoryFilter } from "@/hooks/use-transactions-for-category-filter"; import { useLocalStorage } from "@/hooks/use-local-storage"; export default function TransactionsPage() { const queryClient = useQueryClient(); // Main page state and logic const { metadata, isLoadingMetadata, searchQuery, setSearchQuery, selectedAccounts, onAccountsChange, selectedCategories, onCategoriesChange, showReconciled, onReconciledChange, period, onPeriodChange, customStartDate, customEndDate, onCustomStartDateChange, onCustomEndDateChange, isCustomDatePickerOpen, onCustomDatePickerOpenChange, showDuplicates, onShowDuplicatesChange, page, pageSize, onPageChange, sortField, sortOrder, onSortChange, selectedTransactions, onToggleSelectAll, onToggleSelectTransaction, clearSelection, transactionsData, isLoadingTransactions, invalidateTransactions, duplicateIds, transactionParams, } = useTransactionsPage(); // Transaction mutations const { toggleReconciled, markReconciled, setCategory, deleteTransaction, bulkReconcile: handleBulkReconcile, bulkSetCategory: handleBulkSetCategory, updatingTransactionIds, } = useTransactionMutations({ transactionParams, transactionsData, }); // Transaction rules const { ruleDialogOpen, setRuleDialogOpen, ruleGroup, handleCreateRule, handleSaveRule, } = useTransactionRules({ transactionsData, metadata, }); // Chart data const { monthlyData, isLoading: isLoadingChart, totalAmount: chartTotalAmount, totalCount: chartTotalCount, transactions: chartTransactions, } = useTransactionsChartData({ selectedAccounts, selectedCategories, period, customStartDate, customEndDate, showReconciled, searchQuery, }); // Transactions for account filter (filtered by categories, period, search, reconciled - NOT by accounts) const { transactions: transactionsForAccountFilter } = useTransactionsForAccountFilter({ selectedCategories, period, customStartDate, customEndDate, showReconciled, searchQuery, }); // Transactions for category filter (filtered by accounts, period, search, reconciled - NOT by categories) const { transactions: transactionsForCategoryFilter } = useTransactionsForCategoryFilter({ selectedAccounts, period, customStartDate, customEndDate, showReconciled, searchQuery, }); const invalidateAll = useCallback(() => { invalidateTransactions(); queryClient.invalidateQueries({ queryKey: ["banking-metadata"] }); }, [invalidateTransactions, queryClient]); const handleBulkReconcileWithClear = useCallback( (reconciled: boolean) => { handleBulkReconcile(reconciled, selectedTransactions); clearSelection(); }, [handleBulkReconcile, selectedTransactions, clearSelection] ); const handleBulkSetCategoryWithClear = useCallback( (categoryId: string | null) => { handleBulkSetCategory(categoryId, selectedTransactions); clearSelection(); }, [handleBulkSetCategory, selectedTransactions, clearSelection] ); // Stabilize transactions reference to prevent unnecessary re-renders const filteredTransactions = useMemo( () => transactionsData?.transactions || [], [transactionsData?.transactions] ); const totalTransactions = transactionsData?.total || 0; const hasMore = transactionsData?.hasMore || false; const uncategorizedCount = transactionsData?.uncategorizedCount || 0; const uncategorizedPercent = totalTransactions > 0 ? Math.round((uncategorizedCount / totalTransactions) * 100) : 0; // Use total from chart data (all filtered transactions) or fallback to paginated data const totalAmount = chartTotalAmount ?? 0; const displayTotalCount = chartTotalCount ?? totalTransactions; // Calculate percentages from chart transactions (all filtered transactions) const reconciledPercent = useMemo(() => { if (chartTransactions.length === 0) return "0.00"; const reconciledCount = chartTransactions.filter( (t) => t.isReconciled ).length; return ((reconciledCount / chartTransactions.length) * 100).toFixed(2); }, [chartTransactions]); const categorizedPercent = useMemo(() => { if (chartTransactions.length === 0) return "0.00"; const categorizedCount = chartTransactions.filter( (t) => t.categoryId !== null ).length; return ((categorizedCount / chartTransactions.length) * 100).toFixed(2); }, [chartTransactions]); // Persist statistics collapsed state in localStorage const [isStatsExpanded, setIsStatsExpanded] = useLocalStorage( "transactions-stats-expanded", true ); // Early return for loading state - prevents sidebar flash if (isLoadingMetadata || !metadata) { return (
Nombre de transactions
{displayTotalCount}
Total
= 0 ? "text-emerald-600" : "text-red-600" }`} > {formatCurrency(totalAmount)}
Pointé
{reconciledPercent}%
Catégorisé
{categorizedPercent}%