refactor: streamline transaction page logic by consolidating state management and enhancing pagination, improving overall performance and maintainability
This commit is contained in:
113
hooks/use-transaction-rules.ts
Normal file
113
hooks/use-transaction-rules.ts
Normal file
@@ -0,0 +1,113 @@
|
||||
"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";
|
||||
|
||||
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 }),
|
||||
})
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
// Invalidate queries
|
||||
queryClient.invalidateQueries({ queryKey: ["transactions"] });
|
||||
queryClient.invalidateQueries({ queryKey: ["banking-metadata"] });
|
||||
setRuleDialogOpen(false);
|
||||
},
|
||||
[metadata, queryClient]
|
||||
);
|
||||
|
||||
return {
|
||||
ruleDialogOpen,
|
||||
setRuleDialogOpen,
|
||||
ruleGroup,
|
||||
handleCreateRule,
|
||||
handleSaveRule,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user