"use client"; import { useCallback, useEffect, useState } from "react"; import { useSearchParams } from "next/navigation"; import { useTranslation } from "react-i18next"; import { getDrawResultByNo } from "@/api/draw"; import { getTicketDrawMyMatch } from "@/api/ticket-items"; import Link from "next/link"; import { Button, buttonVariants } from "@/components/ui/button"; import { PlayerPanel } from "@/components/layout/player-panel"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "@/components/ui/card"; import { Skeleton } from "@/components/ui/skeleton"; import { DrawWinningCheckPanel } from "@/features/results/draw-winning-check-panel"; import { JackpotResultsStrip } from "@/features/results/jackpot-results-strip"; import { TwentyThreeResultsGrid } from "@/features/results/twenty-three-results-grid"; import { useCurrencyCatalog } from "@/hooks/use-currency-catalog"; import { getPlayerBearerTokenPayload } from "@/lib/lottery-auth"; import { formatPlayerInstant } from "@/lib/player-datetime"; import { formatMinorAsCurrency } from "@/lib/money"; import { isCreditFundingPlayer } from "@/lib/player-funding-mode"; import { norm4d } from "@/lib/norm-4d"; import { useActivePlayerCurrency } from "@/hooks/use-active-player-currency"; import { resultsPrizeLabelKey, RESULTS_TOP_PRIZE_KEYS } from "@/lib/results-prize-labels"; import { cn } from "@/lib/utils"; import { usePlayerSessionStore } from "@/stores/player-session-store"; import type { DrawResultDetailPayload } from "@/types/api/draw-results"; type DrawResultDetailScreenProps = { drawNo: string; }; /** §4.6 开奖结果详情:23 分区 + [< >] 切换 + 本人命中高亮 + Jackpot */ export function DrawResultDetailScreen({ drawNo }: DrawResultDetailScreenProps) { const { t } = useTranslation("player"); const searchParams = useSearchParams(); const checkOpen = searchParams.get("check") === "1"; const { activeCurrency } = useActivePlayerCurrency(); const creditMode = isCreditFundingPlayer(usePlayerSessionStore((state) => state.profile)); useCurrencyCatalog(); const [data, setData] = useState(null); const [error, setError] = useState(null); const [loading, setLoading] = useState(true); const [activeProviderCode, setActiveProviderCode] = useState(""); const [highlightSet, setHighlightSet] = useState | null>(null); const [myTotals, setMyTotals] = useState<{ win: number; jackpot: number; hasBets: boolean; } | null>(null); const load = useCallback(async () => { setLoading(true); setError(null); try { const row = await getDrawResultByNo(drawNo, { currency: activeCurrency }); setData(row); setActiveProviderCode(String(row.provider_code ?? row.provider_results?.[0]?.provider_code ?? "")); } catch { setData(null); setError(t("results.unavailable")); } finally { setLoading(false); } }, [activeCurrency, drawNo, t]); useEffect(() => { queueMicrotask(() => { void load(); }); }, [load]); useEffect(() => { let cancelled = false; queueMicrotask(() => { if (!data) { setHighlightSet(null); setMyTotals(null); return; } const token = getPlayerBearerTokenPayload(); if (!token) { setHighlightSet(new Set()); setMyTotals(null); return; } void (async () => { try { const m = await getTicketDrawMyMatch(data.draw_no); if (cancelled) return; setHighlightSet(new Set(m.hit_numbers_4d.map((n) => norm4d(n)))); setMyTotals({ win: m.total_win_minor, jackpot: m.total_jackpot_win_minor, hasBets: m.has_bets, }); } catch { if (!cancelled) { setHighlightSet(new Set()); setMyTotals(null); } } })(); }); return () => { cancelled = true; }; }, [data]); if (loading) { return (
); } if (error || !data) { return (

{error ?? t("results.noData")}

); } const currency = data.jackpot?.currency_code ?? activeCurrency; const providerResults = data.provider_results ?? []; const activeProviderResult = providerResults.find((row) => String(row.provider_code ?? "") === activeProviderCode) ?? providerResults[0] ?? null; const activeResults = activeProviderResult?.results ?? data.results; const showMyPayout = myTotals && myTotals.hasBets && (myTotals.win > 0 || myTotals.jackpot > 0); const showHitOnly = myTotals?.hasBets && highlightSet && highlightSet.size > 0 && myTotals && myTotals.win === 0 && myTotals.jackpot === 0; return (
{data.previous_draw_no ? ( {t("results.previous")} ) : ( )}
{data.draw_no} {t("results.drawTime", { time: formatPlayerInstant(data.draw_time_iso ?? data.draw_time ?? null), })}
{data.next_draw_no ? ( {t("results.next")} ) : ( )}

{t("results.detailTitle")}

{t("results.detail")}
{providerResults.length > 1 ? (
{providerResults.map((row) => { const code = String(row.provider_code ?? ""); const active = code === String(activeProviderResult?.provider_code ?? ""); return ( ); })}
) : null}
{RESULTS_TOP_PRIZE_KEYS.map((tier) => (

{t(resultsPrizeLabelKey(tier))}

{activeResults[tier]}

))}
{showMyPayout && myTotals ? (

{t(creditMode ? "results.creditMyWin" : "results.myPayout")}

{t("results.regular", { amount: formatMinorAsCurrency(myTotals.win, currency), })} {myTotals.jackpot > 0 ? ( <> {" "} ·{" "} {t("results.jackpot", { amount: formatMinorAsCurrency(myTotals.jackpot, currency), })} ) : null}

) : null} {showHitOnly ? (
{t(creditMode ? "results.creditHitPending" : "results.hitPending")}
) : null}
); }