feat: 增加 Jackpot 爆池实时弹层与奖池信息展示
This commit is contained in:
@@ -22,6 +22,7 @@ import {
|
|||||||
} from "@/features/hall/hall-bet-rules";
|
} from "@/features/hall/hall-bet-rules";
|
||||||
import type { HallDrawLiveSnapshot } from "@/features/hall/use-hall-draw-live";
|
import type { HallDrawLiveSnapshot } from "@/features/hall/use-hall-draw-live";
|
||||||
import { triggerWalletPollingAfterBet } from "@/hooks/use-wallet-polling";
|
import { triggerWalletPollingAfterBet } from "@/hooks/use-wallet-polling";
|
||||||
|
import { getLotteryEcho } from "@/lib/lottery-echo";
|
||||||
import { getLotteryRequestLocale } from "@/lib/lottery-locale";
|
import { getLotteryRequestLocale } from "@/lib/lottery-locale";
|
||||||
import { formatMinorAsCurrency, parseDecimalInputToMinor } from "@/lib/money";
|
import { formatMinorAsCurrency, parseDecimalInputToMinor } from "@/lib/money";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
@@ -63,6 +64,16 @@ type ClosedPlayCleanupData = {
|
|||||||
cleanup_lines?: Array<{ client_line_no?: number; play_code?: string }>;
|
cleanup_lines?: Array<{ client_line_no?: number; play_code?: string }>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type PlayToggleWsEvent = {
|
||||||
|
play_code?: string;
|
||||||
|
enabled?: boolean;
|
||||||
|
action?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type OddsUpdateWsEvent = {
|
||||||
|
message?: string;
|
||||||
|
};
|
||||||
|
|
||||||
type CellRiskState = "open" | "warning" | "sold_out";
|
type CellRiskState = "open" | "warning" | "sold_out";
|
||||||
type QuickFillState = Record<HallCategory, { favorites: string[]; history: string[] }>;
|
type QuickFillState = Record<HallCategory, { favorites: string[]; history: string[] }>;
|
||||||
|
|
||||||
@@ -482,6 +493,7 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
|
|||||||
);
|
);
|
||||||
|
|
||||||
const alertRows = display?.risk_pool_alerts ?? [];
|
const alertRows = display?.risk_pool_alerts ?? [];
|
||||||
|
const jackpot = display?.jackpot;
|
||||||
const currentQuickFill = quickFillState[activeCategory] ?? { favorites: [], history: [] };
|
const currentQuickFill = quickFillState[activeCategory] ?? { favorites: [], history: [] };
|
||||||
const favorites = currentQuickFill.favorites;
|
const favorites = currentQuickFill.favorites;
|
||||||
const historyNumbers = currentQuickFill.history;
|
const historyNumbers = currentQuickFill.history;
|
||||||
@@ -577,6 +589,79 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const clearAmountsForPlay = useCallback((playCode: string): boolean => {
|
||||||
|
let removed = false;
|
||||||
|
setRows((current) =>
|
||||||
|
current.map((row) => {
|
||||||
|
const nextAmounts = { ...row.amounts };
|
||||||
|
let changed = false;
|
||||||
|
Object.keys(nextAmounts).forEach((amountKey) => {
|
||||||
|
const keyPlayCode = amountKey.split("@")[0];
|
||||||
|
if (keyPlayCode !== playCode || !nextAmounts[amountKey]) return;
|
||||||
|
nextAmounts[amountKey] = "";
|
||||||
|
changed = true;
|
||||||
|
removed = true;
|
||||||
|
});
|
||||||
|
return changed ? { ...row, amounts: nextAmounts } : row;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
|
return removed;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const echo = getLotteryEcho();
|
||||||
|
if (!echo) return;
|
||||||
|
|
||||||
|
const channel = echo.channel("lottery-hall");
|
||||||
|
|
||||||
|
const onPlayToggle = (evt: PlayToggleWsEvent) => {
|
||||||
|
void loadCatalog();
|
||||||
|
if (evt.enabled === false && typeof evt.play_code === "string") {
|
||||||
|
const removed = clearAmountsForPlay(evt.play_code);
|
||||||
|
setPreviewOpen(false);
|
||||||
|
setPreviewData(null);
|
||||||
|
toast.warning(
|
||||||
|
removed
|
||||||
|
? t("hall.playConfig.playClosedDraftCleared", {
|
||||||
|
playCode: evt.play_code,
|
||||||
|
defaultValue: "{{playCode}} 已关闭,相关草稿金额已清除。",
|
||||||
|
})
|
||||||
|
: t("hall.playConfig.playClosed", {
|
||||||
|
playCode: evt.play_code,
|
||||||
|
defaultValue: "{{playCode}} 已关闭。",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
toast.message(
|
||||||
|
t("hall.playConfig.updated", {
|
||||||
|
defaultValue: "玩法配置已更新,已刷新下注表格。",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onOddsUpdate = (evt: OddsUpdateWsEvent) => {
|
||||||
|
void loadCatalog();
|
||||||
|
setPreviewOpen(false);
|
||||||
|
setPreviewData(null);
|
||||||
|
toast.message(
|
||||||
|
evt.message ??
|
||||||
|
t("hall.playConfig.oddsUpdated", {
|
||||||
|
defaultValue: "赔率已更新,请重新预览注单。",
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
channel.listen(".play.toggle", onPlayToggle);
|
||||||
|
channel.listen(".odds.update", onOddsUpdate);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
channel.stopListening(".play.toggle");
|
||||||
|
channel.stopListening(".odds.update");
|
||||||
|
};
|
||||||
|
}, [clearAmountsForPlay, loadCatalog, t]);
|
||||||
|
|
||||||
const collectEntries = useCallback((): DraftEntry[] => {
|
const collectEntries = useCallback((): DraftEntry[] => {
|
||||||
if (activeCategory === "JACKPOT") return [];
|
if (activeCategory === "JACKPOT") return [];
|
||||||
const entries: DraftEntry[] = [];
|
const entries: DraftEntry[] = [];
|
||||||
@@ -847,6 +932,29 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{jackpot?.enabled ? (
|
||||||
|
<div className="rounded-xl border border-amber-200 bg-gradient-to-r from-amber-50 via-white to-[#f8fbff] px-4 py-3">
|
||||||
|
<div className="flex flex-wrap items-center justify-between gap-2">
|
||||||
|
<div>
|
||||||
|
<p className="text-[11px] font-black uppercase tracking-normal text-amber-700">
|
||||||
|
{t("results.jackpotLabel", { defaultValue: "Jackpot" })}
|
||||||
|
</p>
|
||||||
|
<p className="font-mono text-xl font-black tabular-nums text-[#07459f]">
|
||||||
|
{formatMinorAsCurrency(jackpot.current_amount_minor, jackpot.currency_code)}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
{jackpot.draws_since_last_burst !== null ? (
|
||||||
|
<p className="rounded-full bg-amber-100 px-3 py-1 text-xs font-bold text-amber-800">
|
||||||
|
{t("results.jackpotGap", {
|
||||||
|
count: jackpot.draws_since_last_burst,
|
||||||
|
defaultValue: "距上次爆池 {{count}} 期",
|
||||||
|
})}
|
||||||
|
</p>
|
||||||
|
) : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
) : null}
|
||||||
|
|
||||||
{activeCategory === "JACKPOT" ? (
|
{activeCategory === "JACKPOT" ? (
|
||||||
<div className="rounded-xl border border-[#edf1f7] bg-[#f7f9fc] p-7 text-center text-slate-500">
|
<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">
|
<div className="mx-auto flex size-14 items-center justify-center rounded-full bg-slate-200 text-slate-600">
|
||||||
|
|||||||
@@ -9,7 +9,9 @@ import { LanguageSwitcher } from "@/components/language-switcher";
|
|||||||
import { HallBettingGrid } from "@/features/hall/hall-betting-grid";
|
import { HallBettingGrid } from "@/features/hall/hall-betting-grid";
|
||||||
import { HallDrawPanel } from "@/features/hall/hall-draw-panel";
|
import { HallDrawPanel } from "@/features/hall/hall-draw-panel";
|
||||||
import { HallWalletStrip } from "@/features/hall/hall-wallet-strip";
|
import { HallWalletStrip } from "@/features/hall/hall-wallet-strip";
|
||||||
|
import { JackpotBurstOverlay } from "@/features/hall/jackpot-burst-overlay";
|
||||||
import { useHallDrawLive } from "@/features/hall/use-hall-draw-live";
|
import { useHallDrawLive } from "@/features/hall/use-hall-draw-live";
|
||||||
|
import { useJackpotBurstLive } from "@/features/hall/use-jackpot-burst-live";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 下注大厅:钱包条 §4 + 当期期号 §4.2(封盘置灰 / 倒计时错误色 / WS+轮询);玩法目录 §12.3;下注表格 §13.3。
|
* 下注大厅:钱包条 §4 + 当期期号 §4.2(封盘置灰 / 倒计时错误色 / WS+轮询);玩法目录 §12.3;下注表格 §13.3。
|
||||||
@@ -18,6 +20,7 @@ export function HallScreen() {
|
|||||||
const { t } = useTranslation("common");
|
const { t } = useTranslation("common");
|
||||||
const { t: tp } = useTranslation("player");
|
const { t: tp } = useTranslation("player");
|
||||||
const drawLive = useHallDrawLive();
|
const drawLive = useHallDrawLive();
|
||||||
|
const { burstEvent, clearBurstEvent } = useJackpotBurstLive(tp);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="mx-auto w-full max-w-[480px]">
|
<div className="mx-auto w-full max-w-[480px]">
|
||||||
@@ -62,6 +65,7 @@ export function HallScreen() {
|
|||||||
|
|
||||||
<HallBettingGrid drawLive={drawLive} />
|
<HallBettingGrid drawLive={drawLive} />
|
||||||
</section>
|
</section>
|
||||||
|
<JackpotBurstOverlay event={burstEvent} onClose={clearBurstEvent} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
110
src/features/hall/jackpot-burst-overlay.tsx
Normal file
110
src/features/hall/jackpot-burst-overlay.tsx
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { X, Zap } from "lucide-react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import { formatMinorAsCurrency } from "@/lib/money";
|
||||||
|
|
||||||
|
export type JackpotBurstEvent = {
|
||||||
|
draw_id: number;
|
||||||
|
draw_no: string;
|
||||||
|
first_prize_number: string;
|
||||||
|
currency_code: string;
|
||||||
|
total_payout_amount: number;
|
||||||
|
winner_count: number;
|
||||||
|
trigger_type: string;
|
||||||
|
pool_amount_after: number;
|
||||||
|
emitted_at_ms?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
type JackpotBurstOverlayProps = {
|
||||||
|
event: JackpotBurstEvent | null;
|
||||||
|
onClose: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
function triggerLabel(triggerType: string, t: ReturnType<typeof useTranslation>["t"]) {
|
||||||
|
return t(`hall.jackpotBurst.trigger.${triggerType}`, {
|
||||||
|
defaultValue: triggerType,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function JackpotBurstOverlay({ event, onClose }: JackpotBurstOverlayProps) {
|
||||||
|
const { t } = useTranslation("player");
|
||||||
|
|
||||||
|
if (!event) return null;
|
||||||
|
|
||||||
|
const currency = event.currency_code.toUpperCase();
|
||||||
|
const amount = formatMinorAsCurrency(event.total_payout_amount, currency);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className="fixed inset-0 z-[90] flex items-center justify-center bg-[#08111f]/95 px-4 py-6 text-white"
|
||||||
|
role="dialog"
|
||||||
|
aria-modal="true"
|
||||||
|
aria-label={t("hall.jackpotBurst.title", { defaultValue: "Jackpot 爆池" })}
|
||||||
|
>
|
||||||
|
<div className="pointer-events-none absolute inset-0 overflow-hidden">
|
||||||
|
<div className="absolute left-0 top-1/4 h-px w-full animate-[pulse_1.8s_ease-in-out_infinite] bg-[#f5c542]" />
|
||||||
|
<div className="absolute bottom-1/3 left-0 h-px w-full animate-[pulse_2.4s_ease-in-out_infinite] bg-[#2dd4bf]" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="relative flex w-full max-w-[420px] flex-col items-center rounded-lg border border-[#f5c542]/50 bg-[#101b2d] px-5 py-6 text-center shadow-2xl shadow-black/40">
|
||||||
|
<Button
|
||||||
|
type="button"
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
className="absolute right-2 top-2 text-white hover:bg-white/10 hover:text-white"
|
||||||
|
onClick={onClose}
|
||||||
|
aria-label={t("hall.jackpotBurst.close", { defaultValue: "关闭" })}
|
||||||
|
>
|
||||||
|
<X className="size-4" aria-hidden />
|
||||||
|
</Button>
|
||||||
|
|
||||||
|
<div className="mb-4 flex size-14 items-center justify-center rounded-full border border-[#f5c542] bg-[#f5c542]/15 text-[#f5c542]">
|
||||||
|
<Zap className="size-7" aria-hidden />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<p className="text-xs font-bold uppercase tracking-[0.18em] text-[#f5c542]">
|
||||||
|
{t("hall.jackpotBurst.title", { defaultValue: "Jackpot 爆池" })}
|
||||||
|
</p>
|
||||||
|
<p className="mt-2 text-sm font-semibold text-slate-200">
|
||||||
|
{t("hall.jackpotBurst.subtitle", {
|
||||||
|
defaultValue: "期号 {{drawNo}} 触发奖池派发",
|
||||||
|
drawNo: event.draw_no,
|
||||||
|
})}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div className="my-5 w-full rounded-lg border border-white/10 bg-white/[0.06] px-4 py-4">
|
||||||
|
<div className="text-[2.35rem] font-black leading-none text-[#f5c542] sm:text-[2.8rem]">
|
||||||
|
{event.first_prize_number || "----"}
|
||||||
|
</div>
|
||||||
|
<p className="mt-2 text-xs font-semibold text-slate-300">
|
||||||
|
{t("hall.jackpotBurst.number", { defaultValue: "头奖号码" })}
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="w-full space-y-2 text-left text-sm">
|
||||||
|
<div className="flex items-center justify-between gap-4 rounded-md bg-white/[0.05] px-3 py-2">
|
||||||
|
<span className="text-slate-300">
|
||||||
|
{t("hall.jackpotBurst.amount", { defaultValue: "爆池金额" })}
|
||||||
|
</span>
|
||||||
|
<span className="text-right font-black text-[#f5c542]">{amount}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between gap-4 rounded-md bg-white/[0.05] px-3 py-2">
|
||||||
|
<span className="text-slate-300">
|
||||||
|
{t("hall.jackpotBurst.winners", { defaultValue: "中奖人数" })}
|
||||||
|
</span>
|
||||||
|
<span className="font-bold">{event.winner_count}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center justify-between gap-4 rounded-md bg-white/[0.05] px-3 py-2">
|
||||||
|
<span className="text-slate-300">
|
||||||
|
{t("hall.jackpotBurst.triggerLabel", { defaultValue: "触发方式" })}
|
||||||
|
</span>
|
||||||
|
<span className="font-bold">{triggerLabel(event.trigger_type, t)}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
76
src/features/hall/use-jackpot-burst-live.ts
Normal file
76
src/features/hall/use-jackpot-burst-live.ts
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { useCallback, useEffect, useState } from "react";
|
||||||
|
import type { TFunction } from "i18next";
|
||||||
|
|
||||||
|
import { getLotteryEcho } from "@/lib/lottery-echo";
|
||||||
|
import { formatMinorAsCurrency } from "@/lib/money";
|
||||||
|
import type { JackpotBurstEvent } from "@/features/hall/jackpot-burst-overlay";
|
||||||
|
|
||||||
|
function notifyBrowser(event: JackpotBurstEvent, t: TFunction<"player">): void {
|
||||||
|
if (typeof window === "undefined" || !("Notification" in window)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const currency = event.currency_code.toUpperCase();
|
||||||
|
const amount = formatMinorAsCurrency(event.total_payout_amount, currency);
|
||||||
|
const title = t("hall.jackpotBurst.notificationTitle", {
|
||||||
|
defaultValue: "Jackpot 爆池",
|
||||||
|
});
|
||||||
|
const body = t("hall.jackpotBurst.notificationBody", {
|
||||||
|
defaultValue: "期号 {{drawNo}} 派发 {{amount}}",
|
||||||
|
drawNo: event.draw_no,
|
||||||
|
amount,
|
||||||
|
});
|
||||||
|
|
||||||
|
const push = () => {
|
||||||
|
try {
|
||||||
|
new Notification(title, { body, tag: `jackpot-burst-${event.draw_id}` });
|
||||||
|
} catch {
|
||||||
|
// 浏览器通知失败不影响页面内爆池动画。
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if (Notification.permission === "granted") {
|
||||||
|
push();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Notification.permission === "default") {
|
||||||
|
void Notification.requestPermission().then((permission) => {
|
||||||
|
if (permission === "granted") {
|
||||||
|
push();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useJackpotBurstLive(t: TFunction<"player">) {
|
||||||
|
const [event, setEvent] = useState<JackpotBurstEvent | null>(null);
|
||||||
|
|
||||||
|
const handleBurst = useCallback(
|
||||||
|
(payload: JackpotBurstEvent) => {
|
||||||
|
setEvent(payload);
|
||||||
|
notifyBrowser(payload, t);
|
||||||
|
window.dispatchEvent(new Event("lottery-wallet-refresh"));
|
||||||
|
},
|
||||||
|
[t],
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const echo = getLotteryEcho();
|
||||||
|
if (!echo) return;
|
||||||
|
|
||||||
|
const channel = echo.channel("lottery-hall");
|
||||||
|
channel.listen(".jackpot.burst", handleBurst);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
channel.stopListening(".jackpot.burst");
|
||||||
|
};
|
||||||
|
}, [handleBurst]);
|
||||||
|
|
||||||
|
return {
|
||||||
|
burstEvent: event,
|
||||||
|
clearBurstEvent: () => setEvent(null),
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -16,6 +16,7 @@ export function JackpotResultsStrip({
|
|||||||
}: JackpotResultsStripProps) {
|
}: JackpotResultsStripProps) {
|
||||||
const { t } = useTranslation("player");
|
const { t } = useTranslation("player");
|
||||||
const [minor, setMinor] = useState<number | null>(null);
|
const [minor, setMinor] = useState<number | null>(null);
|
||||||
|
const [gap, setGap] = useState<number | null>(null);
|
||||||
const [enabled, setEnabled] = useState(false);
|
const [enabled, setEnabled] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -26,11 +27,13 @@ export function JackpotResultsStrip({
|
|||||||
if (!cancelled) {
|
if (!cancelled) {
|
||||||
setEnabled(j.enabled);
|
setEnabled(j.enabled);
|
||||||
setMinor(j.current_amount_minor);
|
setMinor(j.current_amount_minor);
|
||||||
|
setGap(j.draws_since_last_burst);
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
if (!cancelled) {
|
if (!cancelled) {
|
||||||
setEnabled(false);
|
setEnabled(false);
|
||||||
setMinor(null);
|
setMinor(null);
|
||||||
|
setGap(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})();
|
})();
|
||||||
@@ -51,6 +54,11 @@ export function JackpotResultsStrip({
|
|||||||
<p className="font-mono text-lg font-black tabular-nums text-[#0b3f96]">
|
<p className="font-mono text-lg font-black tabular-nums text-[#0b3f96]">
|
||||||
{formatMinorAsCurrency(minor, currencyCode.toUpperCase())}
|
{formatMinorAsCurrency(minor, currencyCode.toUpperCase())}
|
||||||
</p>
|
</p>
|
||||||
|
{gap !== null ? (
|
||||||
|
<p className="mt-1 text-xs font-semibold text-amber-800">
|
||||||
|
{t("results.jackpotGap", { count: gap, defaultValue: "距上次爆池 {{count}} 期" })}
|
||||||
|
</p>
|
||||||
|
) : null}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -102,6 +102,23 @@
|
|||||||
"changedBeforeSubmit": "Your draft changed before submission. Close the preview and try again.",
|
"changedBeforeSubmit": "Your draft changed before submission. Close the preview and try again.",
|
||||||
"placeFailed": "Submission failed",
|
"placeFailed": "Submission failed",
|
||||||
"placeSuccess": "Bet submitted. Order {{orderNo}}, deducted {{amount}}.",
|
"placeSuccess": "Bet submitted. Order {{orderNo}}, deducted {{amount}}.",
|
||||||
|
"jackpotBurst": {
|
||||||
|
"title": "Jackpot Burst",
|
||||||
|
"subtitle": "Issue {{drawNo}} triggered a pool payout",
|
||||||
|
"number": "First prize number",
|
||||||
|
"amount": "Burst amount",
|
||||||
|
"winners": "Winners",
|
||||||
|
"triggerLabel": "Trigger",
|
||||||
|
"close": "Close",
|
||||||
|
"notificationTitle": "Jackpot Burst",
|
||||||
|
"notificationBody": "Issue {{drawNo}} paid {{amount}}",
|
||||||
|
"trigger": {
|
||||||
|
"threshold": "Threshold reached",
|
||||||
|
"forced_gap": "Forced gap reached",
|
||||||
|
"play_combo": "Play combo matched",
|
||||||
|
"manual": "Manual burst"
|
||||||
|
}
|
||||||
|
},
|
||||||
"closed": {
|
"closed": {
|
||||||
"title": "Closed",
|
"title": "Closed",
|
||||||
"subtitle": "This issue is now closed.",
|
"subtitle": "This issue is now closed.",
|
||||||
@@ -362,6 +379,7 @@
|
|||||||
"hitHint": "If you win, numbers matched by your tickets are highlighted in gold (login required).",
|
"hitHint": "If you win, numbers matched by your tickets are highlighted in gold (login required).",
|
||||||
"viewMyWinning": "View my winning status",
|
"viewMyWinning": "View my winning status",
|
||||||
"jackpotLabel": "Jackpot",
|
"jackpotLabel": "Jackpot",
|
||||||
|
"jackpotGap": "{{count}} draws since last burst",
|
||||||
"tier": {
|
"tier": {
|
||||||
"first": "First prize",
|
"first": "First prize",
|
||||||
"second": "Second prize",
|
"second": "Second prize",
|
||||||
|
|||||||
@@ -102,6 +102,23 @@
|
|||||||
"changedBeforeSubmit": "पेश गर्नु अघि ड्राफ्ट परिवर्तन भयो। पूर्वावलोकन बन्द गरी फेरि प्रयास गर्नुहोस्।",
|
"changedBeforeSubmit": "पेश गर्नु अघि ड्राफ्ट परिवर्तन भयो। पूर्वावलोकन बन्द गरी फेरि प्रयास गर्नुहोस्।",
|
||||||
"placeFailed": "पेश गर्न असफल",
|
"placeFailed": "पेश गर्न असफल",
|
||||||
"placeSuccess": "बेट पेश भयो। अर्डर {{orderNo}}, कट्टा {{amount}}।",
|
"placeSuccess": "बेट पेश भयो। अर्डर {{orderNo}}, कट्टा {{amount}}।",
|
||||||
|
"jackpotBurst": {
|
||||||
|
"title": "Jackpot Burst",
|
||||||
|
"subtitle": "इश्यू {{drawNo}} मा पूल payout ट्रिगर भयो",
|
||||||
|
"number": "पहिलो पुरस्कार नम्बर",
|
||||||
|
"amount": "Burst रकम",
|
||||||
|
"winners": "विजेता",
|
||||||
|
"triggerLabel": "ट्रिगर",
|
||||||
|
"close": "बन्द",
|
||||||
|
"notificationTitle": "Jackpot Burst",
|
||||||
|
"notificationBody": "इश्यू {{drawNo}} ले {{amount}} payout गर्यो",
|
||||||
|
"trigger": {
|
||||||
|
"threshold": "थ्रेसहोल्ड पुगेको",
|
||||||
|
"forced_gap": "Forced gap पुगेको",
|
||||||
|
"play_combo": "Play combo मिलेको",
|
||||||
|
"manual": "Manual burst"
|
||||||
|
}
|
||||||
|
},
|
||||||
"closed": {
|
"closed": {
|
||||||
"title": "बन्द",
|
"title": "बन्द",
|
||||||
"subtitle": "यो इश्यू बन्द भएको छ।",
|
"subtitle": "यो इश्यू बन्द भएको छ।",
|
||||||
@@ -351,6 +368,7 @@
|
|||||||
"hitHint": "तपाईं जित्नुभयो भने, तपाईंका टिकटसँग मिलेका नम्बरहरू सुनौलो रंगमा देखिन्छन् (लगइन आवश्यक)।",
|
"hitHint": "तपाईं जित्नुभयो भने, तपाईंका टिकटसँग मिलेका नम्बरहरू सुनौलो रंगमा देखिन्छन् (लगइन आवश्यक)।",
|
||||||
"viewMyWinning": "मेरो जित स्थिति हेर्नुहोस्",
|
"viewMyWinning": "मेरो जित स्थिति हेर्नुहोस्",
|
||||||
"jackpotLabel": "Jackpot",
|
"jackpotLabel": "Jackpot",
|
||||||
|
"jackpotGap": "पछिल्लो burst देखि {{count}} draw",
|
||||||
"tier": {
|
"tier": {
|
||||||
"first": "पहिलो पुरस्कार",
|
"first": "पहिलो पुरस्कार",
|
||||||
"second": "दोस्रो पुरस्कार",
|
"second": "दोस्रो पुरस्कार",
|
||||||
|
|||||||
@@ -102,6 +102,23 @@
|
|||||||
"changedBeforeSubmit": "提交前数据已变化,请关闭预览后重试。",
|
"changedBeforeSubmit": "提交前数据已变化,请关闭预览后重试。",
|
||||||
"placeFailed": "提交失败",
|
"placeFailed": "提交失败",
|
||||||
"placeSuccess": "下注成功,订单号 {{orderNo}},实扣 {{amount}}。",
|
"placeSuccess": "下注成功,订单号 {{orderNo}},实扣 {{amount}}。",
|
||||||
|
"jackpotBurst": {
|
||||||
|
"title": "Jackpot 爆池",
|
||||||
|
"subtitle": "期号 {{drawNo}} 触发奖池派发",
|
||||||
|
"number": "头奖号码",
|
||||||
|
"amount": "爆池金额",
|
||||||
|
"winners": "中奖人数",
|
||||||
|
"triggerLabel": "触发方式",
|
||||||
|
"close": "关闭",
|
||||||
|
"notificationTitle": "Jackpot 爆池",
|
||||||
|
"notificationBody": "期号 {{drawNo}} 派发 {{amount}}",
|
||||||
|
"trigger": {
|
||||||
|
"threshold": "超过爆池阈值",
|
||||||
|
"forced_gap": "强制爆池期数",
|
||||||
|
"play_combo": "玩法组合命中",
|
||||||
|
"manual": "后台手动爆池"
|
||||||
|
}
|
||||||
|
},
|
||||||
"closed": {
|
"closed": {
|
||||||
"title": "已封盘",
|
"title": "已封盘",
|
||||||
"subtitle": "当前期已停止接收注单。",
|
"subtitle": "当前期已停止接收注单。",
|
||||||
@@ -362,6 +379,7 @@
|
|||||||
"hitHint": "如果您中奖,与注单匹配的号码将以金色高亮显示(需登录)。",
|
"hitHint": "如果您中奖,与注单匹配的号码将以金色高亮显示(需登录)。",
|
||||||
"viewMyWinning": "查看我的中奖情况",
|
"viewMyWinning": "查看我的中奖情况",
|
||||||
"jackpotLabel": "Jackpot",
|
"jackpotLabel": "Jackpot",
|
||||||
|
"jackpotGap": "距上次爆池 {{count}} 期",
|
||||||
"tier": {
|
"tier": {
|
||||||
"first": "头奖",
|
"first": "头奖",
|
||||||
"second": "二奖",
|
"second": "二奖",
|
||||||
|
|||||||
@@ -31,6 +31,14 @@ export type DrawCurrentPayload = {
|
|||||||
seconds_to_draw: number;
|
seconds_to_draw: number;
|
||||||
cooling_end_time: string | null;
|
cooling_end_time: string | null;
|
||||||
seconds_remaining_in_cooldown: number | null;
|
seconds_remaining_in_cooldown: number | null;
|
||||||
|
jackpot?: {
|
||||||
|
currency_code: string;
|
||||||
|
enabled: boolean;
|
||||||
|
current_amount_minor: number;
|
||||||
|
current_amount_formatted?: string;
|
||||||
|
draws_since_last_burst: number | null;
|
||||||
|
last_trigger_draw_id?: number | null;
|
||||||
|
};
|
||||||
risk_pool_alerts?: DrawCurrentRiskPoolAlert[];
|
risk_pool_alerts?: DrawCurrentRiskPoolAlert[];
|
||||||
result_items?: DrawCurrentResultItem[];
|
result_items?: DrawCurrentResultItem[];
|
||||||
result_version?: number;
|
result_version?: number;
|
||||||
|
|||||||
@@ -15,6 +15,14 @@ export type DrawResultListItem = {
|
|||||||
draw_time_iso?: string | null;
|
draw_time_iso?: string | null;
|
||||||
result_version: number;
|
result_version: number;
|
||||||
result_source: string | null;
|
result_source: string | null;
|
||||||
|
jackpot?: {
|
||||||
|
currency_code: string;
|
||||||
|
enabled: boolean;
|
||||||
|
current_amount_minor: number;
|
||||||
|
current_amount_formatted?: string;
|
||||||
|
draws_since_last_burst: number | null;
|
||||||
|
last_trigger_draw_id?: number | null;
|
||||||
|
};
|
||||||
results: DrawResultsNumbers;
|
results: DrawResultsNumbers;
|
||||||
result_items: Array<{
|
result_items: Array<{
|
||||||
prize_type: string;
|
prize_type: string;
|
||||||
|
|||||||
@@ -2,4 +2,7 @@ export type JackpotSummaryData = {
|
|||||||
currency_code: string;
|
currency_code: string;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
current_amount_minor: number;
|
current_amount_minor: number;
|
||||||
|
current_amount_formatted?: string;
|
||||||
|
draws_since_last_burst: number | null;
|
||||||
|
last_trigger_draw_id?: number | null;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user