From f46ccaac6e96757504470bb6ccfc5abafab36d13 Mon Sep 17 00:00:00 2001 From: kang Date: Wed, 24 Jun 2026 11:18:45 +0800 Subject: [PATCH] feat: add D4 play grouping with tab navigation and improve header layout - Introduced D4 play groups (big/small, position, combo, roll/straight, head/tail/odd/even, digit big/small) with tab-based filtering to improve betting grid usability - Updated PlayerPanel header grid layout from percentage-based to auto/1fr/auto columns with increased gap for better responsiveness - Removed "placed" status from ticket orders list filter options - Added D4 group translations in English, Nepali, and Chinese - Refactored play columns --- src/components/layout/player-panel.tsx | 2 +- src/features/hall/hall-betting-grid.tsx | 73 +++++++++++++++++-- .../orders/ticket-orders-list-screen.tsx | 1 - src/i18n/locales/en/player.json | 8 ++ src/i18n/locales/ne/player.json | 8 ++ src/i18n/locales/zh/player.json | 8 ++ 6 files changed, 90 insertions(+), 10 deletions(-) diff --git a/src/components/layout/player-panel.tsx b/src/components/layout/player-panel.tsx index caf28c0..8558f08 100644 --- a/src/components/layout/player-panel.tsx +++ b/src/components/layout/player-panel.tsx @@ -47,7 +47,7 @@ export function PlayerPanel({
diff --git a/src/features/hall/hall-betting-grid.tsx b/src/features/hall/hall-betting-grid.tsx index 80cd830..028b2de 100644 --- a/src/features/hall/hall-betting-grid.tsx +++ b/src/features/hall/hall-betting-grid.tsx @@ -135,6 +135,23 @@ const D4_PLAY_ORDER = [ "digit_small", ] 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, readonly string[]> = { D2: D2_PLAY_ORDER, D3: D3_PLAY_ORDER, @@ -446,6 +463,7 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot } const creditMode = isCreditFundingPlayer(usePlayerSessionStore((state) => state.profile)); const [activeCategory, setActiveCategory] = useState("D2"); + const [d4PlayGroup, setD4PlayGroup] = useState("big_small"); const [rows, setRows] = useState(() => [newDraftRow()]); const [activeRowId, setActiveRowId] = useState(null); const [catalogState, setCatalogState] = useState< @@ -557,11 +575,19 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot } ); }, [activeCategory, catalogState, openPlays]); - const playColumns = useMemo(() => { + const allPlayColumns = useMemo(() => { if (activeCategory === "JACKPOT") return []; return playColumnsForCategory(categoryPlays, activeCategory); }, [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 indexCol = 40; 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; }, [activeCategory, playColumns.length]); - const showWideTableHint = activeCategory === "D4" && playColumns.length > 8; + const showWideTableHint = activeCategory === "D4" && allPlayColumns.length > 8 && playColumns.length > 8; const activeRow = useMemo( () => rows.find((row) => row.id === activeRowId) ?? rows[0] ?? null, @@ -598,7 +624,7 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot } if (activeCategory !== "D4") return defaultNumberPlaceholder; const targetRow = activeRow ?? rows[0]; if (!targetRow) return defaultNumberPlaceholder; - const hasRollStake = playColumns.some((column) => { + const hasRollStake = allPlayColumns.some((column) => { if (column.play.play_code !== "roll") return false; const amount = parseDecimalInputToMinor(targetRow.amounts[column.key] ?? "", currencyCode); return amount !== null && amount > 0; @@ -609,11 +635,19 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot } activeRow, currencyCode, defaultNumberPlaceholder, - playColumns, + allPlayColumns, rows, 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) => { setRows((current) => current.map((row) => @@ -809,7 +843,7 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot } if (activeCategory === "JACKPOT") return []; const issues: DraftLineIssue[] = []; rows.forEach((row, rowIndex) => { - playColumns.forEach((column) => { + allPlayColumns.forEach((column) => { const amount = parseDecimalInputToMinor(row.amounts[column.key] ?? "", currencyCode); if (amount === null || amount <= 0) return; const reason = draftLineIssueReason(column.play.play_code, row.number, column.digitSlot); @@ -823,7 +857,7 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot } }); }); return issues; - }, [activeCategory, currencyCode, playColumns, rows]); + }, [activeCategory, currencyCode, allPlayColumns, rows]); const formatDraftLineIssue = useCallback( (issue: DraftLineIssue): string => { @@ -837,7 +871,7 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot } if (activeCategory === "JACKPOT") return []; const entries: DraftEntry[] = []; rows.forEach((row, rowIndex) => { - playColumns.forEach((column) => { + allPlayColumns.forEach((column) => { const amount = parseDecimalInputToMinor(row.amounts[column.key] ?? "", currencyCode); if (amount === null || amount <= 0) return; const line = lineForPlay(activeCategory, column.play, row.number, amount, column.digitSlot); @@ -855,7 +889,7 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot } }); }); return entries; - }, [activeCategory, currencyCode, playColumns, rows]); + }, [activeCategory, currencyCode, allPlayColumns, rows]); const draftEntries = collectEntries(); const draftSummary = useMemo(() => { @@ -1155,6 +1189,29 @@ export function HallBettingGrid({ drawLive }: { drawLive: HallDrawLiveSnapshot } + {activeCategory === "D4" && availableD4Groups.length > 1 ? ( +
+ {availableD4Groups.map((group) => { + const active = d4PlayGroup === group.id; + return ( + + ); + })} +
+ ) : null} + {jackpot?.enabled ? (
diff --git a/src/features/orders/ticket-orders-list-screen.tsx b/src/features/orders/ticket-orders-list-screen.tsx index 4139406..3e9794a 100644 --- a/src/features/orders/ticket-orders-list-screen.tsx +++ b/src/features/orders/ticket-orders-list-screen.tsx @@ -42,7 +42,6 @@ const ORDERS_PAGE_SIZE = 20; const STATUS_OPTIONS = [ "pending_confirm", "partial_pending_confirm", - "placed", "pending_draw", "partial_failed", "pending_payout", diff --git a/src/i18n/locales/en/player.json b/src/i18n/locales/en/player.json index 64608fa..134c0a4 100644 --- a/src/i18n/locales/en/player.json +++ b/src/i18n/locales/en/player.json @@ -213,6 +213,14 @@ "boxTitle": "Box", "boxDesc": "Multiply all by amount" }, + "d4Group": { + "big_small": "Big / Small", + "position": "Position", + "combo": "Combo", + "roll_straight": "Roll / Straight", + "head_tail_odd_even": "Head / Tail / Odd / Even", + "digit_big_small": "Digit Big / Small" + }, "table": { "no": "No.", "number": "Number", diff --git a/src/i18n/locales/ne/player.json b/src/i18n/locales/ne/player.json index f1bdc61..08eebab 100644 --- a/src/i18n/locales/ne/player.json +++ b/src/i18n/locales/ne/player.json @@ -213,6 +213,14 @@ "boxTitle": "Box", "boxDesc": "रकम सबैसँग गुणा गर्नुहोस्" }, + "d4Group": { + "big_small": "ठूलो / सानो", + "position": "स्थान", + "combo": "कम्बो", + "roll_straight": "रोल / स्ट्रेट", + "head_tail_odd_even": "टाउको / पुच्छर / बिजोर / जोर", + "digit_big_small": "डिजिट ठूलो / सानो" + }, "table": { "no": "नं.", "number": "नम्बर", diff --git a/src/i18n/locales/zh/player.json b/src/i18n/locales/zh/player.json index 9fbeed0..11cad7e 100644 --- a/src/i18n/locales/zh/player.json +++ b/src/i18n/locales/zh/player.json @@ -211,6 +211,14 @@ "boxTitle": "Box", "boxDesc": "按金额乘以全部组合" }, + "d4Group": { + "big_small": "大小盘", + "position": "位置", + "combo": "组合", + "roll_straight": "滚轮 / 直通", + "head_tail_odd_even": "头尾单双", + "digit_big_small": "各位大小" + }, "table": { "no": "序号", "number": "号码",