"use client"; import Link from "next/link"; import { useCallback, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; import { useAsyncEffect } from "@/hooks/use-async-effect"; import { useTranslationRef } from "@/hooks/use-translation-ref"; import { toast } from "sonner"; import { getAdminDraws } from "@/api/admin-draws"; import { getAdminRiskPools } from "@/api/admin-risk"; import { AdminTableExportButton } from "@/components/admin/admin-table-export-button"; import { AdminNoResourceState, AdminTableNoResourceRow } from "@/components/admin/admin-no-resource-state"; import { Button, buttonVariants } from "@/components/ui/button"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "@/components/ui/table"; import { Input } from "@/components/ui/input"; import { AdminLoadingState, AdminLoadingInline, AdminTableLoadingRow } from "@/components/admin/admin-loading-state"; import { ConfigSection } from "@/modules/config/config-section"; import { formatAdminMinorUnits } from "@/lib/money"; import { cn } from "@/lib/utils"; import { LotteryApiBizError } from "@/types/api/errors"; import type { AdminDrawListItem } from "@/types/api/admin-draws"; import type { AdminRiskPoolRow } from "@/types/api/admin-risk"; type PoolFilter = "all" | "sold_out" | "high_risk"; export function RiskCapRuntimePanel() { const { t } = useTranslation(["config", "risk", "draws", "common"]); const tRef = useTranslationRef(["config", "common"]); const [draws, setDraws] = useState([]); const [drawsLoading, setDrawsLoading] = useState(true); const [drawId, setDrawId] = useState(""); const [numberQ, setNumberQ] = useState(""); const [appliedNumber, setAppliedNumber] = useState(""); const [poolFilter, setPoolFilter] = useState("all"); const [pools, setPools] = useState([]); const [currencyCode, setCurrencyCode] = useState(null); const [poolsLoading, setPoolsLoading] = useState(false); const [poolsError, setPoolsError] = useState(null); const selectedDraw = useMemo( () => draws.find((d) => String(d.id) === drawId) ?? null, [draws, drawId], ); const loadDraws = useCallback(async () => { setDrawsLoading(true); try { const data = await getAdminDraws({ page: 1, per_page: 50 }); setDraws(data.items); if (data.items.length > 0) { setDrawId((prev) => (prev === "" ? String(data.items[0].id) : prev)); } } catch (e) { toast.error( e instanceof LotteryApiBizError ? e.message : tRef.current("errors.loadFailed", { ns: "common" }), ); setDraws([]); } finally { setDrawsLoading(false); } }, []); const loadPools = useCallback(async () => { if (!drawId) { setPools([]); return; } const id = Number(drawId); if (!Number.isFinite(id)) { return; } setPoolsLoading(true); setPoolsError(null); try { const data = await getAdminRiskPools(id, { page: 1, per_page: 200, normalized_number: appliedNumber.trim() || undefined, sold_out_only: poolFilter === "sold_out", high_risk_only: poolFilter === "high_risk", sort: poolFilter === "high_risk" ? "usage_desc" : "number_asc", }); setPools(data.items); setCurrencyCode(data.currency_code); } catch (e) { setPoolsError( e instanceof LotteryApiBizError ? e.message : tRef.current("errors.loadFailed", { ns: "common" }), ); setPools([]); } finally { setPoolsLoading(false); } }, [appliedNumber, drawId, poolFilter]); useAsyncEffect(() => { void loadDraws(); }, []); useAsyncEffect(() => { void loadPools(); }, [appliedNumber, drawId, poolFilter]); const riskBase = drawId ? `/admin/draws/${drawId}/risk` : null; return (
{riskBase ? (
{t("subnav.riskPools", { ns: "draws" })} {t("filterHighRisk", { ns: "risk" })} {t("filterSoldOut", { ns: "risk" })} {t("subnav.riskLockLogs", { ns: "draws" })}
) : null}
{drawId ? ( <>
setNumberQ(e.target.value)} />
{( [ { id: "all", label: t("riskCap.runtime.filterAll", { ns: "config" }) }, { id: "sold_out", label: t("riskCap.runtime.filterSoldOut", { ns: "config" }) }, { id: "high_risk", label: t("riskCap.runtime.filterHighRisk", { ns: "config" }) }, ] as const ).map((f) => ( ))}
{pools.length > 0 ? ( ) : null}
{poolsError ?

{poolsError}

: null}
{t("riskCap.table.number", { ns: "config" })} {t("riskCap.table.used", { ns: "config" })} {t("riskCap.table.remaining", { ns: "config" })} {t("riskCap.table.ratio", { ns: "config" })} {t("riskCap.table.soldOut", { ns: "config" })} {poolsLoading ? ( ) : pools.length === 0 ? ( ) : ( pools.map((row) => ( = 0.8 && "bg-amber-500/10", )} > {row.normalized_number} {formatAdminMinorUnits(row.locked_amount, currencyCode ?? undefined)} {formatAdminMinorUnits(row.remaining_amount, currencyCode ?? undefined)} {row.usage_ratio != null ? `${Math.round(row.usage_ratio * 100)}%` : "—"} {row.is_sold_out ? t("riskCap.runtime.soldYes", { ns: "config" }) : t("riskCap.runtime.soldNo", { ns: "config" })} )) )}

{t("riskCap.runtime.manageHint", { ns: "config" })}

) : (

{t("riskCap.runtime.noDraws", { ns: "config" })}

)}
); }