"use client"; import { useState, useCallback } from "react"; import { useQueryClient } from "@tanstack/react-query"; import type { Transaction } from "@/lib/types"; import { getTransactionsQueryKey } from "@/lib/hooks"; import type { TransactionsPaginatedParams } from "@/services/banking.service"; import { invalidateAllTransactionQueries } from "@/lib/cache-utils"; interface UseTransactionMutationsProps { transactionParams: TransactionsPaginatedParams; transactionsData: { transactions: Transaction[]; total: number } | undefined; } export function useTransactionMutations({ transactionParams, transactionsData, }: UseTransactionMutationsProps) { const queryClient = useQueryClient(); const [updatingTransactionIds, setUpdatingTransactionIds] = useState< Set >(new Set()); const toggleReconciled = useCallback( async (transactionId: string) => { if (!transactionsData) return; const transaction = transactionsData.transactions.find( (t) => t.id === transactionId ); if (!transaction) return; const newReconciledState = !transaction.isReconciled; const updatedTransaction = { ...transaction, isReconciled: newReconciledState, }; // Optimistic cache update const queryKey = getTransactionsQueryKey(transactionParams); const previousData = queryClient.getQueryData(queryKey); queryClient.setQueryData(queryKey, (oldData) => { if (!oldData) return oldData; return { ...oldData, transactions: oldData.transactions.map((t) => t.id === transactionId ? { ...t, isReconciled: newReconciledState } : t ), }; }); try { const response = await fetch("/api/banking/transactions", { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(updatedTransaction), }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } // TOUJOURS revalider après succès pour garantir la cohérence invalidateAllTransactionQueries(queryClient); } catch (error) { console.error("Failed to update transaction:", error); // Rollback on error if (previousData) { queryClient.setQueryData(queryKey, previousData); } invalidateAllTransactionQueries(queryClient); } }, [transactionsData, transactionParams, queryClient] ); const markReconciled = useCallback( async (transactionId: string) => { if (!transactionsData) return; const transaction = transactionsData.transactions.find( (t) => t.id === transactionId ); if (!transaction || transaction.isReconciled) return; const updatedTransaction = { ...transaction, isReconciled: true, }; // Optimistic cache update const queryKey = getTransactionsQueryKey(transactionParams); const previousData = queryClient.getQueryData(queryKey); queryClient.setQueryData(queryKey, (oldData) => { if (!oldData) return oldData; return { ...oldData, transactions: oldData.transactions.map((t) => t.id === transactionId ? { ...t, isReconciled: true } : t ), }; }); try { const response = await fetch("/api/banking/transactions", { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(updatedTransaction), }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } // TOUJOURS revalider après succès pour garantir la cohérence invalidateAllTransactionQueries(queryClient); } catch (error) { console.error("Failed to update transaction:", error); // Rollback on error if (previousData) { queryClient.setQueryData(queryKey, previousData); } invalidateAllTransactionQueries(queryClient); } }, [transactionsData, transactionParams, queryClient] ); const setCategory = useCallback( async (transactionId: string, categoryId: string | null) => { if (!transactionsData) return; const transaction = transactionsData.transactions.find( (t) => t.id === transactionId ); if (!transaction) return; setUpdatingTransactionIds((prev) => new Set(prev).add(transactionId)); // Optimistic cache update const queryKey = getTransactionsQueryKey(transactionParams); const previousData = queryClient.getQueryData(queryKey); queryClient.setQueryData(queryKey, (oldData) => { if (!oldData) return oldData; return { ...oldData, transactions: oldData.transactions.map((t) => t.id === transactionId ? { ...t, categoryId } : t ), }; }); try { const response = await fetch("/api/banking/transactions", { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ...transaction, categoryId }), }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } // TOUJOURS revalider après succès pour garantir la cohérence invalidateAllTransactionQueries(queryClient); } catch (error) { console.error("Failed to update transaction:", error); // Rollback on error if (previousData) { queryClient.setQueryData(queryKey, previousData); } invalidateAllTransactionQueries(queryClient); } finally { setUpdatingTransactionIds((prev) => { const next = new Set(prev); next.delete(transactionId); return next; }); } }, [transactionsData, transactionParams, queryClient] ); const deleteTransaction = useCallback( async (transactionId: string) => { if (!transactionsData) return; // Save current data for rollback const queryKey = getTransactionsQueryKey(transactionParams); const previousData = queryClient.getQueryData(queryKey); // Optimistic cache update queryClient.setQueryData(queryKey, (oldData) => { if (!oldData) return oldData; return { ...oldData, transactions: oldData.transactions.filter( (t) => t.id !== transactionId ), total: oldData.total - 1, }; }); try { const response = await fetch( `/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}` ); } // TOUJOURS revalider après succès pour garantir la cohérence invalidateAllTransactionQueries(queryClient); } catch (error) { console.error("Failed to delete transaction:", error); // Rollback on error if (previousData) { queryClient.setQueryData(queryKey, previousData); } invalidateAllTransactionQueries(queryClient); } }, [transactionsData, transactionParams, queryClient] ); const bulkReconcile = useCallback( async (reconciled: boolean, selectedTransactionIds: Set) => { if (!transactionsData) return; const transactionsToUpdate = transactionsData.transactions.filter((t) => selectedTransactionIds.has(t.id) ); const transactionIds = transactionsToUpdate.map((t) => t.id); // Optimistic cache update const queryKey = getTransactionsQueryKey(transactionParams); const previousData = queryClient.getQueryData(queryKey); queryClient.setQueryData(queryKey, (oldData) => { if (!oldData) return oldData; return { ...oldData, transactions: oldData.transactions.map((t) => transactionIds.includes(t.id) ? { ...t, isReconciled: reconciled } : t ), }; }); try { await Promise.all( transactionsToUpdate.map((t) => fetch("/api/banking/transactions", { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ...t, isReconciled: reconciled }), }) ) ); // TOUJOURS revalider après succès pour garantir la cohérence invalidateAllTransactionQueries(queryClient); } catch (error) { console.error("Failed to update transactions:", error); // Rollback on error if (previousData) { queryClient.setQueryData(queryKey, previousData); } invalidateAllTransactionQueries(queryClient); } }, [transactionsData, transactionParams, queryClient] ); const bulkSetCategory = useCallback( async (categoryId: string | null, selectedTransactionIds: Set) => { if (!transactionsData) return; const transactionsToUpdate = transactionsData.transactions.filter((t) => selectedTransactionIds.has(t.id) ); const transactionIds = transactionsToUpdate.map((t) => t.id); setUpdatingTransactionIds((prev) => { const next = new Set(prev); transactionIds.forEach((id) => next.add(id)); return next; }); // Optimistic cache update const queryKey = getTransactionsQueryKey(transactionParams); const previousData = queryClient.getQueryData(queryKey); queryClient.setQueryData(queryKey, (oldData) => { if (!oldData) return oldData; return { ...oldData, transactions: oldData.transactions.map((t) => transactionIds.includes(t.id) ? { ...t, categoryId } : t ), }; }); try { await Promise.all( transactionsToUpdate.map((t) => fetch("/api/banking/transactions", { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ ...t, categoryId }), }) ) ); // TOUJOURS revalider après succès pour garantir la cohérence invalidateAllTransactionQueries(queryClient); } catch (error) { console.error("Failed to update transactions:", error); // Rollback on error if (previousData) { queryClient.setQueryData(queryKey, previousData); } invalidateAllTransactionQueries(queryClient); } finally { setUpdatingTransactionIds((prev) => { const next = new Set(prev); transactionIds.forEach((id) => next.delete(id)); return next; }); } }, [transactionsData, transactionParams, queryClient] ); return { toggleReconciled, markReconciled, setCategory, deleteTransaction, bulkReconcile, bulkSetCategory, updatingTransactionIds, }; }