"use client"; import { useCallback, useEffect, useState } from "react"; import { useTranslation } from "react-i18next"; import { getAdminJackpotPools, postAdminJackpotManualBurst, putAdminJackpotPool, } from "@/api/admin-jackpot"; import { useConfirmAction } from "@/hooks/use-confirm-action"; import { adminHasAnyPermission } from "@/lib/admin-permissions"; import { PRD_JACKPOT_MANAGE, PRD_JACKPOT_MANUAL_BURST } from "@/lib/admin-prd"; import { ModuleScaffold } from "@/components/admin/module-scaffold"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { toast } from "sonner"; import { useAdminProfile } from "@/stores/admin-session"; import { LotteryApiBizError } from "@/types/api/errors"; import type { AdminJackpotPoolRow } from "@/types/api/admin-jackpot"; type Draft = { current_amount: string; contribution_rate: string; trigger_threshold: string; payout_rate: string; force_trigger_draw_gap: string; min_bet_amount: string; combo_trigger_play_codes: string; status: string; manual_burst_draw_id: string; }; function toDraft(p: AdminJackpotPoolRow): Draft { return { current_amount: String(p.current_amount), contribution_rate: String(p.contribution_rate), trigger_threshold: String(p.trigger_threshold), payout_rate: String(p.payout_rate), force_trigger_draw_gap: String(p.force_trigger_draw_gap), min_bet_amount: String(p.min_bet_amount), combo_trigger_play_codes: p.combo_trigger_play_codes.join(","), status: String(p.status), manual_burst_draw_id: "", }; } type JackpotPoolsConsoleProps = { /** 嵌入运营配置单页时去掉外层脚手架与重复标题 */ embedded?: boolean; }; export function JackpotPoolsConsole({ embedded = false }: JackpotPoolsConsoleProps) { const { t } = useTranslation(["jackpot", "common"]); const profile = useAdminProfile(); const canManageJackpot = adminHasAnyPermission(profile?.permissions, [PRD_JACKPOT_MANAGE]); const canManualBurst = adminHasAnyPermission(profile?.permissions, [PRD_JACKPOT_MANUAL_BURST]); const { request: requestConfirm, ConfirmDialog: ConfirmActionDialog } = useConfirmAction(); const [items, setItems] = useState([]); const [drafts, setDrafts] = useState>({}); const [loading, setLoading] = useState(true); const [savingId, setSavingId] = useState(null); const [burstingId, setBurstingId] = useState(null); const [confirmBurstPoolId, setConfirmBurstPoolId] = useState(null); const load = useCallback(async () => { setLoading(true); try { const res = await getAdminJackpotPools(); setItems(res.items); const d: Record = {}; for (const p of res.items) { d[p.id] = toDraft(p); } setDrafts(d); } catch (e) { toast.error(e instanceof LotteryApiBizError ? e.message : t("loadFailed")); } finally { setLoading(false); } }, [t]); useEffect(() => { queueMicrotask(() => { void load(); }); }, [load]); const updateDraft = (id: number, patch: Partial) => { setDrafts((prev) => ({ ...prev, [id]: { ...prev[id], ...patch }, })); }; const save = async (p: AdminJackpotPoolRow) => { const d = drafts[p.id]; if (!d) return; setSavingId(p.id); try { await putAdminJackpotPool(p.id, { current_amount: Number.parseInt(d.current_amount, 10), contribution_rate: Number(d.contribution_rate), trigger_threshold: Number.parseInt(d.trigger_threshold, 10), payout_rate: Number(d.payout_rate), force_trigger_draw_gap: Number.parseInt(d.force_trigger_draw_gap, 10), min_bet_amount: Number.parseInt(d.min_bet_amount, 10), combo_trigger_play_codes: d.combo_trigger_play_codes .split(",") .map((v) => v.trim().toLowerCase()) .filter(Boolean), status: Number.parseInt(d.status, 10), }); toast.success(t("saveSuccess")); await load(); } catch (e) { toast.error(e instanceof LotteryApiBizError ? e.message : t("saveFailed")); } finally { setSavingId(null); } }; const manualBurst = async (p: AdminJackpotPoolRow) => { const d = drafts[p.id]; if (!d) return; const drawId = Number.parseInt(d.manual_burst_draw_id, 10); if (!Number.isFinite(drawId) || drawId <= 0) { toast.error(t("invalidDrawId")); return; } setBurstingId(p.id); try { const res = await postAdminJackpotManualBurst(p.id, { draw_id: drawId }); toast.success( `${t("manualBurstSuccess")} · ${res.draw_no} · ${res.winner_count} ${t("winnerCount")}`, ); await load(); } catch (e) { toast.error(e instanceof LotteryApiBizError ? e.message : t("manualBurstFailed")); } finally { setBurstingId(null); setConfirmBurstPoolId(null); } }; const poolList = (
{loading ?

{t("states.loading", { ns: "common" })}

: null} {!loading && items.length === 0 ? (

{t("noPoolData")}

) : null} {items.map((p) => { const d = drafts[p.id] ?? toDraft(p); return (

{p.currency_code}

updateDraft(p.id, { current_amount: e.target.value })} />
updateDraft(p.id, { contribution_rate: e.target.value })} />
updateDraft(p.id, { trigger_threshold: e.target.value })} />
updateDraft(p.id, { payout_rate: e.target.value })} />
updateDraft(p.id, { force_trigger_draw_gap: e.target.value })} />
updateDraft(p.id, { min_bet_amount: e.target.value })} />
updateDraft(p.id, { combo_trigger_play_codes: e.target.value })} />
{canManageJackpot ? (
) : null} {canManualBurst ? (

{t("manualBurst")}

{t("manualBurstHint")}

updateDraft(p.id, { manual_burst_draw_id: e.target.value })} />
) : null}
); })}
); const confirmPool = confirmBurstPoolId !== null ? items.find((p) => p.id === confirmBurstPoolId) : null; const confirmDraft = confirmPool ? drafts[confirmPool.id] : null; const confirmDialog = ( !open && setConfirmBurstPoolId(null)}> {t("manualBurstConfirmTitle")} {t("manualBurstConfirmDescription", { drawId: confirmDraft?.manual_burst_draw_id ?? "—", })} ); if (embedded) { return ( <> {poolList} {confirmDialog} ); } return ( {t("configTitle")} {poolList} {confirmDialog} ); }