diff --git a/.dockerignore b/.dockerignore index 566633b..8af1773 100644 --- a/.dockerignore +++ b/.dockerignore @@ -14,9 +14,3 @@ README.md .vscode .idea - - - - - - diff --git a/app/accounts/page.tsx b/app/accounts/page.tsx index eb7e199..6447020 100644 --- a/app/accounts/page.tsx +++ b/app/accounts/page.tsx @@ -68,7 +68,7 @@ function FolderDropZone({
{children} @@ -91,7 +91,7 @@ export default function AccountsPage() { const [editingAccount, setEditingAccount] = useState(null); const [isDialogOpen, setIsDialogOpen] = useState(false); const [selectedAccounts, setSelectedAccounts] = useState>( - new Set() + new Set(), ); const [formData, setFormData] = useState({ name: "", @@ -117,7 +117,7 @@ export default function AccountsPage() { activationConstraint: { distance: 8, }, - }) + }), ); if ( @@ -131,7 +131,7 @@ export default function AccountsPage() { // Convert accountsWithStats to regular accounts for compatibility const accounts = accountsWithStats.map( - ({ transactionCount: _transactionCount, ...account }) => account + ({ transactionCount: _transactionCount, ...account }) => account, ); const formatCurrency = (amount: number) => { @@ -191,7 +191,7 @@ export default function AccountsPage() { const count = selectedAccounts.size; if ( !confirm( - `Supprimer ${count} compte${count > 1 ? "s" : ""} et toutes leurs transactions ?` + `Supprimer ${count} compte${count > 1 ? "s" : ""} et toutes leurs transactions ?`, ) ) return; @@ -202,7 +202,7 @@ export default function AccountsPage() { `/api/banking/accounts?ids=${ids.join(",")}`, { method: "DELETE", - } + }, ); if (!response.ok) { throw new Error("Failed to delete accounts"); @@ -275,7 +275,7 @@ export default function AccountsPage() { const handleDeleteFolder = async (folderId: string) => { if ( !confirm( - "Supprimer ce dossier ? Les comptes seront déplacés à la racine." + "Supprimer ce dossier ? Les comptes seront déplacés à la racine.", ) ) return; @@ -315,7 +315,7 @@ export default function AccountsPage() { // Déplacer vers le dossier du compte cible const targetAccountId = overId.replace("account-", ""); const targetAccount = accountsWithStats.find( - (a) => a.id === targetAccountId + (a) => a.id === targetAccountId, ); if (targetAccount) { targetFolderId = targetAccount.folderId; @@ -337,7 +337,7 @@ export default function AccountsPage() { (old: Array | undefined) => { if (!old) return old; return old.map((a) => (a.id === accountId ? updatedAccount : a)); - } + }, ); // Faire la requête en arrière-plan @@ -362,7 +362,7 @@ export default function AccountsPage() { const totalBalance = accounts.reduce( (sum, a) => sum + getAccountBalance(a), - 0 + 0, ); // Grouper les comptes par folder @@ -375,7 +375,7 @@ export default function AccountsPage() { acc[folderId].push(account); return acc; }, - {} as Record + {} as Record, ); // Obtenir les folders racine (sans parent) et les trier par nom @@ -429,7 +429,7 @@ export default function AccountsPage() { className={cn( isMobile ? "text-xl" : "text-2xl", "font-bold", - totalBalance >= 0 ? "text-emerald-600" : "text-red-600" + totalBalance >= 0 ? "text-emerald-600" : "text-red-600", )} > {isMobile && ( @@ -486,17 +486,17 @@ export default function AccountsPage() { "text-xs sm:text-sm font-semibold tabular-nums shrink-0", accountsByFolder["no-folder"].reduce( (sum, a) => sum + getAccountBalance(a), - 0 + 0, ) >= 0 ? "text-emerald-600" - : "text-red-600" + : "text-red-600", )} > {formatCurrency( accountsByFolder["no-folder"].reduce( (sum, a) => sum + getAccountBalance(a), - 0 - ) + 0, + ), )}
@@ -505,7 +505,7 @@ export default function AccountsPage() {
{accountsByFolder["no-folder"].map((account) => { const folder = metadata.folders.find( - (f: FolderType) => f.id === account.folderId + (f: FolderType) => f.id === account.folderId, ); return ( @@ -533,7 +533,7 @@ export default function AccountsPage() { const folderAccounts = accountsByFolder[folder.id] || []; const folderBalance = folderAccounts.reduce( (sum, a) => sum + getAccountBalance(a), - 0 + 0, ); return ( @@ -562,7 +562,7 @@ export default function AccountsPage() { "text-xs sm:text-sm font-semibold tabular-nums shrink-0", folderBalance >= 0 ? "text-emerald-600" - : "text-red-600" + : "text-red-600", )} > {formatCurrency(folderBalance)} @@ -622,7 +622,7 @@ export default function AccountsPage() {
{folderAccounts.map((account) => { const accountFolder = metadata.folders.find( - (f: FolderType) => f.id === account.folderId + (f: FolderType) => f.id === account.folderId, ); return ( @@ -666,7 +666,7 @@ export default function AccountsPage() { {accounts.find( - (a) => a.id === activeId.replace("account-", "") + (a) => a.id === activeId.replace("account-", ""), )?.name || ""} diff --git a/app/api/banking/accounts/route.ts b/app/api/banking/accounts/route.ts index cb82f28..3582ef1 100644 --- a/app/api/banking/accounts/route.ts +++ b/app/api/banking/accounts/route.ts @@ -23,7 +23,7 @@ export async function GET(request: NextRequest) { console.error("Error fetching accounts:", error); return NextResponse.json( { error: "Failed to fetch accounts" }, - { status: 500 } + { status: 500 }, ); } } @@ -51,7 +51,7 @@ export async function POST(request: Request) { console.error("Error creating account:", error); return NextResponse.json( { error: "Failed to create account" }, - { status: 500 } + { status: 500 }, ); } } @@ -79,7 +79,7 @@ export async function PUT(request: Request) { console.error("Error updating account:", error); return NextResponse.json( { error: "Failed to update account" }, - { status: 500 } + { status: 500 }, ); } } @@ -99,7 +99,7 @@ export async function DELETE(request: Request) { if (accountIds.length === 0) { return NextResponse.json( { error: "At least one account ID is required" }, - { status: 400 } + { status: 400 }, ); } await accountService.deleteMany(accountIds); @@ -116,14 +116,14 @@ export async function DELETE(request: Request) { headers: { "Cache-Control": "no-store", }, - } + }, ); } if (!id) { return NextResponse.json( { error: "Account ID is required" }, - { status: 400 } + { status: 400 }, ); } @@ -141,13 +141,13 @@ export async function DELETE(request: Request) { headers: { "Cache-Control": "no-store", }, - } + }, ); } catch (error) { console.error("Error deleting account:", error); return NextResponse.json( { error: "Failed to delete account" }, - { status: 500 } + { status: 500 }, ); } } diff --git a/app/api/banking/categories/route.ts b/app/api/banking/categories/route.ts index d292838..761786e 100644 --- a/app/api/banking/categories/route.ts +++ b/app/api/banking/categories/route.ts @@ -23,7 +23,7 @@ export async function GET(request: NextRequest) { console.error("Error fetching category stats:", error); return NextResponse.json( { error: "Failed to fetch category stats" }, - { status: 500 } + { status: 500 }, ); } } @@ -50,7 +50,7 @@ export async function POST(request: Request) { console.error("Error creating category:", error); return NextResponse.json( { error: "Failed to create category" }, - { status: 500 } + { status: 500 }, ); } } @@ -78,7 +78,7 @@ export async function PUT(request: Request) { console.error("Error updating category:", error); return NextResponse.json( { error: "Failed to update category" }, - { status: 500 } + { status: 500 }, ); } } @@ -94,7 +94,7 @@ export async function DELETE(request: Request) { if (!id) { return NextResponse.json( { error: "Category ID is required" }, - { status: 400 } + { status: 400 }, ); } @@ -112,13 +112,13 @@ export async function DELETE(request: Request) { headers: { "Cache-Control": "no-store", }, - } + }, ); } catch (error) { console.error("Error deleting category:", error); return NextResponse.json( { error: "Failed to delete category" }, - { status: 500 } + { status: 500 }, ); } } diff --git a/app/api/banking/duplicates/ids/route.ts b/app/api/banking/duplicates/ids/route.ts index 9052ab6..773bdb3 100644 --- a/app/api/banking/duplicates/ids/route.ts +++ b/app/api/banking/duplicates/ids/route.ts @@ -17,4 +17,3 @@ export async function GET() { ); } } - diff --git a/app/api/banking/folders/route.ts b/app/api/banking/folders/route.ts index 3d1471e..2bc282a 100644 --- a/app/api/banking/folders/route.ts +++ b/app/api/banking/folders/route.ts @@ -23,7 +23,7 @@ export async function POST(request: Request) { console.error("Error creating folder:", error); return NextResponse.json( { error: "Failed to create folder" }, - { status: 500 } + { status: 500 }, ); } } @@ -48,7 +48,7 @@ export async function PUT(request: Request) { console.error("Error updating folder:", error); return NextResponse.json( { error: "Failed to update folder" }, - { status: 500 } + { status: 500 }, ); } } @@ -64,7 +64,7 @@ export async function DELETE(request: Request) { if (!id) { return NextResponse.json( { error: "Folder ID is required" }, - { status: 400 } + { status: 400 }, ); } @@ -79,7 +79,7 @@ export async function DELETE(request: Request) { headers: { "Cache-Control": "no-store", }, - } + }, ); } catch (error) { if (error instanceof FolderNotFoundError) { @@ -88,7 +88,7 @@ export async function DELETE(request: Request) { console.error("Error deleting folder:", error); return NextResponse.json( { error: "Failed to delete folder" }, - { status: 500 } + { status: 500 }, ); } } diff --git a/app/api/banking/transactions/clear-categories/route.ts b/app/api/banking/transactions/clear-categories/route.ts index 8499b59..adaa2ed 100644 --- a/app/api/banking/transactions/clear-categories/route.ts +++ b/app/api/banking/transactions/clear-categories/route.ts @@ -31,7 +31,7 @@ export async function POST() { headers: { "Cache-Control": "no-store", }, - } + }, ); } catch (error) { console.error("Error clearing categories:", error); diff --git a/app/api/banking/transactions/route.ts b/app/api/banking/transactions/route.ts index e0b33d2..a0efe5a 100644 --- a/app/api/banking/transactions/route.ts +++ b/app/api/banking/transactions/route.ts @@ -64,7 +64,7 @@ export async function GET(request: NextRequest) { console.error("Error fetching transactions:", error); return NextResponse.json( { error: "Failed to fetch transactions" }, - { status: 500 } + { status: 500 }, ); } } @@ -90,7 +90,7 @@ export async function POST(request: Request) { console.error("Error creating transactions:", error); return NextResponse.json( { error: "Failed to create transactions" }, - { status: 500 } + { status: 500 }, ); } } @@ -103,7 +103,7 @@ export async function PUT(request: Request) { const transaction: Transaction = await request.json(); const updated = await transactionService.update( transaction.id, - transaction + transaction, ); // Revalider le cache des pages @@ -120,7 +120,7 @@ export async function PUT(request: Request) { console.error("Error updating transaction:", error); return NextResponse.json( { error: "Failed to update transaction" }, - { status: 500 } + { status: 500 }, ); } } @@ -136,7 +136,7 @@ export async function DELETE(request: Request) { if (!id) { return NextResponse.json( { error: "Transaction ID is required" }, - { status: 400 } + { status: 400 }, ); } @@ -153,7 +153,7 @@ export async function DELETE(request: Request) { headers: { "Cache-Control": "no-store", }, - } + }, ); } catch (error) { console.error("Error deleting transaction:", error); diff --git a/app/categories/page.tsx b/app/categories/page.tsx index e991cec..6e9d336 100644 --- a/app/categories/page.tsx +++ b/app/categories/page.tsx @@ -42,7 +42,7 @@ export default function CategoriesPage() { const [isDialogOpen, setIsDialogOpen] = useState(false); const [editingCategory, setEditingCategory] = useState(null); const [expandedParents, setExpandedParents] = useState>( - new Set() + new Set(), ); const [formData, setFormData] = useState({ name: "", @@ -53,7 +53,7 @@ export default function CategoriesPage() { }); const [searchQuery, setSearchQuery] = useState(""); const [recatResults, setRecatResults] = useState( - [] + [], ); const [isRecatDialogOpen, setIsRecatDialogOpen] = useState(false); const [isRecategorizing, setIsRecategorizing] = useState(false); @@ -69,7 +69,7 @@ export default function CategoriesPage() { }; const parents = metadata.categories.filter( - (c: Category) => c.parentId === null + (c: Category) => c.parentId === null, ); const children: Record = {}; const orphans: Category[] = []; @@ -78,7 +78,7 @@ export default function CategoriesPage() { .filter((c: Category) => c.parentId !== null) .forEach((child: Category) => { const parentExists = parents.some( - (p: Category) => p.id === child.parentId + (p: Category) => p.id === child.parentId, ); if (parentExists) { if (!children[child.parentId!]) { @@ -136,7 +136,7 @@ export default function CategoriesPage() { return { total, count }; }, - [categoryStats, childrenByParent] + [categoryStats, childrenByParent], ); if (isLoadingMetadata || !metadata || isLoadingStats || !categoryStats) { @@ -248,7 +248,7 @@ export default function CategoriesPage() { try { // Fetch uncategorized transactions const uncategorizedResponse = await fetch( - "/api/banking/transactions?limit=1000&offset=0&includeUncategorized=true" + "/api/banking/transactions?limit=1000&offset=0&includeUncategorized=true", ); if (!uncategorizedResponse.ok) { throw new Error("Failed to fetch uncategorized transactions"); @@ -261,11 +261,11 @@ export default function CategoriesPage() { for (const transaction of uncategorized) { const categoryId = autoCategorize( transaction.description + " " + (transaction.memo || ""), - metadata.categories + metadata.categories, ); if (categoryId) { const category = metadata.categories.find( - (c: Category) => c.id === categoryId + (c: Category) => c.id === categoryId, ); if (category) { results.push({ transaction, category }); @@ -299,9 +299,9 @@ export default function CategoriesPage() { return children.some( (c) => c.name.toLowerCase().includes(query) || - c.keywords.some((k) => k.toLowerCase().includes(query)) + c.keywords.some((k) => k.toLowerCase().includes(query)), ); - } + }, ); return ( @@ -346,9 +346,9 @@ export default function CategoriesPage() { (c) => c.name.toLowerCase().includes(searchQuery.toLowerCase()) || c.keywords.some((k) => - k.toLowerCase().includes(searchQuery.toLowerCase()) + k.toLowerCase().includes(searchQuery.toLowerCase()), ) || - parent.name.toLowerCase().includes(searchQuery.toLowerCase()) + parent.name.toLowerCase().includes(searchQuery.toLowerCase()), ) : allChildren; const stats = getCategoryStats(parent.id, true); @@ -435,7 +435,7 @@ export default function CategoriesPage() {

{new Date(result.transaction.date).toLocaleDateString( - "fr-FR" + "fr-FR", )} {" • "} {new Intl.NumberFormat("fr-FR", { diff --git a/app/rules/page.tsx b/app/rules/page.tsx index 978c365..d83c52c 100644 --- a/app/rules/page.tsx +++ b/app/rules/page.tsx @@ -46,7 +46,7 @@ export default function RulesPage() { offset: 0, includeUncategorized: true, }, - !!metadata + !!metadata, ); const refresh = useCallback(() => { @@ -58,7 +58,7 @@ export default function RulesPage() { const [filterMinCount, setFilterMinCount] = useState(2); const [expandedGroups, setExpandedGroups] = useState>(new Set()); const [selectedGroup, setSelectedGroup] = useState( - null + null, ); const [isDialogOpen, setIsDialogOpen] = useState(false); const [isAutoCategorizing, setIsAutoCategorizing] = useState(false); @@ -89,7 +89,7 @@ export default function RulesPage() { totalAmount: transactions.reduce((sum, t) => sum + t.amount, 0), suggestedKeyword: suggestKeyword(descriptions), }; - } + }, ); // Filter by search query @@ -100,7 +100,7 @@ export default function RulesPage() { (g) => g.displayName.toLowerCase().includes(query) || g.key.includes(query) || - g.suggestedKeyword.toLowerCase().includes(query) + g.suggestedKeyword.toLowerCase().includes(query), ); } @@ -169,7 +169,7 @@ export default function RulesPage() { // 1. Add keyword to category const category = metadata.categories.find( - (c: { id: string }) => c.id === ruleData.categoryId + (c: { id: string }) => c.id === ruleData.categoryId, ); if (!category) { throw new Error("Category not found"); @@ -177,7 +177,7 @@ export default function RulesPage() { // Check if keyword already exists const keywordExists = category.keywords.some( - (k: string) => k.toLowerCase() === ruleData.keyword.toLowerCase() + (k: string) => k.toLowerCase() === ruleData.keyword.toLowerCase(), ); if (!keywordExists) { @@ -195,8 +195,8 @@ export default function RulesPage() { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ id, categoryId: ruleData.categoryId }), - }) - ) + }), + ), ); } @@ -204,7 +204,7 @@ export default function RulesPage() { invalidateAllTransactionQueries(queryClient); invalidateAllCategoryQueries(queryClient); }, - [metadata, queryClient] + [metadata, queryClient], ); const handleAutoCategorize = useCallback(async () => { @@ -218,7 +218,7 @@ export default function RulesPage() { for (const transaction of uncategorized) { const categoryId = autoCategorize( transaction.description + " " + (transaction.memo || ""), - metadata.categories + metadata.categories, ); if (categoryId) { await fetch("/api/banking/transactions", { @@ -234,7 +234,7 @@ export default function RulesPage() { invalidateAllTransactionQueries(queryClient); invalidateAllCategoryQueries(queryClient); alert( - `${categorizedCount} transaction(s) catégorisée(s) automatiquement` + `${categorizedCount} transaction(s) catégorisée(s) automatiquement`, ); } catch (error) { console.error("Error auto-categorizing:", error); @@ -253,8 +253,8 @@ export default function RulesPage() { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ...t, categoryId }), - }) - ) + }), + ), ); // Invalider toutes les queries liées invalidateAllTransactionQueries(queryClient); @@ -264,7 +264,7 @@ export default function RulesPage() { alert("Erreur lors de la catégorisation"); } }, - [queryClient] + [queryClient], ); if ( diff --git a/app/transactions/page.tsx b/app/transactions/page.tsx index 7f082eb..ef1a023 100644 --- a/app/transactions/page.tsx +++ b/app/transactions/page.tsx @@ -98,7 +98,7 @@ export default function TransactionsPage() { handleBulkReconcile(reconciled, selectedTransactions); clearSelection(); }, - [handleBulkReconcile, selectedTransactions, clearSelection] + [handleBulkReconcile, selectedTransactions, clearSelection], ); const handleBulkSetCategoryWithClear = useCallback( @@ -106,7 +106,7 @@ export default function TransactionsPage() { handleBulkSetCategory(categoryId, selectedTransactions); clearSelection(); }, - [handleBulkSetCategory, selectedTransactions, clearSelection] + [handleBulkSetCategory, selectedTransactions, clearSelection], ); const filteredTransactions = transactionsData?.transactions || []; diff --git a/components/transactions/transaction-filters.tsx b/components/transactions/transaction-filters.tsx index b8163fc..4e2fb4b 100644 --- a/components/transactions/transaction-filters.tsx +++ b/components/transactions/transaction-filters.tsx @@ -279,7 +279,7 @@ export function TransactionFilters({ onRemoveCategory={(id) => { const newCategories = selectedCategories.filter((c) => c !== id); onCategoriesChange( - newCategories.length > 0 ? newCategories : ["all"] + newCategories.length > 0 ? newCategories : ["all"], ); }} onClearCategories={() => onCategoriesChange(["all"])} @@ -391,7 +391,7 @@ function ActiveFilters({ const selectedAccs = accounts.filter((a) => selectedAccounts.includes(a.id)); const selectedCats = categories.filter((c) => - selectedCategories.includes(c.id) + selectedCategories.includes(c.id), ); const isUncategorized = selectedCategories.includes("uncategorized"); diff --git a/components/transactions/transaction-pagination.tsx b/components/transactions/transaction-pagination.tsx index 3ace454..a72eeaa 100644 --- a/components/transactions/transaction-pagination.tsx +++ b/components/transactions/transaction-pagination.tsx @@ -48,4 +48,3 @@ export function TransactionPagination({

); } - diff --git a/components/transactions/transaction-table.tsx b/components/transactions/transaction-table.tsx index 0157cab..d29ea35 100644 --- a/components/transactions/transaction-table.tsx +++ b/components/transactions/transaction-table.tsx @@ -169,7 +169,7 @@ export function TransactionTable({ setFocusedIndex(index); onMarkReconciled(transactionId); }, - [onMarkReconciled] + [onMarkReconciled], ); const handleKeyDown = useCallback( @@ -198,7 +198,7 @@ export function TransactionTable({ } } }, - [focusedIndex, transactions, onMarkReconciled, virtualizer] + [focusedIndex, transactions, onMarkReconciled, virtualizer], ); useEffect(() => { @@ -215,7 +215,7 @@ export function TransactionTable({ (accountId: string) => { return accounts.find((a) => a.id === accountId); }, - [accounts] + [accounts], ); const getCategory = useCallback( @@ -223,7 +223,7 @@ export function TransactionTable({ if (!categoryId) return null; return categories.find((c) => c.id === categoryId); }, - [categories] + [categories], ); return ( @@ -281,7 +281,7 @@ export function TransactionTable({ "p-4 space-y-3 hover:bg-muted/50 cursor-pointer border-b border-border", transaction.isReconciled && "bg-emerald-500/5", isFocused && "bg-primary/10 ring-1 ring-primary/30", - isDuplicate && "shadow-sm" + isDuplicate && "shadow-sm", )} >
@@ -323,7 +323,7 @@ export function TransactionTable({ "font-semibold tabular-nums text-sm md:text-base shrink-0", transaction.amount >= 0 ? "text-emerald-600" - : "text-red-600" + : "text-red-600", )} > {transaction.amount >= 0 ? "+" : ""} @@ -358,7 +358,7 @@ export function TransactionTable({ showBadge align="start" disabled={updatingTransactionIds.has( - transaction.id + transaction.id, )} />
@@ -391,7 +391,7 @@ export function TransactionTable({ e.stopPropagation(); if ( confirm( - `Êtes-vous sûr de vouloir supprimer cette transaction ?\n\n${transaction.description}\n${formatCurrency(transaction.amount)}` + `Êtes-vous sûr de vouloir supprimer cette transaction ?\n\n${transaction.description}\n${formatCurrency(transaction.amount)}`, ) ) { onDelete(transaction.id); @@ -507,7 +507,7 @@ export function TransactionTable({ "grid grid-cols-[auto_120px_2fr_150px_180px_140px_auto_auto] gap-0 border-b border-border hover:bg-muted/50 cursor-pointer", transaction.isReconciled && "bg-emerald-500/5", isFocused && "bg-primary/10 ring-1 ring-primary/30", - isDuplicate && "shadow-sm" + isDuplicate && "shadow-sm", )} >
e.stopPropagation()}> @@ -576,7 +576,7 @@ export function TransactionTable({ "p-3 text-right font-semibold tabular-nums", transaction.amount >= 0 ? "text-emerald-600" - : "text-red-600" + : "text-red-600", )} > {transaction.amount >= 0 ? "+" : ""} @@ -643,7 +643,7 @@ export function TransactionTable({ e.stopPropagation(); if ( confirm( - `Êtes-vous sûr de vouloir supprimer cette transaction ?\n\n${transaction.description}\n${formatCurrency(transaction.amount)}` + `Êtes-vous sûr de vouloir supprimer cette transaction ?\n\n${transaction.description}\n${formatCurrency(transaction.amount)}`, ) ) { onDelete(transaction.id); diff --git a/hooks/use-transaction-mutations.ts b/hooks/use-transaction-mutations.ts index 75cafe7..2da70b6 100644 --- a/hooks/use-transaction-mutations.ts +++ b/hooks/use-transaction-mutations.ts @@ -26,7 +26,7 @@ export function useTransactionMutations({ if (!transactionsData) return; const transaction = transactionsData.transactions.find( - (t) => t.id === transactionId + (t) => t.id === transactionId, ); if (!transaction) return; @@ -48,7 +48,7 @@ export function useTransactionMutations({ transactions: oldData.transactions.map((t) => t.id === transactionId ? { ...t, isReconciled: newReconciledState } - : t + : t, ), }; }); @@ -75,7 +75,7 @@ export function useTransactionMutations({ invalidateAllTransactionQueries(queryClient); } }, - [transactionsData, transactionParams, queryClient] + [transactionsData, transactionParams, queryClient], ); const markReconciled = useCallback( @@ -83,7 +83,7 @@ export function useTransactionMutations({ if (!transactionsData) return; const transaction = transactionsData.transactions.find( - (t) => t.id === transactionId + (t) => t.id === transactionId, ); if (!transaction || transaction.isReconciled) return; @@ -102,7 +102,7 @@ export function useTransactionMutations({ return { ...oldData, transactions: oldData.transactions.map((t) => - t.id === transactionId ? { ...t, isReconciled: true } : t + t.id === transactionId ? { ...t, isReconciled: true } : t, ), }; }); @@ -129,7 +129,7 @@ export function useTransactionMutations({ invalidateAllTransactionQueries(queryClient); } }, - [transactionsData, transactionParams, queryClient] + [transactionsData, transactionParams, queryClient], ); const setCategory = useCallback( @@ -137,7 +137,7 @@ export function useTransactionMutations({ if (!transactionsData) return; const transaction = transactionsData.transactions.find( - (t) => t.id === transactionId + (t) => t.id === transactionId, ); if (!transaction) return; @@ -154,7 +154,7 @@ export function useTransactionMutations({ return { ...oldData, transactions: oldData.transactions.map((t) => - t.id === transactionId ? { ...t, categoryId } : t + t.id === transactionId ? { ...t, categoryId } : t, ), }; }); @@ -187,7 +187,7 @@ export function useTransactionMutations({ }); } }, - [transactionsData, transactionParams, queryClient] + [transactionsData, transactionParams, queryClient], ); const deleteTransaction = useCallback( @@ -205,7 +205,7 @@ export function useTransactionMutations({ return { ...oldData, transactions: oldData.transactions.filter( - (t) => t.id !== transactionId + (t) => t.id !== transactionId, ), total: oldData.total - 1, }; @@ -216,14 +216,14 @@ export function useTransactionMutations({ `/api/banking/transactions?id=${transactionId}`, { method: "DELETE", - } + }, ); if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw new Error( errorData.error || - `Failed to delete transaction: ${response.status}` + `Failed to delete transaction: ${response.status}`, ); } @@ -238,7 +238,7 @@ export function useTransactionMutations({ invalidateAllTransactionQueries(queryClient); } }, - [transactionsData, transactionParams, queryClient] + [transactionsData, transactionParams, queryClient], ); const bulkReconcile = useCallback( @@ -246,7 +246,7 @@ export function useTransactionMutations({ if (!transactionsData) return; const transactionsToUpdate = transactionsData.transactions.filter((t) => - selectedTransactionIds.has(t.id) + selectedTransactionIds.has(t.id), ); const transactionIds = transactionsToUpdate.map((t) => t.id); @@ -263,7 +263,7 @@ export function useTransactionMutations({ transactions: oldData.transactions.map((t) => transactionIds.includes(t.id) ? { ...t, isReconciled: reconciled } - : t + : t, ), }; }); @@ -275,8 +275,8 @@ export function useTransactionMutations({ method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ...t, isReconciled: reconciled }), - }) - ) + }), + ), ); // TOUJOURS revalider après succès pour garantir la cohérence @@ -290,7 +290,7 @@ export function useTransactionMutations({ invalidateAllTransactionQueries(queryClient); } }, - [transactionsData, transactionParams, queryClient] + [transactionsData, transactionParams, queryClient], ); const bulkSetCategory = useCallback( @@ -298,7 +298,7 @@ export function useTransactionMutations({ if (!transactionsData) return; const transactionsToUpdate = transactionsData.transactions.filter((t) => - selectedTransactionIds.has(t.id) + selectedTransactionIds.has(t.id), ); const transactionIds = transactionsToUpdate.map((t) => t.id); @@ -318,7 +318,7 @@ export function useTransactionMutations({ return { ...oldData, transactions: oldData.transactions.map((t) => - transactionIds.includes(t.id) ? { ...t, categoryId } : t + transactionIds.includes(t.id) ? { ...t, categoryId } : t, ), }; }); @@ -330,8 +330,8 @@ export function useTransactionMutations({ method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ...t, categoryId }), - }) - ) + }), + ), ); // TOUJOURS revalider après succès pour garantir la cohérence @@ -351,7 +351,7 @@ export function useTransactionMutations({ }); } }, - [transactionsData, transactionParams, queryClient] + [transactionsData, transactionParams, queryClient], ); return { diff --git a/hooks/use-transaction-rules.ts b/hooks/use-transaction-rules.ts index c2e7c7a..305ce06 100644 --- a/hooks/use-transaction-rules.ts +++ b/hooks/use-transaction-rules.ts @@ -27,7 +27,7 @@ export function useTransactionRules({ const queryClient = useQueryClient(); const [ruleDialogOpen, setRuleDialogOpen] = useState(false); const [ruleTransaction, setRuleTransaction] = useState( - null + null, ); const handleCreateRule = useCallback((transaction: Transaction) => { @@ -40,7 +40,7 @@ export function useTransactionRules({ const normalizedDesc = normalizeDescription(ruleTransaction.description); const similarTransactions = transactionsData.transactions.filter( - (t) => normalizeDescription(t.description) === normalizedDesc + (t) => normalizeDescription(t.description) === normalizedDesc, ); if (similarTransactions.length === 0) return null; @@ -51,7 +51,7 @@ export function useTransactionRules({ transactions: similarTransactions, totalAmount: similarTransactions.reduce((sum, t) => sum + t.amount, 0), suggestedKeyword: suggestKeyword( - similarTransactions.map((t) => t.description) + similarTransactions.map((t) => t.description), ), }; }, [ruleTransaction, transactionsData]); @@ -67,7 +67,7 @@ export function useTransactionRules({ // Add keyword to category const category = metadata.categories.find( - (c) => c.id === ruleData.categoryId + (c) => c.id === ruleData.categoryId, ); if (!category) { throw new Error("Category not found"); @@ -75,7 +75,7 @@ export function useTransactionRules({ // Check if keyword already exists const keywordExists = category.keywords.some( - (k: string) => k.toLowerCase() === ruleData.keyword.toLowerCase() + (k: string) => k.toLowerCase() === ruleData.keyword.toLowerCase(), ); if (!keywordExists) { @@ -93,8 +93,8 @@ export function useTransactionRules({ method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ id, categoryId: ruleData.categoryId }), - }) - ) + }), + ), ); } @@ -103,7 +103,7 @@ export function useTransactionRules({ invalidateAllCategoryQueries(queryClient); setRuleDialogOpen(false); }, - [metadata, queryClient] + [metadata, queryClient], ); return { diff --git a/hooks/use-transactions-page.ts b/hooks/use-transactions-page.ts index eaca98f..a4bf54f 100644 --- a/hooks/use-transactions-page.ts +++ b/hooks/use-transactions-page.ts @@ -31,10 +31,10 @@ export function useTransactionsPage() { const [showReconciled, setShowReconciled] = useState("all"); const [period, setPeriod] = useState("all"); const [customStartDate, setCustomStartDate] = useState( - undefined + undefined, ); const [customEndDate, setCustomEndDate] = useState( - undefined + undefined, ); const [isCustomDatePickerOpen, setIsCustomDatePickerOpen] = useState(false); const [showDuplicates, setShowDuplicates] = useState(false); @@ -48,7 +48,7 @@ export function useTransactionsPage() { // Selection state const [selectedTransactions, setSelectedTransactions] = useState>( - new Set() + new Set(), ); // Debounce search query @@ -168,18 +168,15 @@ export function useTransactionsPage() { setPage(0); }, []); - const handlePeriodChange = useCallback( - (p: Period) => { - setPeriod(p); - setPage(0); - if (p !== "custom") { - setIsCustomDatePickerOpen(false); - } else { - setIsCustomDatePickerOpen(true); - } - }, - [] - ); + const handlePeriodChange = useCallback((p: Period) => { + setPeriod(p); + setPage(0); + if (p !== "custom") { + setIsCustomDatePickerOpen(false); + } else { + setIsCustomDatePickerOpen(true); + } + }, []); const handleCustomStartDateChange = useCallback((date: Date | undefined) => { setCustomStartDate(date); @@ -191,15 +188,18 @@ export function useTransactionsPage() { setPage(0); }, []); - const handleSortChange = useCallback((field: SortField) => { - if (sortField === field) { - setSortOrder((prev) => (prev === "asc" ? "desc" : "asc")); - } else { - setSortField(field); - setSortOrder(field === "date" ? "desc" : "asc"); - } - setPage(0); - }, [sortField]); + const handleSortChange = useCallback( + (field: SortField) => { + if (sortField === field) { + setSortOrder((prev) => (prev === "asc" ? "desc" : "asc")); + } else { + setSortField(field); + setSortOrder(field === "date" ? "desc" : "asc"); + } + setPage(0); + }, + [sortField], + ); const toggleSelectAll = useCallback(() => { if (!transactionsData) return; @@ -207,7 +207,7 @@ export function useTransactionsPage() { setSelectedTransactions(new Set()); } else { setSelectedTransactions( - new Set(transactionsData.transactions.map((t) => t.id)) + new Set(transactionsData.transactions.map((t) => t.id)), ); } }, [transactionsData, selectedTransactions.size]); @@ -283,4 +283,3 @@ export function useTransactionsPage() { transactionParams, }; } - diff --git a/lib/hooks.ts b/lib/hooks.ts index 0b4ea69..4d7dfc4 100644 --- a/lib/hooks.ts +++ b/lib/hooks.ts @@ -83,7 +83,7 @@ export function useLocalStorage(key: string, initialValue: T) { // Helper function to serialize transaction params into a query key export function getTransactionsQueryKey( - params: TransactionsPaginatedParams = {} + params: TransactionsPaginatedParams = {}, ): (string | number)[] { const key: (string | number)[] = ["transactions"]; if (params.limit) key.push(`limit:${params.limit}`); @@ -106,7 +106,7 @@ export function getTransactionsQueryKey( export function useTransactions( params: TransactionsPaginatedParams = {}, - enabled = true + enabled = true, ) { const queryClient = useQueryClient(); @@ -134,7 +134,7 @@ export function useTransactions( if (params.isReconciled !== undefined && params.isReconciled !== "all") { searchParams.set( "isReconciled", - params.isReconciled === true ? "true" : "false" + params.isReconciled === true ? "true" : "false", ); } if (params.sortField) searchParams.set("sortField", params.sortField); diff --git a/services/transaction.service.ts b/services/transaction.service.ts index 0c5e13a..327857e 100644 --- a/services/transaction.service.ts +++ b/services/transaction.service.ts @@ -41,14 +41,14 @@ export const transactionService = { // Create sets for fast lookup const existingFitIdSet = new Set( - existingByFitId.map((t) => `${t.accountId}-${t.fitId}`) + existingByFitId.map((t) => `${t.accountId}-${t.fitId}`), ); // Create set for duplicates by amount + date + description const existingCriteriaSet = new Set( allExistingTransactions.map( - (t) => `${t.accountId}-${t.date}-${t.amount}-${t.description}` - ) + (t) => `${t.accountId}-${t.date}-${t.amount}-${t.description}`, + ), ); // Filter out duplicates based on fitId OR (amount + date + description) @@ -85,7 +85,7 @@ export const transactionService = { async update( id: string, - data: Partial> + data: Partial>, ): Promise { const updated = await prisma.transaction.update({ where: { id },