import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { api } from "../api"; import Icon from "../components/Icon"; import Money from "../components/Money"; import { useToast } from "../toast"; import { formatDate } from "../format"; // Paare, die zusammengehören könnten: ein Abgang auf dem einen Konto, ein // Zugang auf dem anderen. Verbinden ändert KEINE Buchung – es setzt nur einen // Vermerk, dass die beiden dasselbe Geld sind. Beide bleiben mit Betrag, // Kategorie und Import-Schlüssel stehen; sie fallen nur aus der Auswertung. // Deshalb ist auch eine falsche Verknüpfung folgenlos: Lösen stellt alles her. export default function Uebertragsvorschlaege({ baseCurrency, onChange }) { const { t } = useTranslation(); const toast = useToast(); const [stand, setStand] = useState(null); // { verknuepft, vorschlaege } const [busy, setBusy] = useState(false); // Abgelehnte Vorschlaege. Ohne diese Liste war „Ignorieren" endgueltig: // Das Paar blieb bei jedem weiteren Lauf stumm, und nichts verriet, dass es // je einen Vorschlag gab – wer einmal falsch klickte, suchte danach einen // Fehler in der Erkennung, den es nicht gab. const [abgelehnt, setAbgelehnt] = useState([]); async function suchen(melden = false) { setBusy(true); try { const r = await api.searchTransfers(); setStand(r); if (melden) { toast(t("transfers.searched", { verknuepft: r.verknuepft, offen: r.vorschlaege.length, })); } if (r.verknuepft > 0) onChange?.(); } catch (e) { toast(e.message, "error"); } finally { setBusy(false); } } async function abgelehnteLaden() { try { setAbgelehnt(await api.ignoredTransfers()); } catch { /* still */ } } useEffect(() => { suchen(false); abgelehnteLaden(); }, []); async function zurueckholen(paar) { try { await api.unignoreTransfer(paar.a.id, paar.b.id); await abgelehnteLaden(); suchen(false); } catch (e) { toast(e.message, "error"); } } async function verbinden(paar) { try { await api.connectTransfer(paar.a.id, paar.b.id); onChange?.(); suchen(false); } catch (e) { toast(e.message, "error"); } } async function ignorieren(paar) { try { await api.ignoreTransfer(paar.a.id, paar.b.id); suchen(false); abgelehnteLaden(); } catch (e) { toast(e.message, "error"); } } // Vor dem ersten Ergebnis keine leere Karte zeigen. if (stand === null) return null; if (stand.vorschlaege.length === 0 && abgelehnt.length === 0) return null; return (

{t("transfers.title")}

{t("transfers.intro")}

{stand.vorschlaege.map((paar) => { const mitKategorie = paar.a.category_id != null || paar.b.category_id != null; return (
{/* Eine zugeordnete Buchung faellt durchs Verbinden aus dem Budget. Das soll niemanden ueberraschen. */} {mitKategorie && (

{t("transfers.hasCategory")}

)}
); })}
{/* Der Rueckweg. Eingeklappt, weil man ihn selten braucht – aber da, weil ein Klick sonst fuer immer gilt. */} {abgelehnt.length > 0 && (
{t("transfers.ignoredCount", { count: abgelehnt.length })}

{t("transfers.ignoredHint")}

{abgelehnt.map((paar) => (
))}
)}
); } function Seite({ seite, baseCurrency }) { return (
{formatDate(seite.date)} {" · "} {seite.account_name}
{seite.payee || seite.description || "—"}
); }