import { useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { ApiError, fetchTariff, publishTariffVersion, type TariffState, type TariffVersion } from "./api.js"; import { TariffEditorForm, emptyForm, formFromActive, formFromVersion, toStructure, type FormState } from "./TariffEditorForm.js"; // Tariff composer — the admin edits + publishes the LIVE rate card. Publishing // creates a new IMMUTABLE version (the active card); old versions are kept so past // sessions reprice correctly. A right sidebar lists the published history (named // since 2026-07-05); clicking a version loads it into the editor as the STARTING // POINT — publishing always creates a new version effective now, it never edits the // clicked one. The form machinery is shared with the Tariff Lab's draft modal — see // TariffEditorForm.tsx. To experiment without publishing, use the lab. See // wiki/concepts/tariff.md. export function TariffComposer() { const { t } = useTranslation(); const [state, setState] = useState(null); const [form, setForm] = useState(emptyForm); // Which published version the editor was last loaded from (sidebar highlight). const [loadedId, setLoadedId] = useState(null); // Optional label for the version about to be published. Deliberately NOT prefilled // from the active version — a tweaked card republished under last season's name // would mislabel the history. const [versionName, setVersionName] = useState(""); const [saving, setSaving] = useState(false); const [msg, setMsg] = useState<{ kind: "ok" | "err"; text: string } | null>(null); useEffect(() => { fetchTariff() .then((s) => { setState(s); setForm(formFromActive(s)); setLoadedId(s.active?.id ?? null); }) .catch((e) => setMsg({ kind: "err", text: (e as Error).message })); }, []); function loadVersion(v: TariffVersion) { setForm(formFromVersion(v.currency, v.structure)); setLoadedId(v.id); setMsg(null); } async function publish() { setSaving(true); setMsg(null); try { await publishTariffVersion({ currency: form.currency.trim().toUpperCase(), structure: toStructure(form), ...(versionName.trim() ? { name: versionName.trim() } : {}), }); const fresh = await fetchTariff(); setState(fresh); setLoadedId(fresh.active?.id ?? null); setVersionName(""); setMsg({ kind: "ok", text: t("tariff.publishedOk") }); } catch (e) { const text = e instanceof ApiError && e.problems?.length ? `${e.message}: ${e.problems.join("; ")}` : (e as Error).message; setMsg({ kind: "err", text }); } finally { setSaving(false); } } return (

{t("tariff.title")}

{!state?.active ? (

{t("tariff.noRateCard")}

) : (

{state.active.name ? `${state.active.name} — ` : ""} {t("tariff.activeSince", { date: new Date(state.active.effectiveFrom).toLocaleString(), count: state.versions.length, })}

)}
setVersionName(e.target.value)} placeholder={t("tariff.versionNamePh")} /> {msg && ( {msg.text} )}
{/* Published history — click a version to load it into the editor. Same list the lab's sidebar shows; here it seeds the next publish. */} {state && state.versions.length > 0 && ( )}
); }