From d8c15ca260f875b100d67aa77d776a8c92e05eda Mon Sep 17 00:00:00 2001 From: Guido Date: Fri, 21 Aug 2026 20:30:11 +0200 Subject: [PATCH] feat: Rueckgaengig gemachte Importe sammelweise entfernen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Die Aktionsspalte im Verlauf ist fest und war zu schmal fuer den beschrifteten Knopf. Ausserdem sammeln sich nach einer Testreihe schnell zwanzig rueckgaengig gemachte Eintraege – die einzeln wegzuklicken ist Fleissarbeit. Neben der Ueberschrift steht jetzt ein Knopf, der alle auf einmal entfernt; einzeln geht weiterhin. Co-Authored-By: Claude Opus 5 --- web/src/locales/de.json | 4 +++- web/src/locales/en.json | 4 +++- web/src/pages/Import.jsx | 28 ++++++++++++++++++++++++++-- 3 files changed, 32 insertions(+), 4 deletions(-) diff --git a/web/src/locales/de.json b/web/src/locales/de.json index d26d3f0..6a3d831 100644 --- a/web/src/locales/de.json +++ b/web/src/locales/de.json @@ -360,7 +360,9 @@ "removeEntryConfirm": "Den Verlaufs-Eintrag „{{file}}“ entfernen? Die Buchungen sind bereits zurückgenommen.", "reconMixedHint": "Enthält die Datei mehrere Konten oder Produkte? Revolut legt Giro, Erspartes und Anlagekonto in EINE Datei, jedes mit eigener Kontostand-Spalte. Dann greift der Abgleich erst, wenn du oben den Zeilenfilter setzt (z. B. Produkt = Giro) und die übrigen Produkte einzeln einliest.", "filterTitle": "Diese Datei enthält mehrere Konten", - "filterValueOption": "{{value}} ({{count}} Zeilen)" + "filterValueOption": "{{value}} ({{count}} Zeilen)", + "removeAllUndone": "Rückgängig gemachte entfernen", + "removeAllUndoneConfirm": "{{n}} rückgängig gemachte Einträge aus dem Verlauf entfernen? Die Buchungen sind bereits weg, es verschwindet nur der Eintrag." }, "password": { "show": "Passwort anzeigen", diff --git a/web/src/locales/en.json b/web/src/locales/en.json index c653268..6fb0b99 100644 --- a/web/src/locales/en.json +++ b/web/src/locales/en.json @@ -360,7 +360,9 @@ "removeEntryConfirm": "Remove the history entry “{{file}}”? Its transactions have already been undone.", "reconMixedHint": "Does the file hold several accounts or products? Revolut puts current, savings and investment into ONE file, each with its own balance column. The check only works once you set the row filter above (e.g. Product = Current) and import the other products separately.", "filterTitle": "This file holds several accounts", - "filterValueOption": "{{value}} ({{count}} rows)" + "filterValueOption": "{{value}} ({{count}} rows)", + "removeAllUndone": "Remove undone entries", + "removeAllUndoneConfirm": "Remove {{n}} undone entries from the history? The bookings are already gone; only the entry disappears." }, "password": { "show": "Show password", diff --git a/web/src/pages/Import.jsx b/web/src/pages/Import.jsx index ec29261..2cdf8be 100644 --- a/web/src/pages/Import.jsx +++ b/web/src/pages/Import.jsx @@ -206,6 +206,22 @@ export default function Import() { try { await api.removeImportBatch(b.id); loadStamm(); } catch (e) { toast(e.message, "error"); } } + // Nach einer Reihe von Versuchen stehen schnell zwanzig rueckgaengig gemachte + // Importe im Verlauf. Die einzeln wegzuklicken ist Fleissarbeit ohne Nutzen. + async function alleErledigtenEntfernen() { + const weg = batches.filter((b) => b.status === "undone"); + if (!weg.length) return; + if (!(await confirm({ + title: t("import.removeAllUndone"), + message: t("import.removeAllUndoneConfirm", { n: weg.length }), + danger: true, confirmLabel: t("common.delete"), + }))) return; + try { + for (const b of weg) await api.removeImportBatch(b.id); + loadStamm(); + } catch (e) { toast(e.message, "error"); } + } + async function undo(b) { if (!(await confirm({ title: t("import.undo"), message: t("import.undoConfirm", { file: b.filename }), danger: true, confirmLabel: t("import.undo") }))) return; try { await api.undoImportBatch(b.id); loadStamm(); } catch (e) { toast(e.message, "error"); } @@ -336,7 +352,7 @@ export default function Import() { render: (b) => {t("import.importedCount", { n: b.imported_count })}, }, { - key: "action", header: "", width: 168, fixed: true, + key: "action", header: "", width: 196, fixed: true, render: (b) => (b.status === "committed" ? ( + )} +