feat(rules): show what an apply run changed, and which rule ran
ci / lint-test (push) Successful in 38s
ci / lint-test (push) Successful in 38s
Apply History listed only counts - '13 matches · 13 transactions' - which reads identically whether the run renamed a merchant or split every transaction with another participant. Revert is destructive, so that is not enough to decide on. Two additions. Migration 0017 records rule_id, rule_name and source on each run: rule_name is denormalised so history stays readable after a rule is edited or deleted, and there is no FK so deleting a rule cannot cascade away the audit trail. Both write paths now populate it - the condition-matched run and the selection-based quick action. And rows expand to show the run's snapshot set against current values: which transactions were touched and what changed on each. Rows changed by something else since the run are called out, because reverting restores the pre-run value and would discard that later edit. Runs recorded before this show 'Unknown rule' - the rule they came from is not recoverable.
This commit is contained in:
@@ -617,6 +617,38 @@ export interface RuleRun {
|
||||
matched: number;
|
||||
transactions_affected: number;
|
||||
reverted_at: string | null;
|
||||
// Provenance (migration 0017). NULL on runs recorded before it existed.
|
||||
rule_id: number | null;
|
||||
rule_name: string | null;
|
||||
source: "all" | "rule" | "selection" | null;
|
||||
}
|
||||
|
||||
export interface RuleRunDetail {
|
||||
run: RuleRun & { transactions_affected: number };
|
||||
transactions: {
|
||||
id: number;
|
||||
transaction_date: string;
|
||||
description: string;
|
||||
amount: number;
|
||||
amount_aud: number | null;
|
||||
bank_name: string;
|
||||
merchant: string | null;
|
||||
changes: { field: string; from: string | null; to: string | null }[];
|
||||
}[];
|
||||
still_changed: number;
|
||||
}
|
||||
|
||||
/** What a run actually did — loaded on demand when a row is expanded. */
|
||||
export function useRuleRunDetail(runId: number | null) {
|
||||
return useQuery<RuleRunDetail>({
|
||||
queryKey: ["rule-run-detail", runId],
|
||||
enabled: runId != null,
|
||||
queryFn: async () => {
|
||||
const res = await fetch(`/api/rules/runs/${runId}`);
|
||||
if (!res.ok) throw new Error("Failed to load run detail");
|
||||
return res.json();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
export function useRuleRuns() {
|
||||
|
||||
Reference in New Issue
Block a user