refactor: 合并多语言支持的显示名称字段,优化奖池手动爆发功能的返回数据结构,增强管理端权限控制

This commit is contained in:
2026-05-25 14:31:24 +08:00
parent 7d01e5c47e
commit ddedef824e
101 changed files with 3033 additions and 641 deletions

View File

@@ -11,10 +11,10 @@ import {
postAdminCurrency,
putAdminCurrency,
} from "@/api/admin-currencies";
import { AdminPageCard } from "@/components/admin/admin-page-card";
import { AdminStatusBadge } from "@/components/admin/admin-status-badge";
import { Button } from "@/components/ui/button";
import { AdminTableExportButton } from "@/components/admin/admin-table-export-button";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
import { Checkbox } from "@/components/ui/checkbox";
import {
Dialog,
@@ -204,16 +204,21 @@ export function CurrencySettingsPanel() {
}
return (
<Card className="admin-list-card">
<CardHeader className="admin-list-header flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<CardTitle className="admin-list-title">{t("currencies.title", { ns: "config" })}</CardTitle>
<div className="flex items-center gap-2">
<AdminTableExportButton tableId="admin-currencies-table" filename={exportLabels.filename}
sheetName={exportLabels.sheetName} />
<Button onClick={openCreate}>{t("currencies.actions.create", { ns: "config" })}</Button>
</div>
</CardHeader>
<CardContent className="admin-list-content">
<>
<AdminPageCard
title={t("currencies.title", { ns: "config" })}
description={t("currencies.description", { ns: "config" })}
actions={
<>
<AdminTableExportButton
tableId="admin-currencies-table"
filename={exportLabels.filename}
sheetName={exportLabels.sheetName}
/>
<Button onClick={openCreate}>{t("currencies.actions.create", { ns: "config" })}</Button>
</>
}
>
<div className="admin-table-shell">
<Table id="admin-currencies-table">
<TableHeader>
@@ -277,7 +282,7 @@ export function CurrencySettingsPanel() {
</TableBody>
</Table>
</div>
</CardContent>
</AdminPageCard>
<Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
<DialogContent showCloseButton className="sm:max-w-md">
@@ -385,6 +390,6 @@ export function CurrencySettingsPanel() {
</div>
</DialogContent>
</Dialog>
</Card>
</>
);
}

View File

@@ -8,8 +8,11 @@ import {
getAdminSettings,
updateAdminSetting,
} from "@/api/admin-settings";
import { AdminPageCard } from "@/components/admin/admin-page-card";
import { useConfirmAction } from "@/hooks/use-confirm-action";
import { WalletConfigDocScreen } from "@/modules/config/doc/wallet-config-doc-screen";
import { Button } from "@/components/ui/button";
import { Card, CardContent } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Textarea } from "@/components/ui/textarea";
@@ -23,6 +26,8 @@ const DRAW_KEYS = {
REQUIRE_MANUAL_REVIEW: "draw.require_manual_review",
COOLDOWN_MINUTES: "draw.cooldown_minutes",
AUTO_SETTLEMENT: "settlement.auto_run_on_tick",
AUTO_APPROVE: "settlement.auto_approve_on_tick",
AUTO_PAYOUT: "settlement.auto_payout_on_tick",
} as const;
const FRONTEND_GROUP = "frontend";
@@ -37,6 +42,8 @@ interface RuntimeDraft {
requireManualReview: boolean;
cooldownMinutes: string;
autoSettlement: boolean;
autoApprove: boolean;
autoPayout: boolean;
playRulesHtmlZh: string;
playRulesHtmlEn: string;
playRulesHtmlNe: string;
@@ -116,10 +123,13 @@ function SaveActions({
export function SystemSettingsScreen() {
const { t } = useTranslation(["common", "config", "adminUsers"]);
const { request: requestConfirm, ConfirmDialog } = useConfirmAction();
const [draft, setDraft] = useState<RuntimeDraft>({
requireManualReview: false,
cooldownMinutes: "15",
autoSettlement: true,
autoApprove: true,
autoPayout: true,
playRulesHtmlZh: "",
playRulesHtmlEn: "",
playRulesHtmlNe: "",
@@ -128,6 +138,8 @@ export function SystemSettingsScreen() {
requireManualReview: false,
cooldownMinutes: "15",
autoSettlement: true,
autoApprove: true,
autoPayout: true,
playRulesHtmlZh: "",
playRulesHtmlEn: "",
playRulesHtmlNe: "",
@@ -155,6 +167,8 @@ export function SystemSettingsScreen() {
requireManualReview: Boolean(kv[DRAW_KEYS.REQUIRE_MANUAL_REVIEW] ?? false),
cooldownMinutes: String(kv[DRAW_KEYS.COOLDOWN_MINUTES] ?? 15),
autoSettlement: Boolean(kv[DRAW_KEYS.AUTO_SETTLEMENT] ?? true),
autoApprove: Boolean(kv[DRAW_KEYS.AUTO_APPROVE] ?? true),
autoPayout: Boolean(kv[DRAW_KEYS.AUTO_PAYOUT] ?? true),
playRulesHtmlZh: String(kv[FRONTEND_KEYS.PLAY_RULES_HTML_ZH] ?? legacyHtml),
playRulesHtmlEn: String(kv[FRONTEND_KEYS.PLAY_RULES_HTML_EN] ?? ""),
playRulesHtmlNe: String(kv[FRONTEND_KEYS.PLAY_RULES_HTML_NE] ?? ""),
@@ -189,6 +203,8 @@ export function SystemSettingsScreen() {
Math.max(0, Number.parseInt(draft.cooldownMinutes || "0", 10) || 0),
);
await updateAdminSetting(DRAW_KEYS.AUTO_SETTLEMENT, draft.autoSettlement);
await updateAdminSetting(DRAW_KEYS.AUTO_APPROVE, draft.autoApprove);
await updateAdminSetting(DRAW_KEYS.AUTO_PAYOUT, draft.autoPayout);
await updateAdminSetting(FRONTEND_KEYS.PLAY_RULES_HTML_ZH, draft.playRulesHtmlZh);
await updateAdminSetting(FRONTEND_KEYS.PLAY_RULES_HTML_EN, draft.playRulesHtmlEn);
await updateAdminSetting(FRONTEND_KEYS.PLAY_RULES_HTML_NE, draft.playRulesHtmlNe);
@@ -210,12 +226,11 @@ export function SystemSettingsScreen() {
const discardLabel = t("system.discard", { ns: "config" });
return (
<div className="flex flex-col gap-10">
<section className="space-y-4">
<h2 className="border-b border-border/60 pb-3 text-base font-semibold text-foreground">
{t("system.title", { ns: "config" })}
</h2>
<div className="flex w-full max-w-none flex-col gap-6">
<AdminPageCard
title={t("system.title", { ns: "config" })}
description={t("system.description", { ns: "config" })}
>
<div className="space-y-5">
<div className="flex flex-wrap items-center justify-between gap-3">
<Label className="text-sm font-medium">{t("system.fields.manualReview", { ns: "config" })}</Label>
@@ -243,6 +258,32 @@ export function SystemSettingsScreen() {
<div className="h-px bg-border/60" />
<div className="flex flex-wrap items-center justify-between gap-3">
<Label className="text-sm font-medium">{t("system.fields.autoApprove", { ns: "config" })}</Label>
<BinaryChoice
active={draft.autoApprove}
disabled={loading || saving}
onChange={(value) => updateDraft("autoApprove", value)}
leftLabel={t("system.states.disabled", { ns: "config" })}
rightLabel={t("system.states.enabled", { ns: "config" })}
/>
</div>
<div className="h-px bg-border/60" />
<div className="flex flex-wrap items-center justify-between gap-3">
<Label className="text-sm font-medium">{t("system.fields.autoPayout", { ns: "config" })}</Label>
<BinaryChoice
active={draft.autoPayout}
disabled={loading || saving}
onChange={(value) => updateDraft("autoPayout", value)}
leftLabel={t("system.states.disabled", { ns: "config" })}
rightLabel={t("system.states.enabled", { ns: "config" })}
/>
</div>
<div className="h-px bg-border/60" />
<div className="grid max-w-xs gap-2">
<Label htmlFor="cooldown-minutes" className="text-sm font-medium">
{t("system.fields.cooldownMinutes", { ns: "config" })}
@@ -258,21 +299,16 @@ export function SystemSettingsScreen() {
/>
</div>
</div>
</AdminPageCard>
</section>
<section className="space-y-4">
<h2 className="border-b border-border/60 pb-3 text-base font-semibold text-foreground">
{t("wallet.title", { ns: "config" })}
</h2>
<AdminPageCard
title={t("wallet.title", { ns: "config" })}
description={t("wallet.description", { ns: "config" })}
>
<WalletConfigDocScreen embedded />
</section>
<section className="space-y-4">
<h2 className="border-b border-border/60 pb-3 text-base font-semibold text-foreground">
{t("system.frontendConfig", { ns: "config" })}
</h2>
</AdminPageCard>
<AdminPageCard title={t("system.frontendConfig", { ns: "config" })}>
<div className="grid gap-2">
<Label className="text-sm font-medium">
{t("system.fields.playRulesHtml", { ns: "config" })}
@@ -318,22 +354,33 @@ export function SystemSettingsScreen() {
</TabsContent>
</Tabs>
</div>
</AdminPageCard>
</section>
<SaveActions
dirty={dirty}
loading={loading}
saving={saving}
onSave={() => void handleSave()}
onDiscard={() => {
setDraft(saved);
setDirty(false);
}}
saveLabel={saveLabel}
savingLabel={savingLabel}
discardLabel={discardLabel}
/>
<Card className="admin-list-card">
<CardContent className="admin-list-content">
<SaveActions
dirty={dirty}
loading={loading}
saving={saving}
onSave={() =>
requestConfirm({
title: t("system.confirmSaveTitle", { ns: "config" }),
description: t("system.confirmSaveDescription", { ns: "config" }),
confirmLabel: t("confirm.confirmSave", { ns: "common" }),
onConfirm: () => handleSave(),
})
}
onDiscard={() => {
setDraft(saved);
setDirty(false);
}}
saveLabel={saveLabel}
savingLabel={savingLabel}
discardLabel={discardLabel}
/>
</CardContent>
</Card>
<ConfirmDialog />
</div>
);
}