139 lines
4.8 KiB
TypeScript
139 lines
4.8 KiB
TypeScript
import { decrypt, encrypt, importKey } from '@/lib/crypto';
|
|
import { getStoredKey } from '@/lib/key-storage';
|
|
import { evaluateRules } from '@/lib/rule-engine';
|
|
import { appendNoteIfNotPresent } from '@/lib/utils';
|
|
import { transactionSyncService } from '@/services/transaction-sync';
|
|
import type { Account, Bank } from '@/types/account';
|
|
import type { AutomationRule } from '@/types/automation-rule';
|
|
import type { Category } from '@/types/category';
|
|
import type { DecryptedTransaction } from '@/types/transaction';
|
|
import { useCallback } from 'react';
|
|
import { toast } from 'sonner';
|
|
|
|
interface ReEvaluateAllOptions {
|
|
onProgress?: (progress: {
|
|
current: number;
|
|
total: number;
|
|
transactionId: string;
|
|
description: string;
|
|
}) => void;
|
|
}
|
|
|
|
export function useReEvaluateAllTransactions() {
|
|
const reEvaluateAll = useCallback(
|
|
async (
|
|
transactions: DecryptedTransaction[],
|
|
categories: Category[],
|
|
accounts: Account[],
|
|
banks: Bank[],
|
|
automationRules: AutomationRule[],
|
|
options?: ReEvaluateAllOptions,
|
|
) => {
|
|
if (!transactions.length) {
|
|
toast.error('No transactions to re-evaluate');
|
|
return;
|
|
}
|
|
|
|
const keyString = getStoredKey();
|
|
if (!keyString) {
|
|
toast.error('Please unlock your encryption key');
|
|
return;
|
|
}
|
|
|
|
const key = await importKey(keyString);
|
|
|
|
if (!automationRules.length) {
|
|
toast.error('No automation rules found');
|
|
return;
|
|
}
|
|
|
|
const toastId = toast.loading(
|
|
`Re-evaluating 0 of ${transactions.length} transactions...`,
|
|
);
|
|
|
|
let successCount = 0;
|
|
|
|
try {
|
|
for (let i = 0; i < transactions.length; i++) {
|
|
const transaction = transactions[i];
|
|
const progress = i + 1;
|
|
|
|
options?.onProgress?.({
|
|
current: progress,
|
|
total: transactions.length,
|
|
transactionId: transaction.id,
|
|
description: transaction.decryptedDescription,
|
|
});
|
|
|
|
const result = await evaluateRules(
|
|
transaction,
|
|
automationRules,
|
|
categories,
|
|
accounts,
|
|
banks,
|
|
key,
|
|
);
|
|
|
|
if (result) {
|
|
let finalNotes = transaction.notes;
|
|
let finalNotesIv = transaction.notes_iv;
|
|
|
|
if (result.note && result.noteIv) {
|
|
const decryptedRuleNote = await decrypt(
|
|
result.note,
|
|
key,
|
|
result.noteIv,
|
|
);
|
|
const combinedNote = appendNoteIfNotPresent(
|
|
transaction.decryptedNotes,
|
|
decryptedRuleNote,
|
|
);
|
|
|
|
if (combinedNote !== transaction.decryptedNotes) {
|
|
const encrypted = await encrypt(
|
|
combinedNote,
|
|
key,
|
|
);
|
|
finalNotes = encrypted.encrypted;
|
|
finalNotesIv = encrypted.iv;
|
|
}
|
|
}
|
|
|
|
await transactionSyncService.update(transaction.id, {
|
|
category_id: result.categoryId,
|
|
notes: finalNotes,
|
|
notes_iv: finalNotesIv,
|
|
});
|
|
|
|
successCount++;
|
|
}
|
|
|
|
toast.loading(
|
|
`Re-evaluating ${progress} of ${transactions.length} transactions...`,
|
|
{ id: toastId },
|
|
);
|
|
}
|
|
|
|
toast.dismiss(toastId);
|
|
toast.success(() => (
|
|
<div>
|
|
{`Re-evaluation complete!`}
|
|
<br />
|
|
{`${successCount} transaction(s) updated.`}
|
|
</div>
|
|
));
|
|
} catch (error) {
|
|
console.error('Failed to re-evaluate transactions:', error);
|
|
toast.error(
|
|
'Failed to re-evaluate transactions. Please try again.',
|
|
{ id: toastId },
|
|
);
|
|
throw error;
|
|
}
|
|
},
|
|
[],
|
|
);
|
|
|
|
return { reEvaluateAll };
|
|
}
|