Files
lotteryFront/src/features/results/twenty-three-results-grid.tsx
kang 0cd85ae287 feat: enhance UI consistency and improve spacing across components
- 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.
2026-05-21 17:28:06 +08:00

132 lines
4.6 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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>
);
}