import { useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { api } from "../api";
import { useConfirm } from "../confirm";
import { useToast } from "../toast";
import Icon from "../components/Icon";
import IconChip from "../components/IconChip";
import Modal from "../components/Modal";
import IconPicker, { CATEGORY_ICONS } from "../components/IconPicker";
import { centsToInput, parseAmountToCents } from "../format";
// Popup zum Pflegen der Schlagwörter einer Kategorie. Wörter werden beim Import
// gegen Empfänger/Verwendungszweck geprüft und ordnen Buchungen automatisch zu.
// Optional lässt sich ein fester Betrag als Zusatzbedingung angeben.
//
// Bearbeitet wird in derselben Zeile, in der auch neue Wörter entstehen: ein
// Doppelklick auf ein Schlagwort lädt Wort und Betrag dorthin. Vorher wurde im
// Chip selbst getippt – dort war für den Betrag kein Platz, er liess sich also
// gar nicht ändern.
function KeywordModal({ category, onClose }) {
const { t } = useTranslation();
const toast = useToast();
const [words, setWords] = useState([]);
const [input, setInput] = useState("");
const [amount, setAmount] = useState("");
const [busy, setBusy] = useState(false);
// Gesetzt, solange ein vorhandenes Schlagwort in der Zeile liegt.
const [editId, setEditId] = useState(null);
const wortFeld = useRef(null);
async function laden() {
try { setWords(await api.listKeywords(category.id)); }
catch (err) { toast(err.message, "error"); }
}
useEffect(() => { laden(); }, [category.id]);
function zeileLeeren() {
setEditId(null);
setInput("");
setAmount("");
}
async function speichern() {
const w = input.trim();
if (!w) return;
const c = parseAmountToCents(amount);
setBusy(true);
try {
if (editId) {
// Betrag ausdrücklich mitschicken – auch als null. Der Server lässt ihn
// sonst stehen, und ein geleertes Feld hätte keine Wirkung.
await api.updateKeyword(editId, { word: w, amount_cents: c || null });
} else {
const body = { word: w };
if (c) body.amount_cents = c;
await api.addKeyword(category.id, body);
}
zeileLeeren();
// Der Server normalisiert das Wort, deshalb neu laden statt lokal zu raten.
await laden();
} catch (err) {
toast(err.message, "error");
} finally {
setBusy(false);
}
}
async function entfernen(id) {
if (id === editId) zeileLeeren();
try {
await api.deleteKeyword(id);
setWords((ws) => ws.filter((x) => x.id !== id));
} catch (err) {
toast(err.message, "error");
}
}
function starteBearbeiten(w) {
setEditId(w.id);
setInput(w.word);
setAmount(centsToInput(w.amount_cents));
// Wort markiert vorlegen: überschreiben ist der häufigere Fall.
requestAnimationFrame(() => {
wortFeld.current?.focus();
wortFeld.current?.select();
});
}
function beiTaste(e) {
if (e.key === "Enter") { e.preventDefault(); speichern(); }
else if (e.key === "Escape" && editId) { e.preventDefault(); zeileLeeren(); }
}
return (
{t("common.close")}}
>
{c.name}
{/* Belegte Kategorien heben sich ab: Akzentfarbe plus Anzahl.
Sonst sähe man der Liste nicht an, wo schon etwas hinterlegt ist. */}
{c.excluded_from_stats && (
{t("categories.excludedShort")}
)}
))}
{g.categories.length === 0 && (
{t("categories.emptyGroup")}
)}
))}
{groups.length === 0 &&
{t("categories.emptyGroup")}
}
{modal && (
setModal(null)}
footer={
<>
>
}
>
)}
{/* Beim Schliessen neu laden: im Popup kann sich die Anzahl der
Schlagwörter geändert haben, und die steht am Knopf. */}
{keywordCat && (
{ setKeywordCat(null); laden(); }}
/>
)}