diff --git a/src/i18n/locales/en/config.json b/src/i18n/locales/en/config.json index 9517eae..625cea8 100644 --- a/src/i18n/locales/en/config.json +++ b/src/i18n/locales/en/config.json @@ -182,6 +182,14 @@ "fields": { "manualReview": "Require manual review for draw results", "cooldownMinutes": "Cooldown duration (minutes)", + "defaultCurrency": "Default currency code", + "drawIntervalMinutes": "Draw interval (minutes)", + "drawBettingWindowSeconds": "Betting window (seconds)", + "drawCloseBeforeDrawSeconds": "Close before draw (seconds)", + "drawBufferDrawsAhead": "Pre-generated future draws", + "currencyDisplayDecimals": "Display decimals", + "currencyDecimalSeparator": "Decimal separator", + "currencyThousandsSeparator": "Thousands separator", "autoSettlement": "Run settlement automatically", "autoApprove": "Auto-approve settlement batches", "autoPayout": "Auto-credit winnings to wallets", diff --git a/src/i18n/locales/ne/config.json b/src/i18n/locales/ne/config.json index b52a6a3..dfa1ec9 100644 --- a/src/i18n/locales/ne/config.json +++ b/src/i18n/locales/ne/config.json @@ -182,6 +182,14 @@ "fields": { "manualReview": "ड्रअ परिणामका लागि म्यानुअल समीक्षा चाहिने", "cooldownMinutes": "कूलडाउन अवधि (मिनेट)", + "defaultCurrency": "पूर्वनिर्धारित मुद्रा कोड", + "drawIntervalMinutes": "ड्रअ अन्तराल (मिनेट)", + "drawBettingWindowSeconds": "बेटिङ विन्डो (सेकेन्ड)", + "drawCloseBeforeDrawSeconds": "ड्रअ अघि बन्द (सेकेन्ड)", + "drawBufferDrawsAhead": "अग्रिम सिर्जना गरिने ड्रअ संख्या", + "currencyDisplayDecimals": "प्रदर्शन दशमलव स्थान", + "currencyDecimalSeparator": "दशमलव विभाजक", + "currencyThousandsSeparator": "हजार विभाजक", "autoSettlement": "सेटलमेन्ट स्वतः चलाउने", "autoApprove": "सेटलमेन्ट ब्याच स्वतः स्वीकृत", "autoPayout": "जित रकम स्वतः वालेटमा जम्मा", diff --git a/src/i18n/locales/zh/config.json b/src/i18n/locales/zh/config.json index d12e672..d639b74 100644 --- a/src/i18n/locales/zh/config.json +++ b/src/i18n/locales/zh/config.json @@ -182,6 +182,14 @@ "fields": { "manualReview": "开奖结果必须人工审核", "cooldownMinutes": "冷静期时长(分钟)", + "defaultCurrency": "默认币种代码", + "drawIntervalMinutes": "开奖间隔(分钟)", + "drawBettingWindowSeconds": "下注窗口(秒)", + "drawCloseBeforeDrawSeconds": "封盘提前(秒)", + "drawBufferDrawsAhead": "预生成未来期数", + "currencyDisplayDecimals": "金额显示小数位", + "currencyDecimalSeparator": "小数分隔符", + "currencyThousandsSeparator": "千位分隔符", "autoSettlement": "自动执行结算", "autoApprove": "自动审核结算批次", "autoPayout": "自动派彩入账", diff --git a/src/lib/admin-http.ts b/src/lib/admin-http.ts index 8de0577..daad2d9 100644 --- a/src/lib/admin-http.ts +++ b/src/lib/admin-http.ts @@ -14,8 +14,7 @@ export function hasLotteryAdminApiBaseUrl(): boolean { } export const adminHttp = axios.create({ - // 统一走 Next 同源 /api 代理,由 next.config.ts 的 API_BASE_URL 转发到后端。 - baseURL: "/api", + // API 路径统一由调用方传 `/api/v1/...`,避免与前缀重复拼接成 `/api/api/v1/...`。 timeout: 30_000, headers: { Accept: "application/json" }, }); diff --git a/src/modules/players/players-console.tsx b/src/modules/players/players-console.tsx index 91cb8cb..359b2b1 100644 --- a/src/modules/players/players-console.tsx +++ b/src/modules/players/players-console.tsx @@ -310,7 +310,16 @@ export function PlayersConsole(): React.ReactElement { onValueChange={(v) => setSiteCode(v === "__all__" ? "" : v ?? "")} > - + + {(v) => { + const value = String(v ?? "__all__"); + if (value === "__all__") { + return t("filterAllSites"); + } + const site = siteOptions.find((item) => item.code === value); + return site ? `${site.code} — ${site.name}` : value; + }} + {t("filterAllSites")} diff --git a/src/modules/settings/system-settings-screen.tsx b/src/modules/settings/system-settings-screen.tsx index 0095971..2e93606 100644 --- a/src/modules/settings/system-settings-screen.tsx +++ b/src/modules/settings/system-settings-screen.tsx @@ -24,8 +24,16 @@ const DRAW_GROUP = "draw"; const SETTLEMENT_GROUP = "settlement"; const DRAW_KEYS = { + DEFAULT_CURRENCY: "currency.default_code", + DRAW_INTERVAL_MINUTES: "draw.interval_minutes", + DRAW_BETTING_WINDOW_SECONDS: "draw.betting_window_seconds", + DRAW_CLOSE_BEFORE_DRAW_SECONDS: "draw.close_before_draw_seconds", + 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", AUTO_SETTLEMENT: "settlement.auto_run_on_tick", AUTO_APPROVE: "settlement.auto_approve_on_tick", AUTO_PAYOUT: "settlement.auto_payout_on_tick", @@ -41,8 +49,16 @@ const FRONTEND_KEYS = { } as const; interface RuntimeDraft { + defaultCurrency: string; + drawIntervalMinutes: string; + drawBettingWindowSeconds: string; + drawCloseBeforeDrawSeconds: string; + drawBufferDrawsAhead: string; requireManualReview: boolean; cooldownMinutes: string; + currencyDisplayDecimals: string; + currencyDecimalSeparator: string; + currencyThousandsSeparator: string; autoSettlement: boolean; autoApprove: boolean; autoPayout: boolean; @@ -89,8 +105,16 @@ export function SystemSettingsScreen() { const { t } = useTranslation(["common", "config", "adminUsers"]); const { request: requestConfirm, ConfirmDialog } = useConfirmAction(); const [draft, setDraft] = useState({ + defaultCurrency: "NPR", + drawIntervalMinutes: "5", + drawBettingWindowSeconds: "270", + drawCloseBeforeDrawSeconds: "30", + drawBufferDrawsAhead: "8", requireManualReview: false, cooldownMinutes: "15", + currencyDisplayDecimals: "2", + currencyDecimalSeparator: ".", + currencyThousandsSeparator: ",", autoSettlement: true, autoApprove: true, autoPayout: true, @@ -100,8 +124,16 @@ export function SystemSettingsScreen() { playRulesHtmlNe: "", }); const [saved, setSaved] = useState({ + defaultCurrency: "NPR", + drawIntervalMinutes: "5", + drawBettingWindowSeconds: "270", + drawCloseBeforeDrawSeconds: "30", + drawBufferDrawsAhead: "8", requireManualReview: false, cooldownMinutes: "15", + currencyDisplayDecimals: "2", + currencyDecimalSeparator: ".", + currencyThousandsSeparator: ",", autoSettlement: true, autoApprove: true, autoPayout: true, @@ -130,8 +162,16 @@ export function SystemSettingsScreen() { const legacyHtml = String(kv[FRONTEND_KEYS.PLAY_RULES_HTML] ?? ""); const nextDraft: RuntimeDraft = { + defaultCurrency: String(kv[DRAW_KEYS.DEFAULT_CURRENCY] ?? "NPR"), + drawIntervalMinutes: String(kv[DRAW_KEYS.DRAW_INTERVAL_MINUTES] ?? 5), + drawBettingWindowSeconds: String(kv[DRAW_KEYS.DRAW_BETTING_WINDOW_SECONDS] ?? 270), + drawCloseBeforeDrawSeconds: String(kv[DRAW_KEYS.DRAW_CLOSE_BEFORE_DRAW_SECONDS] ?? 30), + drawBufferDrawsAhead: String(kv[DRAW_KEYS.DRAW_BUFFER_DRAWS_AHEAD] ?? 8), requireManualReview: Boolean(kv[DRAW_KEYS.REQUIRE_MANUAL_REVIEW] ?? false), cooldownMinutes: String(kv[DRAW_KEYS.COOLDOWN_MINUTES] ?? 15), + 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] ?? ","), autoSettlement: Boolean(kv[DRAW_KEYS.AUTO_SETTLEMENT] ?? true), autoApprove: Boolean(kv[DRAW_KEYS.AUTO_APPROVE] ?? true), autoPayout: Boolean(kv[DRAW_KEYS.AUTO_PAYOUT] ?? true), @@ -164,11 +204,43 @@ export function SystemSettingsScreen() { const handleSave = async () => { setSaving(true); try { + await updateAdminSetting( + DRAW_KEYS.DEFAULT_CURRENCY, + draft.defaultCurrency.trim().toUpperCase() || "NPR", + ); + await updateAdminSetting( + DRAW_KEYS.DRAW_INTERVAL_MINUTES, + Math.max(1, Number.parseInt(draft.drawIntervalMinutes || "5", 10) || 5), + ); + await updateAdminSetting( + DRAW_KEYS.DRAW_BETTING_WINDOW_SECONDS, + Math.max(10, Number.parseInt(draft.drawBettingWindowSeconds || "270", 10) || 270), + ); + await updateAdminSetting( + DRAW_KEYS.DRAW_CLOSE_BEFORE_DRAW_SECONDS, + Math.max(5, Number.parseInt(draft.drawCloseBeforeDrawSeconds || "30", 10) || 30), + ); + await updateAdminSetting( + DRAW_KEYS.DRAW_BUFFER_DRAWS_AHEAD, + Math.max(1, Number.parseInt(draft.drawBufferDrawsAhead || "8", 10) || 8), + ); 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.CURRENCY_DISPLAY_DECIMALS, + Math.max(0, Math.min(12, Number.parseInt(draft.currencyDisplayDecimals || "2", 10) || 2)), + ); + await updateAdminSetting( + DRAW_KEYS.CURRENCY_DECIMAL_SEPARATOR, + (draft.currencyDecimalSeparator || ".").slice(0, 1), + ); + await updateAdminSetting( + DRAW_KEYS.CURRENCY_THOUSANDS_SEPARATOR, + (draft.currencyThousandsSeparator || ",").slice(0, 1), + ); await updateAdminSetting(DRAW_KEYS.AUTO_SETTLEMENT, draft.autoSettlement); await updateAdminSetting(DRAW_KEYS.AUTO_APPROVE, draft.autoApprove); await updateAdminSetting(DRAW_KEYS.AUTO_PAYOUT, draft.autoPayout); @@ -212,6 +284,119 @@ export function SystemSettingsScreen() {
+
+
+ + updateDraft("defaultCurrency", e.target.value.toUpperCase())} + disabled={loading || saving} + maxLength={16} + /> +
+
+ + updateDraft("drawIntervalMinutes", e.target.value)} + disabled={loading || saving} + /> +
+
+ + updateDraft("drawBettingWindowSeconds", e.target.value)} + disabled={loading || saving} + /> +
+
+ + updateDraft("drawCloseBeforeDrawSeconds", e.target.value)} + disabled={loading || saving} + /> +
+
+ + updateDraft("drawBufferDrawsAhead", e.target.value)} + disabled={loading || saving} + /> +
+
+ + updateDraft("currencyDisplayDecimals", e.target.value)} + disabled={loading || saving} + /> +
+
+ + updateDraft("currencyDecimalSeparator", e.target.value)} + disabled={loading || saving} + maxLength={1} + /> +
+
+ + updateDraft("currencyThousandsSeparator", e.target.value)} + disabled={loading || saving} + maxLength={1} + /> +
+
+ +
+
- + + {(v) => { + const value = String(v ?? "__all__"); + if (value === "__all__") { + return t("filterAllSites"); + } + const site = siteOptions.find((item) => item.code === value); + return site ? `${site.code} — ${site.name}` : value; + }} + {t("filterAllSites")}