"use client"; import { useCallback, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { toast } from "sonner"; import { getAdminSettings, updateAdminSetting, } from "@/api/admin-settings"; import { WalletConfigDocScreen } from "@/modules/config/doc/wallet-config-doc-screen"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Textarea } from "@/components/ui/textarea"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { LotteryApiBizError } from "@/types/api/errors"; const DRAW_GROUP = "draw"; const SETTLEMENT_GROUP = "settlement"; const DRAW_KEYS = { REQUIRE_MANUAL_REVIEW: "draw.require_manual_review", COOLDOWN_MINUTES: "draw.cooldown_minutes", AUTO_SETTLEMENT: "settlement.auto_run_on_tick", } as const; const FRONTEND_GROUP = "frontend"; const FRONTEND_KEYS = { PLAY_RULES_HTML: "frontend.play_rules_html", PLAY_RULES_HTML_ZH: "frontend.play_rules_html_zh", PLAY_RULES_HTML_EN: "frontend.play_rules_html_en", PLAY_RULES_HTML_NE: "frontend.play_rules_html_ne", } as const; interface RuntimeDraft { requireManualReview: boolean; cooldownMinutes: string; autoSettlement: boolean; playRulesHtmlZh: string; playRulesHtmlEn: string; playRulesHtmlNe: string; } function BinaryChoice({ active, disabled, onChange, leftLabel, rightLabel, }: { active: boolean; disabled: boolean; onChange: (value: boolean) => void; leftLabel: string; rightLabel: string; }) { return (
); } function SaveActions({ dirty, loading, saving, onSave, onDiscard, saveLabel, savingLabel, discardLabel, }: { dirty: boolean; loading: boolean; saving: boolean; onSave: () => void; onDiscard: () => void; saveLabel: string; savingLabel: string; discardLabel: string; }) { return (
{dirty ? ( ) : null}
); } export function SystemSettingsScreen() { const { t } = useTranslation(["common", "config", "adminUsers"]); const [draft, setDraft] = useState({ requireManualReview: false, cooldownMinutes: "15", autoSettlement: true, playRulesHtmlZh: "", playRulesHtmlEn: "", playRulesHtmlNe: "", }); const [saved, setSaved] = useState({ requireManualReview: false, cooldownMinutes: "15", autoSettlement: true, playRulesHtmlZh: "", playRulesHtmlEn: "", playRulesHtmlNe: "", }); const [loading, setLoading] = useState(true); const [saving, setSaving] = useState(false); const [dirty, setDirty] = useState(false); const load = useCallback(async () => { setLoading(true); try { const [drawRes, settlementRes, frontendRes] = await Promise.all([ getAdminSettings(DRAW_GROUP), getAdminSettings(SETTLEMENT_GROUP), getAdminSettings(FRONTEND_GROUP), ]); const kv: Record = {}; for (const item of [...drawRes.items, ...settlementRes.items, ...frontendRes.items]) { kv[item.key] = item.value; } const legacyHtml = String(kv[FRONTEND_KEYS.PLAY_RULES_HTML] ?? ""); const nextDraft: RuntimeDraft = { 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), 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] ?? ""), }; setDraft(nextDraft); setSaved(nextDraft); setDirty(false); } catch { toast.error(t("system.loadFailed", { ns: "config" })); } finally { setLoading(false); } }, [t]); useEffect(() => { queueMicrotask(() => { void load(); }); }, [load]); const updateDraft = (field: K, value: RuntimeDraft[K]) => { setDraft((prev) => ({ ...prev, [field]: value })); setDirty(true); }; const handleSave = async () => { setSaving(true); try { await updateAdminSetting(DRAW_KEYS.REQUIRE_MANUAL_REVIEW, draft.requireManualReview); await updateAdminSetting( DRAW_KEYS.COOLDOWN_MINUTES, Math.max(0, Number.parseInt(draft.cooldownMinutes || "0", 10) || 0), ); await updateAdminSetting(DRAW_KEYS.AUTO_SETTLEMENT, draft.autoSettlement); 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); await updateAdminSetting(FRONTEND_KEYS.PLAY_RULES_HTML, draft.playRulesHtmlZh); toast.success(t("system.saveSuccess", { ns: "config" })); setSaved(draft); setDirty(false); } catch (error) { toast.error( error instanceof LotteryApiBizError ? error.message : t("system.saveFailed", { ns: "config" }), ); } finally { setSaving(false); } }; const saveLabel = t("actions.save", { ns: "adminUsers" }); const savingLabel = t("saving", { ns: "adminUsers" }); const discardLabel = t("system.discard", { ns: "config" }); return (

{t("system.title", { ns: "config" })}

updateDraft("requireManualReview", value)} leftLabel={t("system.states.disabled", { ns: "config" })} rightLabel={t("system.states.enabled", { ns: "config" })} />
updateDraft("autoSettlement", value)} leftLabel={t("system.states.disabled", { ns: "config" })} rightLabel={t("system.states.enabled", { ns: "config" })} />
updateDraft("cooldownMinutes", e.target.value)} disabled={loading || saving} />

{t("wallet.title", { ns: "config" })}

{t("system.frontendConfig", { ns: "config" })}

{t("system.fields.playRulesHtmlDesc", { ns: "config" })}

{t("play.locales.zh", { ns: "config" })} {t("play.locales.en", { ns: "config" })} {t("play.locales.ne", { ns: "config" })}