"use client"; import { useState } from "react"; import { Shield, Trash2 } from "lucide-react"; import type { Card, Rank, Suit } from "@/lib/minigames/poker"; import type { PokerSnapshot } from "@/app/(frontend)/minigames/poker/actions"; import { adjustPokerStack, setPokerBoardQueue, setPokerHoleQueue, } from "@/app/(frontend)/minigames/poker/actions"; import { Button } from "@/components/ui/button"; import { Combobox } from "@/components/ui/combobox"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, } from "@/components/ui/dialog"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@/components/ui/select"; const RANK_OPTIONS: readonly { value: Rank; label: string }[] = [ { value: 2, label: "2" }, { value: 3, label: "3" }, { value: 4, label: "4" }, { value: 5, label: "5" }, { value: 6, label: "6" }, { value: 7, label: "7" }, { value: 8, label: "8" }, { value: 9, label: "9" }, { value: 10, label: "10" }, { value: 11, label: "Jack" }, { value: 12, label: "Queen" }, { value: 13, label: "King" }, { value: 14, label: "Ace" }, ]; const SUIT_OPTIONS: readonly { value: Suit; label: string }[] = [ { value: "s", label: "Spades" }, { value: "h", label: "Hearts" }, { value: "d", label: "Diamonds" }, { value: "c", label: "Clubs" }, ]; function cardLabel(card: Card): string { const rank = RANK_OPTIONS.find((option) => option.value === card.rank)?.label ?? String(card.rank); const suit = SUIT_OPTIONS.find((option) => option.value === card.suit)?.label ?? card.suit; return `${rank} ${suit}`; } /** * Builds a complete card from one picker interaction: an empty selection * starts from the 2-of-spades baseline for the not-yet-chosen half, and any * already-chosen half is preserved. Returning a full card (never null) is * what lets a single pick land; the previous null-propagating version made * first picks no-ops and hole-card queuing impossible. */ export function applyCardPick(value: Card | null, part: "rank" | "suit", next: string): Card { if (part === "rank") { return { rank: Number(next) as Rank, suit: value?.suit ?? "s" }; } return { rank: value?.rank ?? 2, suit: next as Suit }; } function CardPicker({ label, value, onChange, }: { readonly label: string; readonly value: Card | null; readonly onChange: (card: Card | null) => void; }) { const setRank = (next: string) => onChange(applyCardPick(value, "rank", next)); const setSuit = (next: string) => onChange(applyCardPick(value, "suit", next)); return (
{label} ({ value: String(option.value), label: option.label, }))} />
); } export function PokerDevPanel({ tableId, view, }: { readonly tableId: string; readonly view: NonNullable; }) { const [open, setOpen] = useState(false); const [boardQueue, setBoardQueue] = useState([]); const [boardDraft, setBoardDraft] = useState(null); const [seatId, setSeatId] = useState(""); const [holeA, setHoleA] = useState(null); const [holeB, setHoleB] = useState(null); const [adjustSeat, setAdjustSeat] = useState(""); const [adjustAmount, setAdjustAmount] = useState(""); const [error, setError] = useState(null); const run = async (action: () => Promise<{ success: boolean; error?: string }>) => { const result = await action(); if (!result.success) setError(result.error ?? "The dev action failed."); else setError(null); }; const queueBoard = () => { if (boardQueue.length === 0) return; void run(async () => { const result = await setPokerBoardQueue(tableId, boardQueue); if (result.success) setBoardQueue([]); return result; }); }; const queueHole = () => { if (seatId === "" || !holeA || !holeB) return; void run(async () => { const result = await setPokerHoleQueue(tableId, Number(seatId), [holeA, holeB]); if (result.success) { setHoleA(null); setHoleB(null); } return result; }); }; const adjust = (sign: 1 | -1) => { if (adjustSeat === "") return; const amount = Number(adjustAmount); if (!Number.isInteger(amount) || amount <= 0) { setError("Enter a positive whole number of chips."); return; } void run(async () => { const result = await adjustPokerStack(tableId, Number(adjustSeat), sign * amount); if (result.success) setAdjustAmount(""); return result; }); }; const boardSlotsLeft = 5 - view.communityCards.length - boardQueue.length; return ( Developer controls Queue upcoming cards or adjust stacks. Queues apply at the next hand; board cards hit the felt in order. {error &&

{error}

}

Board queue

{boardQueue.length > 0 && (
    {boardQueue.map((card, index) => (
  • {index + 1}. {cardLabel(card)}
  • ))}
)}

Next hand hole cards

!player.eliminated) .map((player) => ({ value: String(player.seatId), label: player.name, }))} />

Stack adjustment

!player.eliminated) .map((player) => ({ value: String(player.seatId), label: `${player.name} (${player.stack.toLocaleString()})`, }))} />
setAdjustAmount(event.target.value)} className="h-8 w-28 text-xs" />
); }