feat(splits): save split as rule from split modal

- Checkbox in split modal: 'Also save as rule for <merchant>'
- Creates a rule with apply_split action storing the participant shares
- Rules engine now handles apply_split: deletes existing splits and re-applies
- Bulk split mode hides the checkbox (rule wouldn't make sense for ad-hoc bulk)
This commit is contained in:
2026-03-11 12:40:03 +11:00
parent a8743ba7df
commit af4c64bba7
4 changed files with 58 additions and 6 deletions
+3 -2
View File
@@ -232,7 +232,7 @@ function TransactionsContent() {
const [selected, setSelected] = useState<Set<number>>(new Set());
const [bulkCategory, setBulkCategory] = useState("");
const [bulkTagId, setBulkTagId] = useState("");
const [splitModal, setSplitModal] = useState<{ transactionId?: number; transactionIds?: number[]; amount?: number; description: string } | null>(null);
const [splitModal, setSplitModal] = useState<{ transactionId?: number; transactionIds?: number[]; amount?: number; description: string; merchant?: string } | null>(null);
const [rulePrompt, setRulePrompt] = useState<{
tx: { id: number; effective_merchant: string; description: string; bank_name: string };
field: "category" | "merchant";
@@ -543,7 +543,7 @@ function TransactionsContent() {
</td>
<td className="p-2">
<button
onClick={() => setSplitModal({ transactionId: t.id, amount: t.amount, description: t.description, transactionIds: undefined })}
onClick={() => setSplitModal({ transactionId: t.id, amount: t.amount, description: t.description, merchant: t.effective_merchant || undefined, transactionIds: undefined })}
className="text-xs text-zinc-500 hover:text-zinc-200 px-2 py-0.5 rounded hover:bg-zinc-800 transition-colors"
title="Split this transaction"
>
@@ -564,6 +564,7 @@ function TransactionsContent() {
transactionIds={splitModal.transactionIds}
amount={splitModal.amount}
description={splitModal.description}
merchant={splitModal.merchant}
onClose={() => { setSplitModal(null); if (splitModal.transactionIds) setSelected(new Set()); }}
/>
)}