feat(admin, i18n): enhance reports, draws, config, and player workflows

This commit is contained in:
2026-06-08 17:41:55 +08:00
parent af982bb9f7
commit 7e65c53732
55 changed files with 1986 additions and 804 deletions

View File

@@ -1,151 +0,0 @@
"use client";
import { useCallback } from "react";
import { useTranslation } from "react-i18next";
import { AdminPageCard } from "@/components/admin/admin-page-card";
import { useConfirmAction } from "@/hooks/use-confirm-action";
import { SettingsSectionActions } from "@/modules/settings/components/settings-section-actions";
import { useSettingsSection } from "@/modules/settings/hooks/use-settings-section";
import { DRAW_KEYS } from "@/modules/settings/settings-keys";
import type { AdminSettingBatchItem } from "@/api/admin-settings";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
interface CurrencyFormatDraft {
currencyDisplayDecimals: string;
currencyDecimalSeparator: string;
currencyThousandsSeparator: string;
}
const INITIAL: CurrencyFormatDraft = {
currencyDisplayDecimals: "2",
currencyDecimalSeparator: ".",
currencyThousandsSeparator: ",",
};
function fromKv(kv: Record<string, unknown>): CurrencyFormatDraft {
return {
currencyDisplayDecimals: String(kv[DRAW_KEYS.CURRENCY_DISPLAY_DECIMALS] ?? 2),
currencyDecimalSeparator: String(kv[DRAW_KEYS.CURRENCY_DECIMAL_SEPARATOR] ?? "."),
currencyThousandsSeparator: String(kv[DRAW_KEYS.CURRENCY_THOUSANDS_SEPARATOR] ?? ","),
};
}
function buildDirtyItems(draft: CurrencyFormatDraft, saved: CurrencyFormatDraft): AdminSettingBatchItem[] {
const items: AdminSettingBatchItem[] = [];
if (draft.currencyDisplayDecimals !== saved.currencyDisplayDecimals) {
items.push({
key: DRAW_KEYS.CURRENCY_DISPLAY_DECIMALS,
value: Math.max(
0,
Math.min(12, Number.parseInt(draft.currencyDisplayDecimals || "2", 10) || 2),
),
});
}
if (draft.currencyDecimalSeparator !== saved.currencyDecimalSeparator) {
items.push({
key: DRAW_KEYS.CURRENCY_DECIMAL_SEPARATOR,
value: (draft.currencyDecimalSeparator || ".").slice(0, 1),
});
}
if (draft.currencyThousandsSeparator !== saved.currencyThousandsSeparator) {
items.push({
key: DRAW_KEYS.CURRENCY_THOUSANDS_SEPARATOR,
value: (draft.currencyThousandsSeparator || ",").slice(0, 1),
});
}
return items;
}
export function CurrencyFormatSettingsPanel() {
const { t } = useTranslation(["config", "adminUsers", "common"]);
const { request: requestConfirm, ConfirmDialog } = useConfirmAction();
const buildItems = useCallback(buildDirtyItems, []);
const section = useSettingsSection({
initialDraft: INITIAL,
fromKv,
buildDirtyItems: buildItems,
saveSuccessKey: "system.saveCurrencyFormatSuccess",
saveFailedKey: "system.saveFailed",
});
const { draft, loading, saving, dirty, updateField, discard, save } = section;
return (
<>
<AdminPageCard
title={t("system.sections.currencyFormat", { ns: "config" })}
description={t("system.sections.currencyFormatDescription", { ns: "config" })}
>
<div className="space-y-5">
<div className="grid grid-cols-1 gap-4 md:grid-cols-3">
<div className="grid gap-2">
<Label htmlFor="currency-display-decimals" className="text-sm font-medium">
{t("system.fields.currencyDisplayDecimals", { ns: "config" })}
</Label>
<Input
id="currency-display-decimals"
type="number"
min="0"
max="12"
step="1"
value={draft.currencyDisplayDecimals}
placeholder={t("system.placeholders.currencyDisplayDecimals", { ns: "config" })}
onChange={(e) => updateField("currencyDisplayDecimals", e.target.value)}
disabled={loading || saving}
/>
</div>
<div className="grid gap-2">
<Label htmlFor="currency-decimal-separator" className="text-sm font-medium">
{t("system.fields.currencyDecimalSeparator", { ns: "config" })}
</Label>
<Input
id="currency-decimal-separator"
value={draft.currencyDecimalSeparator}
placeholder={t("system.placeholders.currencyDecimalSeparator", { ns: "config" })}
onChange={(e) => updateField("currencyDecimalSeparator", e.target.value)}
disabled={loading || saving}
maxLength={1}
/>
</div>
<div className="grid gap-2">
<Label htmlFor="currency-thousands-separator" className="text-sm font-medium">
{t("system.fields.currencyThousandsSeparator", { ns: "config" })}
</Label>
<Input
id="currency-thousands-separator"
value={draft.currencyThousandsSeparator}
placeholder={t("system.placeholders.currencyThousandsSeparator", { ns: "config" })}
onChange={(e) => updateField("currencyThousandsSeparator", e.target.value)}
disabled={loading || saving}
maxLength={1}
/>
</div>
</div>
<SettingsSectionActions
dirty={dirty}
loading={loading}
saving={saving}
onSave={() =>
requestConfirm({
title: t("system.confirmSaveCurrencyFormatTitle", { ns: "config" }),
description: t("system.confirmSaveCurrencyFormatDescription", { ns: "config" }),
confirmLabel: t("confirm.confirmSave", { ns: "common" }),
onConfirm: () => {
void save();
},
})
}
onDiscard={discard}
saveLabel={t("actions.save", { ns: "adminUsers" })}
savingLabel={t("saving", { ns: "adminUsers" })}
discardLabel={t("system.discard", { ns: "config" })}
/>
</div>
</AdminPageCard>
<ConfirmDialog />
</>
);
}

View File

@@ -11,9 +11,6 @@ export const DRAW_KEYS = {
DRAW_BUFFER_DRAWS_AHEAD: "draw.buffer_draws_ahead",
REQUIRE_MANUAL_REVIEW: "draw.require_manual_review",
COOLDOWN_MINUTES: "draw.cooldown_minutes",
CURRENCY_DISPLAY_DECIMALS: "currency.display_decimals",
CURRENCY_DECIMAL_SEPARATOR: "currency.decimal_separator",
CURRENCY_THOUSANDS_SEPARATOR: "currency.thousands_separator",
} as const;
export const SETTLEMENT_KEYS = {

View File

@@ -3,7 +3,6 @@
import { WalletConfigDocScreen } from "@/modules/config/doc/wallet-config-doc-screen";
import { AdminPageCard } from "@/components/admin/admin-page-card";
import { AdminSettingsDataProvider } from "@/modules/settings/admin-settings-data-context";
import { CurrencyFormatSettingsPanel } from "@/modules/settings/panels/currency-format-settings-panel";
import { DrawSettingsPanel } from "@/modules/settings/panels/draw-settings-panel";
import { FrontendSettingsPanel } from "@/modules/settings/panels/frontend-settings-panel";
import { SettlementSettingsPanel } from "@/modules/settings/panels/settlement-settings-panel";
@@ -15,7 +14,6 @@ function SystemSettingsContent() {
return (
<div className="flex w-full max-w-none flex-col gap-6">
<DrawSettingsPanel />
<CurrencyFormatSettingsPanel />
<SettlementSettingsPanel />
<AdminPageCard