Files
fintrack/hooks/use-transaction-rules.ts

117 lines
3.2 KiB
TypeScript

"use client";
import { useState, useMemo, useCallback } from "react";
import { useQueryClient } from "@tanstack/react-query";
import type { Transaction, Category } from "@/lib/types";
import { updateCategory } from "@/lib/store-db";
import {
normalizeDescription,
suggestKeyword,
} from "@/components/rules/constants";
import {
invalidateAllTransactionQueries,
invalidateAllCategoryQueries,
} from "@/lib/cache-utils";
interface UseTransactionRulesProps {
transactionsData: { transactions: Transaction[] } | undefined;
metadata: {
categories: Category[];
} | null;
}
export function useTransactionRules({
transactionsData,
metadata,
}: UseTransactionRulesProps) {
const queryClient = useQueryClient();
const [ruleDialogOpen, setRuleDialogOpen] = useState(false);
const [ruleTransaction, setRuleTransaction] = useState<Transaction | null>(
null
);
const handleCreateRule = useCallback((transaction: Transaction) => {
setRuleTransaction(transaction);
setRuleDialogOpen(true);
}, []);
const ruleGroup = useMemo(() => {
if (!ruleTransaction || !transactionsData) return null;
const normalizedDesc = normalizeDescription(ruleTransaction.description);
const similarTransactions = transactionsData.transactions.filter(
(t) => normalizeDescription(t.description) === normalizedDesc
);
if (similarTransactions.length === 0) return null;
return {
key: normalizedDesc,
displayName: ruleTransaction.description,
transactions: similarTransactions,
totalAmount: similarTransactions.reduce((sum, t) => sum + t.amount, 0),
suggestedKeyword: suggestKeyword(
similarTransactions.map((t) => t.description)
),
};
}, [ruleTransaction, transactionsData]);
const handleSaveRule = useCallback(
async (ruleData: {
keyword: string;
categoryId: string;
applyToExisting: boolean;
transactionIds: string[];
}) => {
if (!metadata) return;
// Add keyword to category
const category = metadata.categories.find(
(c) => c.id === ruleData.categoryId
);
if (!category) {
throw new Error("Category not found");
}
// Check if keyword already exists
const keywordExists = category.keywords.some(
(k: string) => k.toLowerCase() === ruleData.keyword.toLowerCase()
);
if (!keywordExists) {
await updateCategory({
...category,
keywords: [...category.keywords, ruleData.keyword],
});
}
// Apply to existing transactions if requested
if (ruleData.applyToExisting) {
await Promise.all(
ruleData.transactionIds.map((id) =>
fetch("/api/banking/transactions", {
method: "PUT",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ id, categoryId: ruleData.categoryId }),
})
)
);
}
// Invalider toutes les queries liées
invalidateAllTransactionQueries(queryClient);
invalidateAllCategoryQueries(queryClient);
setRuleDialogOpen(false);
},
[metadata, queryClient]
);
return {
ruleDialogOpen,
setRuleDialogOpen,
ruleGroup,
handleCreateRule,
handleSaveRule,
};
}