From a4da2d62df0d2b6c958f1bbfd497f4f32be07854 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonas=20Flod=C3=A9n?= Date: Thu, 25 Jun 2026 12:40:05 +0200 Subject: [PATCH] feat: add ignore menu item to transaction inbox card (#758) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Imported bank transactions can now be hidden from the inbox via a new "Ignorera transaktion" item in the ⋯ overflow menu. The backend (POST/DELETE /api/transactions/[id]/ignore) and the is_ignored DB column already existed; this wires up the missing UI affordance. - TransactionInboxCard: add onIgnore prop + EyeOff menu item (imported rows only — manually created rows use delete instead) - page.tsx: pass onIgnore={handleIgnoreTransaction} to the card; add handleBatchIgnore for the batch action bar's new "Ignorera" button - sv.json / en.json: add ignore_btn translation key to tx_inbox_card Signed-off-by: Jonas Flodén Co-authored-by: Claude Sonnet 4.6 --- app/(dashboard)/transactions/page.tsx | 72 ++++++++++++++++++- .../transactions/TransactionInboxCard.tsx | 44 ++++++++---- messages/en.json | 1 + messages/sv.json | 1 + 4 files changed, 103 insertions(+), 15 deletions(-) diff --git a/app/(dashboard)/transactions/page.tsx b/app/(dashboard)/transactions/page.tsx index 415b4cbd..4c0f0e67 100644 --- a/app/(dashboard)/transactions/page.tsx +++ b/app/(dashboard)/transactions/page.tsx @@ -21,7 +21,7 @@ import { DropdownMenuRadioGroup, DropdownMenuRadioItem, } from '@/components/ui/dropdown-menu' -import { ChevronDown, Layers, Search, Trash2, X } from 'lucide-react' +import { ChevronDown, EyeOff, Layers, Search, Trash2, X } from 'lucide-react' import TransactionForm from '@/components/transactions/TransactionForm' import BatchCategorySelector from '@/components/transactions/BatchCategorySelector' import TransactionStatusBar from '@/components/transactions/TransactionStatusBar' @@ -1667,6 +1667,67 @@ export default function TransactionsPage() { exitBatchMode() } + async function handleBatchIgnore() { + const ids = Array.from(selectedIds) + const ok = await confirm({ + title: `Ignorera ${ids.length} transaktioner?`, + description: 'Transaktionerna försvinner från listan utan att bokföras. Du kan återställa dem under Bankavstämning.', + confirmLabel: 'Ignorera', + cancelLabel: 'Avbryt', + variant: 'warning', + }) + if (!ok) return + + const ignoredIds = new Set() + setBatchProgress({ done: 0, total: ids.length }) + let successes = 0 + const failures: string[] = [] + for (let i = 0; i < ids.length; i++) { + try { + const res = await fetch(`/api/transactions/${ids[i]}/ignore`, { method: 'POST' }) + if (res.ok) { + successes++ + ignoredIds.add(ids[i]) + } else { + const tx = transactions.find((t) => t.id === ids[i]) + failures.push(tx?.description || ids[i]) + } + } catch { + failures.push(ids[i]) + } + setBatchProgress({ done: i + 1, total: ids.length }) + } + if (ignoredIds.size > 0) { + setExitingIds((prev) => { + const next = new Set(prev) + for (const id of ignoredIds) next.add(id) + return next + }) + setTotalUncategorizedCount((prev) => Math.max(0, (prev ?? ignoredIds.size) - ignoredIds.size)) + setTimeout(() => { + setTransactions((prev) => + prev.map((t) => (ignoredIds.has(t.id) ? { ...t, is_ignored: true } : t)) + ) + setExitingIds((prev) => { + const next = new Set(prev) + for (const id of ignoredIds) next.delete(id) + return next + }) + }, 350) + } + setBatchProgress(null) + if (failures.length === 0) { + toast({ title: 'Klart', description: `${successes} transaktioner ignorerade` }) + } else { + toast({ + title: 'Delvis klart', + description: `${successes} ignorerade, ${failures.length} misslyckades`, + variant: 'destructive', + }) + } + exitBatchMode() + } + async function handleBatchCategorize(category: TransactionCategory, vatTreatment?: VatTreatment) { const ids = Array.from(selectedIds) setBatchProgress({ done: 0, total: ids.length }) @@ -2011,6 +2072,7 @@ export default function TransactionsPage() { onOpenAttachDocument={openAttachDocumentDialog} onOpenCategoryDialog={openCategoryDialog} onDelete={handleDeleteTransaction} + onIgnore={handleIgnoreTransaction} onEditTitle={openEditTitleDialog} onToggleSelect={toggleBatchSelect} /> @@ -2065,6 +2127,14 @@ export default function TransactionsPage() { Avmarkera +