diff --git a/web/src/locales/de.json b/web/src/locales/de.json index 4c179b2..e5586be 100644 --- a/web/src/locales/de.json +++ b/web/src/locales/de.json @@ -440,7 +440,6 @@ "modeDebitCredit": "Getrennte Soll-/Haben-Spalten", "modeAbsExpense": "Ein Betrag (immer Ausgabe)", "readFile": "Datei einlesen", - "makePreview": "Vorschau erstellen", "saveTemplate": "Als Vorlage speichern", "templateName": "Vorlagenname", "colDate": "Datum", @@ -452,7 +451,6 @@ "colIban": "Gegen-IBAN", "makePreviewHint": "Ordne mindestens Datum und Betrag zu.", "rawHint": "Nur die ersten Zeilen – sie dienen nur der Spaltenzuordnung.", - "makePreviewNote": "„Vorschau erstellen“ zeigt danach ALLE Buchungen zum Prüfen und Buchen.", "summary": "{{total}} Zeilen erkannt · {{n}} werden gebucht (Duplikate/ungültige ausgenommen).", "setTypeForAll": "Alle gewählten Zeilen:", "flipAll": "Richtung umdrehen", @@ -510,7 +508,9 @@ "detectedBank": "Format erkannt: {{name}}", "detectedGeneric": "Format aus der Datei abgeleitet", "detectFailed": "Format nicht erkannt", - "detectUncertain": "Nicht alles war eindeutig – wirf einen Blick auf die Spaltenzuordnung unten, bevor du die Vorschau erzeugst." + "detectUncertain": "Nicht alles war eindeutig – wirf einen Blick auf die Spaltenzuordnung unten, bevor du die Vorschau erzeugst.", + "showAllSettings": "Alle Einstellungen anzeigen", + "hideAllSettings": "Einstellungen ausblenden" }, "password": { "show": "Passwort anzeigen", diff --git a/web/src/locales/en.json b/web/src/locales/en.json index 9bbc145..68d4df0 100644 --- a/web/src/locales/en.json +++ b/web/src/locales/en.json @@ -440,7 +440,6 @@ "modeDebitCredit": "Separate debit/credit columns", "modeAbsExpense": "Single amount (always expense)", "readFile": "Read file", - "makePreview": "Build preview", "saveTemplate": "Save as template", "templateName": "Template name", "colDate": "Date", @@ -452,7 +451,6 @@ "colIban": "Counterparty IBAN", "makePreviewHint": "Map at least date and amount.", "rawHint": "Only the first rows – just for mapping the columns.", - "makePreviewNote": "“Build preview” then shows ALL transactions to review and import.", "summary": "{{total}} rows detected · {{n}} will be imported (duplicates/invalid excluded).", "setTypeForAll": "All selected rows:", "flipAll": "Flip direction", @@ -510,7 +508,9 @@ "detectedBank": "Format detected: {{name}}", "detectedGeneric": "Format derived from the file", "detectFailed": "Format not recognised", - "detectUncertain": "Not everything was clear – check the column mapping below before building the preview." + "detectUncertain": "Not everything was clear – check the column mapping below before building the preview.", + "showAllSettings": "Show all settings", + "hideAllSettings": "Hide settings" }, "password": { "show": "Show password", diff --git a/web/src/pages/Import.jsx b/web/src/pages/Import.jsx index 1c0d846..fc610da 100644 --- a/web/src/pages/Import.jsx +++ b/web/src/pages/Import.jsx @@ -77,6 +77,8 @@ export default function Import() { const [erkannt, setErkannt] = useState(null); // 0 = noch nichts gebucht, also auch nichts zu zeigen. const [uebertragsLauf, setUebertragsLauf] = useState(0); + // Hat der Nutzer die vollstaendige Ansicht ausdruecklich geoeffnet? + const [alleEinstellungen, setAlleEinstellungen] = useState(false); const [quelle, setQuelle] = useState(""); // "preset:i" | "tpl:id" | "" const [rows, setRows] = useState(null); const [recon, setRecon] = useState(null); @@ -106,6 +108,27 @@ export default function Import() { // eslint-disable-next-line react-hooks/exhaustive-deps }, [file, mode, config.delimiter, config.encoding, config.has_header, config.skip_rows]); + // Auch die Pruefansicht laeuft von selbst, sobald Datum und Betrag zugeordnet + // sind. Der Knopf „Vorschau erstellen" war eine Pflichtuebung: Er tat genau + // das, was ohnehin als naechstes kommen musste. + // + // Entprellt, weil jede Aenderung an „Zeilen ueberspringen" sonst eine + // Anfrage samt Dateiuebertragung ausloest. + const vorschauBereit = mode === "csv" && !!file && !!accountId && !!raw + && config.column_map.date != null + && (config.amount_mode === "debit_credit" + ? (config.column_map.debit != null || config.column_map.credit != null) + : config.column_map.amount != null); + + useEffect(() => { + if (!vorschauBereit) return; + const id = setTimeout(() => { loadPreview(); }, 500); + return () => clearTimeout(id); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [vorschauBereit, raw, accountId, config.decimal_sep, config.date_format, + config.amount_mode, config.filter_column, config.filter_value, + JSON.stringify(config.column_map)]); + function setCfg(patch) { setConfig((c) => ({ ...c, ...patch })); } function setCol(field, value) { setConfig((c) => ({ ...c, column_map: { ...c.column_map, [field]: value === "" ? null : Number(value) } })); @@ -219,6 +242,9 @@ export default function Import() { n: batch.imported_count, u: batch.uebertraege_verknuepft }) : t("import.imported", { n: batch.imported_count })); setRows(null); setRecon(null); setRaw(null); setFile(null); setDocText(""); setDocPages([]); + // Auch die Erkennungs-Meldung: Sie bezoege sich sonst auf eine Datei, + // die nicht mehr gewaehlt ist. + setErkannt(null); setAlleEinstellungen(false); if (fileRef.current) fileRef.current.value = ""; loadStamm(); // Uebertraege sind nach einem Import am wahrscheinlichsten – eine @@ -406,6 +432,11 @@ export default function Import() { const acc = accountById[Number(accountId)]; + // Sicher erkannt heisst: Der Nutzer muss nichts einstellen. Dann stehen + // Format und Spaltenzuordnung nicht offen im Weg – erreichbar bleiben sie. + const sicherErkannt = !!erkannt?.ok && erkannt.sicherheit === "hoch"; + const zeigeEinstellungen = !sicherErkannt || alleEinstellungen; + const reviewColumns = useMemo(() => [ { key: "include", header: "", width: 42, fixed: true, @@ -592,6 +623,7 @@ export default function Import() { onChange={(e) => { const f = e.target.files?.[0] || null; setFile(f); setRaw(null); setRows(null); setDocText(""); setDocPages([]); + setAlleEinstellungen(false); if (f) formatErkennen(f); }} /> @@ -604,6 +636,43 @@ export default function Import() {

{t("import.docHint")}

)} + {/* Was erkannt wurde, gehoert hierher – direkt unter die Datei, auf + die es sich bezieht. Vorher stand es weit unten zwischen den + Spaltenfeldern, wo es niemand suchte. */} + {mode === "csv" && erkannt && ( +
+ + {erkannt.ok + ? (erkannt.preset + ? t("import.detectedBank", { name: erkannt.preset }) + : t("import.detectedGeneric")) + : t("import.detectFailed")} + + {/* Im Normalfall genuegt „erkannt". Wer wissen will, WAS erkannt + wurde und warum es so aussieht, wie es aussieht, klappt auf – + und findet dort auch jedes Feld zum Aendern. */} + {(!sicherErkannt || alleEinstellungen) && ( +

+ {erkannt.begruendung || erkannt.grund} +

+ )} + {erkannt.ok && erkannt.sicherheit !== "hoch" && ( +

+ {t("import.detectUncertain")} +

+ )} + {sicherErkannt && ( + + )} +
+ )}
- {/* Schritt 2: Format (nur CSV) */} - {mode === "csv" && ( + {/* Schritt 2: Format (nur CSV) – eingeklappt, solange nichts einzustellen + ist. Erreichbar ueber „Alle Einstellungen anzeigen" in Schritt 1. */} + {mode === "csv" && zeigeEinstellungen && (

{t("import.step2")}

@@ -704,9 +774,13 @@ export default function Import() {
)} - {/* Schritt 3: Spaltenzuordnung */} - {raw && ( + {/* Schritt 3: Spaltenzuordnung. + Die Karte bleibt stehen, wenn ein Zeilenfilter noetig sein koennte – + der darf NIE mit eingeklappt werden: Revolut legt alle Pockets in + eine Datei, und ohne Filter zaehlen die Bewegungen doppelt. */} + {raw && (zeigeEinstellungen || filterSpalten.length > 0) && (
+ {zeigeEinstellungen && (<>

{t("import.step3")}

{t("import.rawHint")}

@@ -741,6 +815,8 @@ export default function Import() { ))}
+ )} + {/* Revolut legt alle Produkte in EINE Datei. Ohne Filter zaehlen die Bewegungen doppelt – deshalb steht der Filter offen, sobald die Datei eine Spalte mit wenigen wiederkehrenden Werten hat. */} @@ -777,6 +853,7 @@ export default function Import() {
)} + {zeigeEinstellungen && (<>
{t("import.moreColumns")}

{t("import.ibanHint")}

@@ -800,37 +877,24 @@ export default function Import() {

{t("import.extraColsHint")}

- {erkannt && ( -
- - {erkannt.ok - ? (erkannt.preset - ? t("import.detectedBank", { name: erkannt.preset }) - : t("import.detectedGeneric")) - : t("import.detectFailed")} - -

- {erkannt.begruendung || erkannt.grund} -

- {erkannt.ok && erkannt.sicherheit !== "hoch" && ( -

- {t("import.detectUncertain")} -

- )} -
- )} + {/* Eine Vorlage zu speichern ist Pflege, nicht der naechste Schritt. + Als primaere Aktion stand sie neben „Vorschau erstellen" und sah + aus wie eine Entscheidung, die jetzt ansteht. */} + {/* Ohne den Knopf „Vorschau erstellen" muss gesagt werden, warum + noch nichts kommt. Sonst wartet man auf etwas, das nie laeuft. */} + {!vorschauBereit && ( +

+ {t("import.makePreviewHint")} +

+ )} -
- -
- setSaveName(e.target.value)} /> - -
+
+ setSaveName(e.target.value)} /> +
-

{t("import.makePreviewNote")}

+ )}
)}