feat(web): currency becomes a closed select (ALL / EUR / USD)
Build desktop / desktop (push) Successful in 4m13s
Build & push images / images (push) Successful in 2m51s
CI / check (push) Successful in 41s

Currency was free text in the tariff editor (composer page + lab draft
modal — shared form) and the subscription plan editor; a typo could
publish an unknown code onto immutable versions. Both now offer a closed
select from lib/currencies.ts. An out-of-set code already stored on an
old record is appended as an extra option so it displays + round-trips
unchanged. Blank tariff form defaults to ALL (was EUR) — the site's
actual currency.

Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
This commit is contained in:
2026-07-05 15:23:29 +02:00
parent 1de209be48
commit d5ff2097bd
3 changed files with 24 additions and 3 deletions
+7 -2
View File
@@ -1,4 +1,5 @@
import { useTranslation } from "react-i18next";
import { currencyOptions } from "./lib/currencies.js";
import {
isTariffV2,
type TariffBlock,
@@ -101,7 +102,7 @@ function emptyTier(): TierForm {
export function emptyForm(): FormState {
return {
currency: "EUR",
currency: "ALL",
gracePeriodEntryMin: "15",
incrementMin: "60",
lostTicket: "20.00",
@@ -339,7 +340,11 @@ export function TariffEditorForm({
<div>
<div className="card card-body grid grid-cols-[max-content_1fr] items-center gap-x-4 gap-y-2">
<label className="label">{t("tariff.currency")}</label>
<input className="input w-24" value={form.currency} onChange={(e) => set("currency", e.target.value)} maxLength={3} />
<select className="input w-24" value={form.currency} onChange={(e) => set("currency", e.target.value)}>
{currencyOptions(form.currency).map((c) => (
<option key={c} value={c}>{c}</option>
))}
</select>
<label className="label">{t("tariff.freeEntryGrace")}</label>
<input className="input w-32" value={form.gracePeriodEntryMin} onChange={(e) => set("gracePeriodEntryMin", e.target.value)} />
<label className="label">{t("tariff.billingIncrement")}</label>