feat: 优化大厅 Jackpot 参与提示与玩家页头布局
This commit is contained in:
@@ -39,12 +39,12 @@ export function PlayerPanel({
|
||||
<div className={cn("mx-auto w-full max-w-[480px]", containerClassName)}>
|
||||
<section
|
||||
className={cn(
|
||||
"overflow-hidden bg-white text-slate-900",
|
||||
playerPageInset,
|
||||
"bg-white text-slate-900 min-h-screen",
|
||||
"px-3 pb-6",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<header className={playerPageHeader}>
|
||||
<header className={cn(playerPageHeader, "sticky top-0 z-50 bg-white/95 backdrop-blur pt-2 pb-2 -mx-3 px-3")}>
|
||||
<div className="flex min-w-0 justify-start">
|
||||
<Link
|
||||
href={backHref}
|
||||
|
||||
@@ -31,6 +31,7 @@ type HallBetPreviewDialogProps = {
|
||||
currencyCode: string;
|
||||
data: TicketPreviewData | null;
|
||||
placing: boolean;
|
||||
jackpotEnabled?: boolean;
|
||||
/** 界面 §4.2:封盘后禁止提交,主按钮文案为「已封盘」 */
|
||||
allowSubmit?: boolean;
|
||||
onConfirmPlace: () => void;
|
||||
@@ -107,6 +108,7 @@ export function HallBetPreviewDialog({
|
||||
currencyCode,
|
||||
data,
|
||||
placing,
|
||||
jackpotEnabled = false,
|
||||
allowSubmit = true,
|
||||
onConfirmPlace,
|
||||
}: HallBetPreviewDialogProps) {
|
||||
@@ -188,6 +190,16 @@ export function HallBetPreviewDialog({
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{jackpotEnabled ? (
|
||||
<Alert className="border-[#d9ecff] bg-[#f4f9ff] text-[#0b3f96]">
|
||||
<CheckCircle2 className="size-4" />
|
||||
<AlertTitle>{t("hall.jackpotParticipation.title")}</AlertTitle>
|
||||
<AlertDescription className="text-xs leading-relaxed">
|
||||
{t("hall.jackpotParticipation.previewDescription")}
|
||||
</AlertDescription>
|
||||
</Alert>
|
||||
) : null}
|
||||
|
||||
<div className="overflow-x-auto rounded-xl border border-[#dfe8f6]">
|
||||
<table className="min-w-[640px] w-full border-collapse text-xs">
|
||||
<thead className="bg-[#f4f7fd] text-[#304f86]">
|
||||
|
||||
@@ -21,6 +21,7 @@ type HallBetResultDialogProps = {
|
||||
onOpenChange: (open: boolean) => void;
|
||||
currencyCode: string;
|
||||
data: TicketPlaceData | null;
|
||||
jackpotEnabled?: boolean;
|
||||
};
|
||||
|
||||
export function HallBetResultDialog({
|
||||
@@ -28,6 +29,7 @@ export function HallBetResultDialog({
|
||||
onOpenChange,
|
||||
currencyCode,
|
||||
data,
|
||||
jackpotEnabled = false,
|
||||
}: HallBetResultDialogProps) {
|
||||
const { t } = useTranslation("player");
|
||||
|
||||
@@ -119,6 +121,13 @@ export function HallBetResultDialog({
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{jackpotEnabled ? (
|
||||
<div className="rounded-lg border border-[#d9ecff] bg-[#f4f9ff] px-3 py-3 text-sm leading-relaxed text-[#0b3f96]">
|
||||
<p className="font-black">{t("hall.jackpotParticipation.title")}</p>
|
||||
<p className="mt-1 text-xs">{t("hall.jackpotParticipation.resultDescription")}</p>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
<div className="space-y-2">
|
||||
<p className="text-sm font-black text-[#e5002c]">
|
||||
{t("hall.result.items")}
|
||||
|
||||
@@ -84,7 +84,6 @@ const categoryTabs: { value: HallCategory; label: string }[] = [
|
||||
{ value: "D2", label: "2D" },
|
||||
{ value: "D3", label: "3D" },
|
||||
{ value: "D4", label: "4D" },
|
||||
{ value: "JACKPOT", label: "Jackpot" },
|
||||
];
|
||||
|
||||
const D2_PLAY_ORDER = ["pos_2a", "pos_2b", "pos_2c", "pos_2abc"] as const;
|
||||
@@ -496,8 +495,25 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
|
||||
const currentQuickFill = quickFillState[activeCategory] ?? { favorites: [], history: [] };
|
||||
const favorites = currentQuickFill.favorites;
|
||||
const historyNumbers = currentQuickFill.history;
|
||||
const jackpotPanelCopy = !jackpot?.enabled
|
||||
? {
|
||||
title: t("hall.jackpotPanel.disabledTitle"),
|
||||
subtitle: t("hall.jackpotPanel.disabledSubtitle"),
|
||||
description: t("hall.jackpotPanel.disabledDescription"),
|
||||
}
|
||||
: isBettable
|
||||
? {
|
||||
title: t("hall.jackpotPanel.infoTitle"),
|
||||
subtitle: t("hall.jackpotPanel.infoSubtitle"),
|
||||
description: t("hall.jackpotPanel.infoDescription"),
|
||||
}
|
||||
: {
|
||||
title: t("hall.jackpotPanel.closedInfoTitle"),
|
||||
subtitle: t("hall.jackpotPanel.closedInfoSubtitle"),
|
||||
description: t("hall.jackpotPanel.closedInfoDescription"),
|
||||
};
|
||||
|
||||
const tableDisabled = activeCategory === "JACKPOT" || !isBettable || catalogState.kind !== "ok";
|
||||
const tableDisabled = !isBettable || catalogState.kind !== "ok";
|
||||
const sealedBetUi = Boolean(display && isHallSealedCountdownUi(display.status));
|
||||
const numberPlaceholder = activeCategory === "D2" ? "00" : activeCategory === "D3" ? "000" : "0000";
|
||||
|
||||
@@ -888,7 +904,7 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
|
||||
<>
|
||||
<section className="space-y-3" aria-label={t("hall.aria")}>
|
||||
<div className="rounded-xl border border-[#e8eef7] bg-white p-1 shadow-[0_6px_18px_rgba(30,64,175,0.06)]">
|
||||
<div className="grid grid-cols-4 gap-1">
|
||||
<div className="grid grid-cols-3 gap-1">
|
||||
{categoryTabs.map((tab) => {
|
||||
const active = activeCategory === tab.value;
|
||||
return (
|
||||
@@ -941,33 +957,18 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{activeCategory === "JACKPOT" ? (
|
||||
<div className="rounded-xl border border-[#edf1f7] bg-[#f7f9fc] p-7 text-center text-slate-500">
|
||||
<div className="mx-auto flex size-14 items-center justify-center rounded-full bg-slate-200 text-slate-600">
|
||||
<Ticket className="size-7" aria-hidden />
|
||||
<div className="rounded-xl border border-[#e3ebf7] bg-white px-3 py-3 shadow-[0_8px_24px_rgba(15,23,42,0.045)]">
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="min-w-0">
|
||||
<p className="text-sm font-bold leading-5 text-slate-950">
|
||||
{t("hall.quickFill.title")}
|
||||
</p>
|
||||
<p className="mt-0.5 text-xs leading-5 text-slate-500">
|
||||
{t("hall.quickFill.description")}
|
||||
</p>
|
||||
</div>
|
||||
<p className="mt-3 text-lg font-bold text-slate-900">
|
||||
{t("hall.closed.title")}
|
||||
</p>
|
||||
<p className="mt-1 text-xs">{t("hall.closed.subtitle")}</p>
|
||||
<div className="mt-5 rounded-lg border border-[#cbdcf7] bg-white px-3 py-3 text-left text-xs text-[#315a9f]">
|
||||
{t("hall.closed.description")}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="rounded-xl border border-[#e3ebf7] bg-white px-3 py-3 shadow-[0_8px_24px_rgba(15,23,42,0.045)]">
|
||||
<div className="flex items-start justify-between gap-3">
|
||||
<div className="min-w-0">
|
||||
<p className="text-sm font-bold leading-5 text-slate-950">
|
||||
{t("hall.quickFill.title")}
|
||||
</p>
|
||||
<p className="mt-0.5 text-xs leading-5 text-slate-500">
|
||||
{t("hall.quickFill.description")}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex shrink-0 items-center gap-1.5">
|
||||
{activeRow?.number ? (
|
||||
<div className="flex shrink-0 items-center gap-1.5">
|
||||
{activeRow?.number ? (
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
@@ -1275,8 +1276,6 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
|
||||
? t("hall.table.insufficientBalance")
|
||||
: t("hall.table.submitBet")}
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</section>
|
||||
|
||||
<HallBetPreviewDialog
|
||||
@@ -1288,6 +1287,7 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
|
||||
currencyCode={currencyCode}
|
||||
data={previewData}
|
||||
placing={placeLoading}
|
||||
jackpotEnabled={Boolean(jackpot?.enabled)}
|
||||
allowSubmit={isBettable && availableMinor >= debouncedSummary.actual}
|
||||
onConfirmPlace={() => void handlePlace()}
|
||||
/>
|
||||
@@ -1300,6 +1300,7 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
|
||||
}}
|
||||
currencyCode={currencyCode}
|
||||
data={resultData}
|
||||
jackpotEnabled={Boolean(jackpot?.enabled)}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -135,6 +135,22 @@
|
||||
"subtitle": "This issue is now closed.",
|
||||
"description": "The betting window has closed. Please wait for the next issue to place your bets."
|
||||
},
|
||||
"jackpotPanel": {
|
||||
"disabledTitle": "Unavailable",
|
||||
"disabledSubtitle": "Jackpot is currently disabled.",
|
||||
"disabledDescription": "The admin has turned off Jackpot for now. This tab remains visible for information only.",
|
||||
"infoTitle": "Pool Information",
|
||||
"infoSubtitle": "Jackpot is currently enabled.",
|
||||
"infoDescription": "Jackpot is not a standalone play. Eligible tickets placed in the active plays will join the pool automatically based on admin rules.",
|
||||
"closedInfoTitle": "Pool Information",
|
||||
"closedInfoSubtitle": "Jackpot is currently enabled.",
|
||||
"closedInfoDescription": "Jackpot uses automatic participation for eligible tickets. This issue is closed, so please wait for the next issue."
|
||||
},
|
||||
"jackpotParticipation": {
|
||||
"title": "Automatic Jackpot Participation",
|
||||
"previewDescription": "If this ticket is submitted successfully and meets the configured rules, it will join the Jackpot pool automatically. No separate Jackpot bet is needed.",
|
||||
"resultDescription": "The successful ticket lines in this order will join the Jackpot pool automatically under the configured rules. If a first-prize hit later triggers the pool, it will settle together with the normal payout."
|
||||
},
|
||||
"boxMode": {
|
||||
"iboxTitle": "iBox",
|
||||
"iboxDesc": "Divide all by amount",
|
||||
|
||||
@@ -135,6 +135,22 @@
|
||||
"subtitle": "यो इश्यू बन्द भएको छ।",
|
||||
"description": "बेटिङ समय बन्द भएको छ। अर्को इश्यू पर्खेर बेट राख्नुहोस्।"
|
||||
},
|
||||
"jackpotPanel": {
|
||||
"disabledTitle": "बन्द गरिएको छ",
|
||||
"disabledSubtitle": "Jackpot अहिले सक्षम छैन।",
|
||||
"disabledDescription": "एडमिनले Jackpot बन्द गरेको छ। यो ट्याब अहिले जानकारीका लागि मात्र देखाइन्छ।",
|
||||
"infoTitle": "पूल जानकारी",
|
||||
"infoSubtitle": "Jackpot अहिले सक्षम छ।",
|
||||
"infoDescription": "Jackpot छुट्टै बेट होइन। उपलब्ध खेलमा गरिएको योग्य टिकटहरू एडमिन नियमअनुसार स्वतः Jackpot पूलमा सहभागी हुन्छन्।",
|
||||
"closedInfoTitle": "पूल जानकारी",
|
||||
"closedInfoSubtitle": "Jackpot अहिले सक्षम छ।",
|
||||
"closedInfoDescription": "योग्य टिकटहरू स्वतः Jackpot मा सहभागी हुन्छन्। यो इश्यू बन्द भइसकेको छ, त्यसैले अर्को इश्यू पर्खनुहोस्।"
|
||||
},
|
||||
"jackpotParticipation": {
|
||||
"title": "Jackpot स्वतः सहभागिता",
|
||||
"previewDescription": "यदि यो टिकट सफलतापूर्वक पेश भयो र नियम पूरा गर्यो भने, यो स्वतः Jackpot पूलमा सहभागी हुनेछ। छुट्टै Jackpot बेट आवश्यक छैन।",
|
||||
"resultDescription": "यस अर्डरका सफल टिकटहरू कन्फिगर गरिएको नियमअनुसार स्वतः Jackpot पूलमा सहभागी हुनेछन्। पछि पहिलो पुरस्कार र पूल ट्रिगर सर्त पूरा भएमा, नियमित payout सँगै सेटल हुनेछ।"
|
||||
},
|
||||
"boxMode": {
|
||||
"iboxTitle": "iBox",
|
||||
"iboxDesc": "रकम सबैमा बाँड्नुहोस्",
|
||||
|
||||
@@ -135,6 +135,22 @@
|
||||
"subtitle": "当前期已停止接收注单。",
|
||||
"description": "下注窗口已关闭,请等待下一期再下注。"
|
||||
},
|
||||
"jackpotPanel": {
|
||||
"disabledTitle": "已关闭",
|
||||
"disabledSubtitle": "Jackpot 当前未启用。",
|
||||
"disabledDescription": "后台已关闭 Jackpot 功能,当前仅保留展示页签。",
|
||||
"infoTitle": "奖池信息",
|
||||
"infoSubtitle": "Jackpot 当前已启用。",
|
||||
"infoDescription": "Jackpot 不是单独下注玩法。玩家在开放玩法中提交有效注单后,将按后台规则自动参与奖池。",
|
||||
"closedInfoTitle": "奖池信息",
|
||||
"closedInfoSubtitle": "Jackpot 当前已启用。",
|
||||
"closedInfoDescription": "Jackpot 采用有效注单自动参与机制。本期下注窗口已关闭,请等待下一期开放后参与。"
|
||||
},
|
||||
"jackpotParticipation": {
|
||||
"title": "Jackpot 自动参与",
|
||||
"previewDescription": "若本单成功提交且满足后台规则,系统会自动按有效注单参与 Jackpot 奖池,无需单独再下一笔。",
|
||||
"resultDescription": "本次成功注项将按后台规则自动参与 Jackpot 奖池,后续若命中头奖并满足触发条件,将与常规派彩一并结算。"
|
||||
},
|
||||
"boxMode": {
|
||||
"iboxTitle": "iBox",
|
||||
"iboxDesc": "按金额分摊全部组合",
|
||||
|
||||
Reference in New Issue
Block a user