- Added styles for player-side toast notifications to improve user feedback. - Adjusted padding and spacing in various components for a more cohesive layout. - Updated card and dialog components to streamline visual hierarchy and enhance readability. - Refactored player panel and navigation elements for better alignment and user experience.
132 lines
4.6 KiB
TypeScript
132 lines
4.6 KiB
TypeScript
import type { DrawResultsNumbers } from "@/types/api/draw-results";
|
||
import { Trophy } from "lucide-react";
|
||
import { useTranslation } from "react-i18next";
|
||
|
||
import { norm4d } from "@/lib/norm-4d";
|
||
import { cn } from "@/lib/utils";
|
||
|
||
type TwentyThreeResultsGridProps = {
|
||
numbers: DrawResultsNumbers;
|
||
/** 与本人注单组合相交的 4D(规范化后),命中格子使用界面文档 §4.6 金色高亮 */
|
||
highlighted4d?: ReadonlySet<string> | null;
|
||
};
|
||
|
||
/**
|
||
* §4.6 开奖结果页:头/二/三奖 + Starter 10 + Consolation 10
|
||
*/
|
||
export function TwentyThreeResultsGrid({
|
||
numbers,
|
||
highlighted4d,
|
||
}: TwentyThreeResultsGridProps) {
|
||
const { t } = useTranslation("player");
|
||
const starters = numbers.starter ?? [];
|
||
const consos = numbers.consolation ?? [];
|
||
const hits = highlighted4d ?? null;
|
||
|
||
const isHit = (raw: string): boolean => {
|
||
const v = (raw || "").trim();
|
||
return (
|
||
hits !== null &&
|
||
hits.size > 0 &&
|
||
v !== "" &&
|
||
v !== "—" &&
|
||
hits.has(norm4d(v))
|
||
);
|
||
};
|
||
|
||
const smallCellTone = (raw: string, tone: "red" | "blue") =>
|
||
cn(
|
||
"grid min-h-[3.875rem] grid-rows-[auto_1fr] rounded-lg border bg-white px-1.5 py-2 text-center shadow-[0_6px_16px_rgba(15,23,42,0.04)]",
|
||
tone === "red" ? "border-red-100 text-[#e5002c]" : "border-blue-100 text-[#0b56b7]",
|
||
isHit(raw) && "border-amber-400 bg-amber-50 text-amber-700 shadow-[0_8px_18px_rgba(245,158,11,0.16)]",
|
||
);
|
||
|
||
const prizeCards = [
|
||
{
|
||
key: "1st" as const,
|
||
label: t("results.grid.first"),
|
||
value: numbers["1st"] || "—",
|
||
tone: "red",
|
||
border: "border-[#ffb8c3]",
|
||
text: "text-[#e5002c]",
|
||
wash: "from-[#fff4f6] to-white",
|
||
},
|
||
{
|
||
key: "2nd" as const,
|
||
label: t("results.grid.second"),
|
||
value: numbers["2nd"] || "—",
|
||
tone: "blue",
|
||
border: "border-[#b9ccf6]",
|
||
text: "text-[#0b56b7]",
|
||
wash: "from-[#f3f7ff] to-white",
|
||
},
|
||
{
|
||
key: "3rd" as const,
|
||
label: t("results.grid.third"),
|
||
value: numbers["3rd"] || "—",
|
||
tone: "green",
|
||
border: "border-[#bde7cc]",
|
||
text: "text-[#0a8f3e]",
|
||
wash: "from-[#f0fff5] to-white",
|
||
},
|
||
];
|
||
|
||
return (
|
||
<div className="flex flex-col gap-3">
|
||
<div className="grid grid-cols-3 gap-2">
|
||
{prizeCards.map((card) => (
|
||
<div
|
||
key={card.key}
|
||
className={cn(
|
||
"relative overflow-hidden rounded-xl border bg-gradient-to-b px-2 py-4 text-center shadow-[0_10px_24px_rgba(15,23,42,0.06)]",
|
||
card.border,
|
||
card.wash,
|
||
isHit(card.value) && "ring-2 ring-amber-300",
|
||
)}
|
||
>
|
||
<div className={cn("mx-auto flex size-9 items-center justify-center rounded-full text-white", card.tone === "red" ? "bg-[#e5002c]" : card.tone === "blue" ? "bg-[#0b56b7]" : "bg-[#0a8f3e]")}>
|
||
<Trophy className="size-5" />
|
||
</div>
|
||
<p className={cn("mt-3 text-xs font-black", card.text)}>{card.label}</p>
|
||
<p className={cn("mt-2 font-mono text-3xl font-black tabular-nums", card.text)}>{card.value}</p>
|
||
</div>
|
||
))}
|
||
</div>
|
||
|
||
<div className="rounded-xl border border-red-100 bg-white p-3 shadow-[0_8px_22px_rgba(15,23,42,0.05)]">
|
||
<p className="mb-3 flex items-center gap-2 text-sm font-black text-[#e5002c]">
|
||
<Trophy className="size-4" />
|
||
{t("results.grid.starter")}
|
||
</p>
|
||
<div className="grid grid-cols-5 gap-1.5">
|
||
{Array.from({ length: 10 }).map((_, i) => (
|
||
<div key={`s-${i}`} className={smallCellTone(starters[i] ?? "—", "red")}>
|
||
<span className="text-[11px] font-black">{i + 1}</span>
|
||
<span className="self-center font-mono text-xs font-semibold tabular-nums text-slate-700">
|
||
{starters[i] ?? "—"}
|
||
</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
|
||
<div className="rounded-xl border border-blue-100 bg-white p-3 shadow-[0_8px_22px_rgba(15,23,42,0.05)]">
|
||
<p className="mb-3 flex items-center gap-2 text-sm font-black text-[#0b56b7]">
|
||
<Trophy className="size-4" />
|
||
{t("results.grid.consolation")}
|
||
</p>
|
||
<div className="grid grid-cols-5 gap-1.5">
|
||
{Array.from({ length: 10 }).map((_, i) => (
|
||
<div key={`c-${i}`} className={smallCellTone(consos[i] ?? "—", "blue")}>
|
||
<span className="text-[11px] font-black">{i + 1}</span>
|
||
<span className="self-center font-mono text-xs font-semibold tabular-nums text-slate-700">
|
||
{consos[i] ?? "—"}
|
||
</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|