feat: 统一管理端多语言、配置与票据/结算页面重构
This commit is contained in:
@@ -218,11 +218,11 @@ export function RebateConfigDocScreen() {
|
||||
setP2(inferPercentFrom(2, rows, types));
|
||||
setP3(inferPercentFrom(3, rows, types));
|
||||
setP4(inferPercentFrom(4, rows, types));
|
||||
toast.success("Published odds version with rebate");
|
||||
toast.success(t("rebate.publishSuccess", { ns: "config" }));
|
||||
void refreshList();
|
||||
setSelectedId(String(d.id));
|
||||
} catch (e) {
|
||||
toast.error(e instanceof LotteryApiBizError ? e.message : "Publish failed");
|
||||
toast.error(e instanceof LotteryApiBizError ? e.message : t("rebate.publishFailed", { ns: "config" }));
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
@@ -236,7 +236,7 @@ export function RebateConfigDocScreen() {
|
||||
reason: `rebate draft ${new Date().toISOString()}`,
|
||||
clone_from_version_id: active?.id ?? null,
|
||||
});
|
||||
toast.success(`Created draft v${d.version_no}`);
|
||||
toast.success(t("rebate.createDraftSuccess", { ns: "config", version: d.version_no }));
|
||||
await refreshList();
|
||||
setSelectedId(String(d.id));
|
||||
const rows = d.items.map((it) => ({ ...it }));
|
||||
@@ -246,7 +246,7 @@ export function RebateConfigDocScreen() {
|
||||
setP3(inferPercentFrom(3, rows, types));
|
||||
setP4(inferPercentFrom(4, rows, types));
|
||||
} catch (e) {
|
||||
toast.error(e instanceof LotteryApiBizError ? e.message : "Create draft failed");
|
||||
toast.error(e instanceof LotteryApiBizError ? e.message : t("rebate.createDraftFailed", { ns: "config" }));
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
@@ -260,7 +260,7 @@ export function RebateConfigDocScreen() {
|
||||
toast.success(t("versionSwitcher.delete", { ns: "config" }));
|
||||
await refreshList();
|
||||
} catch (e) {
|
||||
toast.error(e instanceof LotteryApiBizError ? e.message : "Delete failed");
|
||||
toast.error(e instanceof LotteryApiBizError ? e.message : t("rebate.deleteFailed", { ns: "config" }));
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
@@ -278,7 +278,7 @@ export function RebateConfigDocScreen() {
|
||||
onSelectedIdChange={setSelectedId}
|
||||
loading={loading}
|
||||
sheetTitle={`${t("nav.items.rebate", { ns: "config" })} ${t("versionSwitcher.sheetTitle", { ns: "config" })}`}
|
||||
sheetDescription="Rebate is stored in the odds draft version and shares the same version set as odds."
|
||||
sheetDescription={t("rebate.sheetDescription", { ns: "config" })}
|
||||
onDeleteVersion={handleDeleteVersion}
|
||||
className="w-auto min-w-0"
|
||||
/>
|
||||
@@ -288,7 +288,7 @@ export function RebateConfigDocScreen() {
|
||||
loadingList={loading}
|
||||
loadingDetail={loadingDetail}
|
||||
saving={saving}
|
||||
publishLabel="Publish"
|
||||
publishLabel={t("rebate.publishLabel", { ns: "config" })}
|
||||
onRefresh={() => void refreshList()}
|
||||
onNewDraft={() => void handleNewDraft()}
|
||||
onSaveDraft={() => void handleSave()}
|
||||
@@ -297,9 +297,18 @@ export function RebateConfigDocScreen() {
|
||||
|
||||
{detail ? (
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Editing version v{detail.version_no} · {detail.status === "draft" ? "Draft" : detail.status === "active" ? "Active" : "Archived"}
|
||||
{t("rebate.editingVersion", {
|
||||
ns: "config",
|
||||
version: detail.version_no,
|
||||
status:
|
||||
detail.status === "draft"
|
||||
? t("versionStatus.draft", { ns: "config" })
|
||||
: detail.status === "active"
|
||||
? t("versionStatus.active", { ns: "config" })
|
||||
: t("versionStatus.archived", { ns: "config" }),
|
||||
})}
|
||||
{!isDraft ? (
|
||||
<span className="text-amber-600 dark:text-amber-400"> - Create a draft before editing rebate.</span>
|
||||
<span className="text-amber-600 dark:text-amber-400"> - {t("rebate.readOnlyHint", { ns: "config" })}</span>
|
||||
) : null}
|
||||
</p>
|
||||
) : null}
|
||||
@@ -307,7 +316,7 @@ export function RebateConfigDocScreen() {
|
||||
|
||||
<div className="grid gap-4 sm:grid-cols-3">
|
||||
<div className="grid gap-2">
|
||||
<Label>2D Rebate Rate (%)</Label>
|
||||
<Label>{t("rebate.fields.d2", { ns: "config" })}</Label>
|
||||
{isDraft ? (
|
||||
<Input
|
||||
type="number"
|
||||
@@ -323,7 +332,7 @@ export function RebateConfigDocScreen() {
|
||||
)}
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<Label>3D Rebate Rate (%)</Label>
|
||||
<Label>{t("rebate.fields.d3", { ns: "config" })}</Label>
|
||||
{isDraft ? (
|
||||
<Input
|
||||
type="number"
|
||||
@@ -339,7 +348,7 @@ export function RebateConfigDocScreen() {
|
||||
)}
|
||||
</div>
|
||||
<div className="grid gap-2">
|
||||
<Label>4D Rebate Rate (%)</Label>
|
||||
<Label>{t("rebate.fields.d4", { ns: "config" })}</Label>
|
||||
{isDraft ? (
|
||||
<Input
|
||||
type="number"
|
||||
@@ -357,19 +366,25 @@ export function RebateConfigDocScreen() {
|
||||
</div>
|
||||
|
||||
<div className="flex items-start gap-3 rounded-lg border bg-muted/30 p-4">
|
||||
<Checkbox id="win-enjoy" checked aria-disabled disabled aria-label="Apply rebate on winning tickets" />
|
||||
<Checkbox
|
||||
id="win-enjoy"
|
||||
checked
|
||||
aria-disabled
|
||||
disabled
|
||||
aria-label={t("rebate.winEnjoy.label", { ns: "config" })}
|
||||
/>
|
||||
<div className="grid gap-1">
|
||||
<Label htmlFor="win-enjoy" className="font-medium leading-snug">
|
||||
Apply rebate on winning tickets
|
||||
{t("rebate.winEnjoy.label", { ns: "config" })}
|
||||
</Label>
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Placeholder field. It can later be aligned with risk and settlement rules and persisted.
|
||||
{t("rebate.winEnjoy.description", { ns: "config" })}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="grid gap-1 text-sm">
|
||||
<span className="text-muted-foreground">Effective Time (current active odds version)</span>
|
||||
<span className="text-muted-foreground">{t("rebate.effectiveTime", { ns: "config" })}</span>
|
||||
<span className="font-mono text-sm">
|
||||
{activeHead?.effective_at ? formatDt(activeHead.effective_at) : "—"}
|
||||
</span>
|
||||
|
||||
Reference in New Issue
Block a user