fix: Tabelle passt wieder, und Uebertraege gehen auch aus der Buchungsliste

Die neue Kategoriespalte hatte 230 px Mindestbreite hinzugefuegt. Damit war die
Tabelle in einem schmalen Fenster breiter als ihre Karte und scrollte waagerecht
- von aussen sah das aus, als waere sie abgeschnitten. Alle Spalten sind jetzt
enger bemessen; bei 900 px Fensterbreite passt sie ohne Scrollen.

Ausserdem liess sich eine Buchung nur aus der Arbeitsliste zum Uebertrag
machen, nicht aus der Buchungsliste. Dort steht der Knopf jetzt auch - und zwar
statt des Loesen-Knopfes, denn beides zugleich gibt es nie: Eine verknuepfte
Buchung IST schon ein Paar.

Der Dialog dafuer ist eine eigene Komponente geworden. Die Nachfrage bei einem
geschaetzten Betrag - 409 abfangen, begruenden lassen, mit 'trotzdem' erneut
senden - ist zu leicht falsch nachgebaut, und jetzt gibt es sie nur einmal.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Guido
2026-08-27 11:01:47 +02:00
co-authored by Claude Opus 5
parent 654c53e3f5
commit 64b32cf8b8
3 changed files with 143 additions and 82 deletions
+9 -74
View File
@@ -2,12 +2,11 @@ import { useEffect, useMemo, useState } from "react";
import { useTranslation } from "react-i18next";
import { api } from "../api";
import AccountSelect from "../components/AccountSelect";
import CategorySelect from "../components/CategorySelect";
import Icon from "../components/Icon";
import IconChip from "../components/IconChip";
import Modal from "../components/Modal";
import Money from "../components/Money";
import UebertragDialog from "../components/UebertragDialog";
import Uebertragsvorschlaege from "../components/Uebertragsvorschlaege";
import { useToast } from "../toast";
import { useConfirm } from "../confirm";
@@ -185,38 +184,9 @@ export default function Eingang() {
waehrung: accountById[aufgabe.tx.account_id]?.currency,
text: aufgabe.tx.payee || aufgabe.tx.description, datum: aufgabe.tx.date,
};
setUebertrag({ ...s, ziel: "" });
setUebertrag(s);
}
async function uebertragBuchen() {
const { art, id, ziel } = uebertrag;
if (!ziel) return;
const senden = (trotzdem) => (art === "vorschlag"
? api.bookInboxAsTransfer(id, Number(ziel), trotzdem)
: api.makeTransactionTransfer(id, Number(ziel), trotzdem));
try {
await senden(false);
} catch (e) {
// 409 heisst nachfragen: Der Betrag der Gegenseite ist geschätzt und das
// Zielkonto meldet den Zugang selbst. Die Begründung kommt vom Server –
// dort steht das Wissen, nicht hier.
if (e.status !== 409) { toast(e.message, "error"); return; }
if (!(await confirm({
title: t("inbox.transferTitle"),
message: e.message,
confirmLabel: t("inbox.asTransfer"),
}))) return;
try {
await senden(true);
} catch (zweiter) {
toast(zweiter.message, "error");
return;
}
}
setUebertrag(null);
toast(t("inbox.transferBooked"));
await laden();
}
if (inbox === null) {
return <div className="card"><div className="empty-state">…</div></div>;
@@ -277,49 +247,14 @@ export default function Eingang() {
)}
{uebertrag && (
<Modal
title={t("inbox.transferTitle")}
<UebertragDialog
vorgang={uebertrag}
accounts={accounts}
accountById={accountById}
baseCurrency={baseCurrency}
onClose={() => setUebertrag(null)}
footer={
<>
<button className="btn" onClick={() => setUebertrag(null)}>
{t("common.cancel")}
</button>
<button className="btn primary" disabled={!uebertrag.ziel}
onClick={uebertragBuchen}>
{t("inbox.asTransfer")}
</button>
</>
}
>
<p className="muted small">{t("inbox.transferIntro")}</p>
<div className="account-row" style={{ borderBottom: "none" }}>
<div className="named-text grow">
<div><strong>{uebertrag.text || t("inbox.noText")}</strong></div>
<div className="named-sub">
{formatDate(uebertrag.datum)} ·{" "}
{accountById[uebertrag.kontoId]?.name || "—"}
</div>
</div>
<Money cents={Math.abs(uebertrag.betrag)} currency={uebertrag.waehrung}
sign={uebertrag.betrag < 0 ? "neg" : "pos"}
baseCurrency={baseCurrency} />
</div>
<label>
{/* Die Frage hängt an der Richtung – „wohin" bei einem Abgang,
„woher" bei einem Zugang. */}
<span>
{uebertrag.betrag < 0
? t("inbox.transferTargetOut")
: t("inbox.transferTargetIn")}
</span>
<AccountSelect
accounts={accounts.filter((a) => a.id !== uebertrag.kontoId)}
value={uebertrag.ziel}
onChange={(id) => setUebertrag((u) => ({ ...u, ziel: id ?? "" }))}
/>
</label>
</Modal>
onFertig={laden}
/>
)}
</div>
);