useToast() liefert die Melde-Funktion selbst, kein Objekt. In drei Dateien aus
dieser Sitzung stand "const { toast } = useToast()", also undefined. Der Aufruf
steht fast immer in einem catch – dort verschluckt der TypeError die
eigentliche Meldung, und nach aussen passiert gar nichts.
Das erklaert beide Beschwerden:
- "Als Uebertrag buchen" schien wirkungslos: Die Buchung lief durch, dann warf
toast(), der catch warf beim Melden erneut, der Dialog blieb offen.
- "Braucht ein Reload": Im Eingang stand toast() VOR laden() – der Dialog
schloss noch, das Neuladen kam nie. Die Cache-Kopfzeile, die ich dafuer
gesetzt habe, war nicht die Ursache; richtig ist sie trotzdem.
Am Ende in der laufenden Oberflaeche nachgeklickt: Dialog schliesst, Meldung
erscheint, die Zeile wird ohne Neuladen zum Uebertrag, die Gegenseite liegt auf
dem Zielkonto, beide Salden stimmen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
100 lines
3.4 KiB
React
100 lines
3.4 KiB
React
import { useState } from "react";
|
||
import { useTranslation } from "react-i18next";
|
||
|
||
import { api } from "../api";
|
||
import AccountSelect from "./AccountSelect";
|
||
import Modal from "./Modal";
|
||
import Money from "./Money";
|
||
import { useToast } from "../toast";
|
||
import { useConfirm } from "../confirm";
|
||
import { formatDate } from "../format";
|
||
|
||
// Etwas als Kontoübertrag buchen. Zwei Aufrufer – die Arbeitsliste und die
|
||
// Buchungsliste –, deshalb eine Komponente: Die Nachfrage bei einem
|
||
// geschätzten Betrag ist zu leicht falsch nachgebaut.
|
||
//
|
||
// `vorgang` beschreibt, was umgewidmet wird:
|
||
// { art: "vorschlag" | "buchung", id, kontoId, betrag (vorzeichenbehaftet),
|
||
// waehrung, text, datum }
|
||
export default function UebertragDialog({
|
||
vorgang, accounts, accountById, baseCurrency, onClose, onFertig,
|
||
}) {
|
||
const { t } = useTranslation();
|
||
const toast = useToast();
|
||
const confirm = useConfirm();
|
||
const [ziel, setZiel] = useState("");
|
||
const [busy, setBusy] = useState(false);
|
||
|
||
async function buchen() {
|
||
if (!ziel) return;
|
||
const senden = (trotzdem) => (vorgang.art === "vorschlag"
|
||
? api.bookInboxAsTransfer(vorgang.id, Number(ziel), trotzdem)
|
||
: api.makeTransactionTransfer(vorgang.id, Number(ziel), trotzdem));
|
||
setBusy(true);
|
||
try {
|
||
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;
|
||
await senden(true);
|
||
}
|
||
toast(t("inbox.transferBooked"));
|
||
onClose();
|
||
await onFertig();
|
||
} catch (e) {
|
||
toast(e.message, "error");
|
||
} finally {
|
||
setBusy(false);
|
||
}
|
||
}
|
||
|
||
return (
|
||
<Modal
|
||
title={t("inbox.transferTitle")}
|
||
onClose={onClose}
|
||
footer={
|
||
<>
|
||
<button className="btn" onClick={onClose}>{t("common.cancel")}</button>
|
||
<button className="btn primary" disabled={!ziel || busy} onClick={buchen}>
|
||
{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>{vorgang.text || t("inbox.noText")}</strong></div>
|
||
<div className="named-sub">
|
||
{formatDate(vorgang.datum)} · {accountById[vorgang.kontoId]?.name || "—"}
|
||
</div>
|
||
</div>
|
||
<Money cents={Math.abs(vorgang.betrag)} currency={vorgang.waehrung}
|
||
sign={vorgang.betrag < 0 ? "neg" : "pos"} baseCurrency={baseCurrency} />
|
||
</div>
|
||
<label>
|
||
{/* Die Frage hängt an der Richtung – „wohin" bei einem Abgang,
|
||
„woher" bei einem Zugang. */}
|
||
<span>
|
||
{vorgang.betrag < 0
|
||
? t("inbox.transferTargetOut")
|
||
: t("inbox.transferTargetIn")}
|
||
</span>
|
||
<AccountSelect
|
||
accounts={accounts.filter((a) => a.id !== vorgang.kontoId)}
|
||
value={ziel}
|
||
onChange={(id) => setZiel(id ?? "")}
|
||
/>
|
||
</label>
|
||
</Modal>
|
||
);
|
||
}
|