feat: Eine Arbeitsliste, und Kategorien direkt in der Buchungsliste
'Eingang' und 'Offene Buchungen' zeigten fast dasselbe. Seit angebundene Konten selbst buchen, ist eine Bank-Buchung sofort gebucht und ohne Kategorie – sie stand damit in BEIDEN Listen. Die Trennung stammte aus der Zeit, als der Eingang nur Vorschlaege enthielt. Jetzt eine Seite 'Zu erledigen' mit allem, was noch eine Hand braucht: offene Vorschlaege zum Buchen oder Uebertragen und Buchungen ohne Kategorie, egal ob von Bank, Mail, Kurzbefehl oder Datei. Aelteste zuerst, mit der Herkunft in der Zeile. Erledigtes verschwindet – die Eingangs-Eintraege bleiben in der Datenbank, weil der spaetere Import gegen sie abgleicht. /offen leitet um. Das Haekchen 'Schlagwort merken' aus der alten Seite bleibt: Es ist der Grund, warum der Stapel mit der Zeit kleiner wird, und der Eingang hatte es nie. Der Uebertrags-Knopf gilt jetzt auch fuer schon gebuchte Buchungen – sonst haette in einer gemeinsamen Liste die eine Haelfte der Zeilen ihn gehabt und die andere nicht. In der Buchungsliste steht die Kategorie jetzt als Auswahl in der Zeile, dort wo bisher nur ihr Name stand. Vorher musste man jede Buchung einzeln zum Bearbeiten oeffnen. Die Zeile aendert sich an Ort und Stelle, die Bildlaufposition bleibt. Ausserdem tragen API-Antworten jetzt 'Cache-Control: no-store'. Ohne das darf ein Browser selbst entscheiden, und der Zurueck-Knopf zeigt Salden von vorhin – bei Geld ist eine veraltete Anzeige schlimmer als eine langsame. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -81,6 +81,18 @@ export default function Transactions() {
|
||||
}
|
||||
}
|
||||
|
||||
async function kategorieSetzen(tx, categoryId) {
|
||||
if (categoryId == null) return;
|
||||
try {
|
||||
const neu = await api.updateTransaction(tx.id, { category_id: Number(categoryId) });
|
||||
// In der Liste ersetzen statt alles neu zu laden: Die Zeile ändert sich
|
||||
// sofort, und die Bildlaufposition bleibt, wo sie war.
|
||||
setTxns((r) => r.map((x) => (x.id === tx.id ? neu : x)));
|
||||
} catch (err) {
|
||||
toast(err.message, "error");
|
||||
}
|
||||
}
|
||||
|
||||
async function loesen(tx) {
|
||||
const link = links[String(tx.id)];
|
||||
if (!link) return;
|
||||
@@ -106,6 +118,7 @@ export default function Transactions() {
|
||||
|
||||
// Standard-Kategorie für budgetierte Sparen-Transfers (falls vorhanden).
|
||||
const expenseGroups = useMemo(() => gruppenFuer(groups, "expense"), [groups]);
|
||||
const incomeGroups = useMemo(() => gruppenFuer(groups, "income"), [groups]);
|
||||
const sparenCatId = useMemo(() => {
|
||||
for (const g of expenseGroups)
|
||||
for (const c of g.categories) if (c.name === "Sparen & Investments") return String(c.id);
|
||||
@@ -242,7 +255,21 @@ export default function Transactions() {
|
||||
<IconChip name={l.icon} color={l.color} size={26} />
|
||||
<div className="named-text">
|
||||
<div className="ellipsis">{l.title}</div>
|
||||
{l.sub && <div className="named-sub ellipsis">{l.sub}</div>}
|
||||
{/* Die Kategorie direkt in der Zeile ändern – dafür musste man
|
||||
vorher jede Buchung einzeln zum Bearbeiten öffnen. Ein
|
||||
Übertrag bekommt keine, dort bleibt der Text stehen. */}
|
||||
{tx.type === "transfer" ? (
|
||||
l.sub && <div className="named-sub ellipsis">{l.sub}</div>
|
||||
) : (
|
||||
<div className="zeilen-kategorie">
|
||||
<CategorySelect
|
||||
groups={tx.type === "income" ? incomeGroups : expenseGroups}
|
||||
value={tx.category_id}
|
||||
onChange={(id) => kategorieSetzen(tx, id)}
|
||||
placeholder={t("transactions.uncategorized")}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
{/* Verknüpft heisst: zählt nicht als Ausgabe oder Einnahme.
|
||||
Ohne das Gegenkonto sagt „Übertrag" nichts. */}
|
||||
{links[String(tx.id)] && (
|
||||
@@ -294,7 +321,8 @@ export default function Transactions() {
|
||||
),
|
||||
},
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
], [accountById, categoryById, baseCurrency, formatDate, links, t]);
|
||||
], [accountById, categoryById, baseCurrency, expenseGroups, incomeGroups,
|
||||
formatDate, links, t]);
|
||||
|
||||
return (
|
||||
<div>
|
||||
|
||||
Reference in New Issue
Block a user