|
|
|
@@ -135,6 +135,23 @@ const D4_PLAY_ORDER = [
|
|
|
|
"digit_small",
|
|
|
|
"digit_small",
|
|
|
|
] as const;
|
|
|
|
] as const;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
type D4PlayGroupId =
|
|
|
|
|
|
|
|
| "big_small"
|
|
|
|
|
|
|
|
| "position"
|
|
|
|
|
|
|
|
| "combo"
|
|
|
|
|
|
|
|
| "roll_straight"
|
|
|
|
|
|
|
|
| "head_tail_odd_even"
|
|
|
|
|
|
|
|
| "digit_big_small";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const D4_PLAY_GROUPS: { id: D4PlayGroupId; labelKey: string; playCodes: readonly string[] }[] = [
|
|
|
|
|
|
|
|
{ id: "big_small", labelKey: "hall.d4Group.big_small", playCodes: ["big", "small"] },
|
|
|
|
|
|
|
|
{ id: "position", labelKey: "hall.d4Group.position", playCodes: ["pos_4a", "pos_4b", "pos_4c", "pos_4d", "pos_4e"] },
|
|
|
|
|
|
|
|
{ id: "combo", labelKey: "hall.d4Group.combo", playCodes: ["box", "ibox", "mbox"] },
|
|
|
|
|
|
|
|
{ id: "roll_straight", labelKey: "hall.d4Group.roll_straight", playCodes: ["roll", "straight"] },
|
|
|
|
|
|
|
|
{ id: "head_tail_odd_even", labelKey: "hall.d4Group.head_tail_odd_even", playCodes: ["head", "tail", "odd", "even"] },
|
|
|
|
|
|
|
|
{ id: "digit_big_small", labelKey: "hall.d4Group.digit_big_small", playCodes: ["digit_big", "digit_small"] },
|
|
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
const categoryPlayOrders: Record<Exclude<HallCategory, "JACKPOT">, readonly string[]> = {
|
|
|
|
const categoryPlayOrders: Record<Exclude<HallCategory, "JACKPOT">, readonly string[]> = {
|
|
|
|
D2: D2_PLAY_ORDER,
|
|
|
|
D2: D2_PLAY_ORDER,
|
|
|
|
D3: D3_PLAY_ORDER,
|
|
|
|
D3: D3_PLAY_ORDER,
|
|
|
|
@@ -446,6 +463,7 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
|
|
|
|
const creditMode = isCreditFundingPlayer(usePlayerSessionStore((state) => state.profile));
|
|
|
|
const creditMode = isCreditFundingPlayer(usePlayerSessionStore((state) => state.profile));
|
|
|
|
|
|
|
|
|
|
|
|
const [activeCategory, setActiveCategory] = useState<HallCategory>("D2");
|
|
|
|
const [activeCategory, setActiveCategory] = useState<HallCategory>("D2");
|
|
|
|
|
|
|
|
const [d4PlayGroup, setD4PlayGroup] = useState<D4PlayGroupId>("big_small");
|
|
|
|
const [rows, setRows] = useState<DraftRow[]>(() => [newDraftRow()]);
|
|
|
|
const [rows, setRows] = useState<DraftRow[]>(() => [newDraftRow()]);
|
|
|
|
const [activeRowId, setActiveRowId] = useState<string | null>(null);
|
|
|
|
const [activeRowId, setActiveRowId] = useState<string | null>(null);
|
|
|
|
const [catalogState, setCatalogState] = useState<
|
|
|
|
const [catalogState, setCatalogState] = useState<
|
|
|
|
@@ -557,11 +575,19 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
|
|
|
|
);
|
|
|
|
);
|
|
|
|
}, [activeCategory, catalogState, openPlays]);
|
|
|
|
}, [activeCategory, catalogState, openPlays]);
|
|
|
|
|
|
|
|
|
|
|
|
const playColumns = useMemo(() => {
|
|
|
|
const allPlayColumns = useMemo(() => {
|
|
|
|
if (activeCategory === "JACKPOT") return [];
|
|
|
|
if (activeCategory === "JACKPOT") return [];
|
|
|
|
return playColumnsForCategory(categoryPlays, activeCategory);
|
|
|
|
return playColumnsForCategory(categoryPlays, activeCategory);
|
|
|
|
}, [activeCategory, categoryPlays]);
|
|
|
|
}, [activeCategory, categoryPlays]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const playColumns = useMemo(() => {
|
|
|
|
|
|
|
|
if (activeCategory !== "D4") return allPlayColumns;
|
|
|
|
|
|
|
|
const group = D4_PLAY_GROUPS.find((g) => g.id === d4PlayGroup);
|
|
|
|
|
|
|
|
if (!group) return allPlayColumns;
|
|
|
|
|
|
|
|
const allowedCodes = new Set(group.playCodes);
|
|
|
|
|
|
|
|
return allPlayColumns.filter((col) => allowedCodes.has(col.play.play_code));
|
|
|
|
|
|
|
|
}, [activeCategory, allPlayColumns, d4PlayGroup]);
|
|
|
|
|
|
|
|
|
|
|
|
const tableMinWidthPx = useMemo(() => {
|
|
|
|
const tableMinWidthPx = useMemo(() => {
|
|
|
|
const indexCol = 40;
|
|
|
|
const indexCol = 40;
|
|
|
|
const numberCol = activeCategory === "D4" ? 112 : activeCategory === "D3" ? 88 : 72;
|
|
|
|
const numberCol = activeCategory === "D4" ? 112 : activeCategory === "D3" ? 88 : 72;
|
|
|
|
@@ -570,7 +596,7 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
|
|
|
|
return indexCol + numberCol + playColumns.length * amountCol + deleteCol;
|
|
|
|
return indexCol + numberCol + playColumns.length * amountCol + deleteCol;
|
|
|
|
}, [activeCategory, playColumns.length]);
|
|
|
|
}, [activeCategory, playColumns.length]);
|
|
|
|
|
|
|
|
|
|
|
|
const showWideTableHint = activeCategory === "D4" && playColumns.length > 8;
|
|
|
|
const showWideTableHint = activeCategory === "D4" && allPlayColumns.length > 8 && playColumns.length > 8;
|
|
|
|
|
|
|
|
|
|
|
|
const activeRow = useMemo(
|
|
|
|
const activeRow = useMemo(
|
|
|
|
() => rows.find((row) => row.id === activeRowId) ?? rows[0] ?? null,
|
|
|
|
() => rows.find((row) => row.id === activeRowId) ?? rows[0] ?? null,
|
|
|
|
@@ -598,7 +624,7 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
|
|
|
|
if (activeCategory !== "D4") return defaultNumberPlaceholder;
|
|
|
|
if (activeCategory !== "D4") return defaultNumberPlaceholder;
|
|
|
|
const targetRow = activeRow ?? rows[0];
|
|
|
|
const targetRow = activeRow ?? rows[0];
|
|
|
|
if (!targetRow) return defaultNumberPlaceholder;
|
|
|
|
if (!targetRow) return defaultNumberPlaceholder;
|
|
|
|
const hasRollStake = playColumns.some((column) => {
|
|
|
|
const hasRollStake = allPlayColumns.some((column) => {
|
|
|
|
if (column.play.play_code !== "roll") return false;
|
|
|
|
if (column.play.play_code !== "roll") return false;
|
|
|
|
const amount = parseDecimalInputToMinor(targetRow.amounts[column.key] ?? "", currencyCode);
|
|
|
|
const amount = parseDecimalInputToMinor(targetRow.amounts[column.key] ?? "", currencyCode);
|
|
|
|
return amount !== null && amount > 0;
|
|
|
|
return amount !== null && amount > 0;
|
|
|
|
@@ -609,11 +635,19 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
|
|
|
|
activeRow,
|
|
|
|
activeRow,
|
|
|
|
currencyCode,
|
|
|
|
currencyCode,
|
|
|
|
defaultNumberPlaceholder,
|
|
|
|
defaultNumberPlaceholder,
|
|
|
|
playColumns,
|
|
|
|
allPlayColumns,
|
|
|
|
rows,
|
|
|
|
rows,
|
|
|
|
t,
|
|
|
|
t,
|
|
|
|
]);
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const availableD4Groups = useMemo(() => {
|
|
|
|
|
|
|
|
if (activeCategory !== "D4") return [];
|
|
|
|
|
|
|
|
return D4_PLAY_GROUPS.filter((group) => {
|
|
|
|
|
|
|
|
const allowedCodes = new Set(group.playCodes);
|
|
|
|
|
|
|
|
return allPlayColumns.some((col) => allowedCodes.has(col.play.play_code));
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
}, [activeCategory, allPlayColumns]);
|
|
|
|
|
|
|
|
|
|
|
|
const updateRowNumber = (id: string, value: string) => {
|
|
|
|
const updateRowNumber = (id: string, value: string) => {
|
|
|
|
setRows((current) =>
|
|
|
|
setRows((current) =>
|
|
|
|
current.map((row) =>
|
|
|
|
current.map((row) =>
|
|
|
|
@@ -809,7 +843,7 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
|
|
|
|
if (activeCategory === "JACKPOT") return [];
|
|
|
|
if (activeCategory === "JACKPOT") return [];
|
|
|
|
const issues: DraftLineIssue[] = [];
|
|
|
|
const issues: DraftLineIssue[] = [];
|
|
|
|
rows.forEach((row, rowIndex) => {
|
|
|
|
rows.forEach((row, rowIndex) => {
|
|
|
|
playColumns.forEach((column) => {
|
|
|
|
allPlayColumns.forEach((column) => {
|
|
|
|
const amount = parseDecimalInputToMinor(row.amounts[column.key] ?? "", currencyCode);
|
|
|
|
const amount = parseDecimalInputToMinor(row.amounts[column.key] ?? "", currencyCode);
|
|
|
|
if (amount === null || amount <= 0) return;
|
|
|
|
if (amount === null || amount <= 0) return;
|
|
|
|
const reason = draftLineIssueReason(column.play.play_code, row.number, column.digitSlot);
|
|
|
|
const reason = draftLineIssueReason(column.play.play_code, row.number, column.digitSlot);
|
|
|
|
@@ -823,7 +857,7 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
return issues;
|
|
|
|
return issues;
|
|
|
|
}, [activeCategory, currencyCode, playColumns, rows]);
|
|
|
|
}, [activeCategory, currencyCode, allPlayColumns, rows]);
|
|
|
|
|
|
|
|
|
|
|
|
const formatDraftLineIssue = useCallback(
|
|
|
|
const formatDraftLineIssue = useCallback(
|
|
|
|
(issue: DraftLineIssue): string => {
|
|
|
|
(issue: DraftLineIssue): string => {
|
|
|
|
@@ -837,7 +871,7 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
|
|
|
|
if (activeCategory === "JACKPOT") return [];
|
|
|
|
if (activeCategory === "JACKPOT") return [];
|
|
|
|
const entries: DraftEntry[] = [];
|
|
|
|
const entries: DraftEntry[] = [];
|
|
|
|
rows.forEach((row, rowIndex) => {
|
|
|
|
rows.forEach((row, rowIndex) => {
|
|
|
|
playColumns.forEach((column) => {
|
|
|
|
allPlayColumns.forEach((column) => {
|
|
|
|
const amount = parseDecimalInputToMinor(row.amounts[column.key] ?? "", currencyCode);
|
|
|
|
const amount = parseDecimalInputToMinor(row.amounts[column.key] ?? "", currencyCode);
|
|
|
|
if (amount === null || amount <= 0) return;
|
|
|
|
if (amount === null || amount <= 0) return;
|
|
|
|
const line = lineForPlay(activeCategory, column.play, row.number, amount, column.digitSlot);
|
|
|
|
const line = lineForPlay(activeCategory, column.play, row.number, amount, column.digitSlot);
|
|
|
|
@@ -855,7 +889,7 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
return entries;
|
|
|
|
return entries;
|
|
|
|
}, [activeCategory, currencyCode, playColumns, rows]);
|
|
|
|
}, [activeCategory, currencyCode, allPlayColumns, rows]);
|
|
|
|
|
|
|
|
|
|
|
|
const draftEntries = collectEntries();
|
|
|
|
const draftEntries = collectEntries();
|
|
|
|
const draftSummary = useMemo(() => {
|
|
|
|
const draftSummary = useMemo(() => {
|
|
|
|
@@ -1155,6 +1189,29 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot }
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
{activeCategory === "D4" && availableD4Groups.length > 1 ? (
|
|
|
|
|
|
|
|
<div className="flex gap-1 overflow-x-auto overscroll-x-contain pb-0.5">
|
|
|
|
|
|
|
|
{availableD4Groups.map((group) => {
|
|
|
|
|
|
|
|
const active = d4PlayGroup === group.id;
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
|
|
|
<button
|
|
|
|
|
|
|
|
key={group.id}
|
|
|
|
|
|
|
|
type="button"
|
|
|
|
|
|
|
|
onClick={() => setD4PlayGroup(group.id)}
|
|
|
|
|
|
|
|
className={cn(
|
|
|
|
|
|
|
|
"shrink-0 rounded-full px-3 py-1.5 text-xs font-bold transition-colors",
|
|
|
|
|
|
|
|
active
|
|
|
|
|
|
|
|
? "bg-[#07459f] text-white"
|
|
|
|
|
|
|
|
: "border border-[#dce7f7] bg-white text-[#32518d] hover:bg-[#f4f7fb]",
|
|
|
|
|
|
|
|
)}
|
|
|
|
|
|
|
|
>
|
|
|
|
|
|
|
|
{t(group.labelKey)}
|
|
|
|
|
|
|
|
</button>
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
})}
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
) : null}
|
|
|
|
|
|
|
|
|
|
|
|
{jackpot?.enabled ? (
|
|
|
|
{jackpot?.enabled ? (
|
|
|
|
<div className="rounded-xl border border-amber-200 bg-gradient-to-r from-amber-50 via-white to-[#f8fbff] px-3 py-2.5">
|
|
|
|
<div className="rounded-xl border border-amber-200 bg-gradient-to-r from-amber-50 via-white to-[#f8fbff] px-3 py-2.5">
|
|
|
|
<div className="flex flex-wrap items-center justify-between gap-2">
|
|
|
|
<div className="flex flex-wrap items-center justify-between gap-2">
|
|
|
|
|