"use client"; import { useEffect, useState } from "react"; import type { PokerActionKind, PokerLegalActions } from "@/lib/minigames/poker"; import { pokerAction, type PokerSnapshot } from "@/app/(frontend)/minigames/poker/actions"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; interface BettingControlsProps { readonly tableId: string; readonly snapshotVersion: number; readonly legalActions: PokerLegalActions; readonly potTotal: number; readonly yourTurn: boolean; readonly onSnapshot: (snapshot: PokerSnapshot) => void; } function clampAmount(value: number, min: number, max: number): number { if (!Number.isFinite(value)) return min; return Math.min(max, Math.max(min, Math.round(value))); } function amountLabel(value: number): string { return value.toLocaleString(); } export function BettingControls({ tableId, snapshotVersion, legalActions, potTotal, yourTurn, onSnapshot, }: BettingControlsProps) { const [pendingAction, setPendingAction] = useState(null); const [error, setError] = useState(null); const [amount, setAmount] = useState(legalActions.minBetTo); const wagerKind: PokerActionKind | null = legalActions.canBet ? "bet" : legalActions.canRaise ? "raise" : null; const wagerMin = legalActions.canBet ? legalActions.minBetTo : legalActions.minRaiseTo; const wagerMax = legalActions.canBet ? legalActions.maxBetTo : legalActions.maxRaiseTo; const canAct = yourTurn && pendingAction === null; useEffect(() => { setAmount((current) => clampAmount(current, wagerMin, wagerMax)); }, [wagerMax, wagerMin]); useEffect(() => { setPendingAction(null); setError(null); }, [snapshotVersion]); const submit = async (kind: PokerActionKind, wager?: number) => { setError(null); setPendingAction(kind); try { const result = await pokerAction(tableId, kind, wager); if (!result.success) { setError(result.error ?? "That action was rejected by the table."); setPendingAction(null); return; } if (result.data) onSnapshot(result.data); } catch (caught) { setError(caught instanceof Error ? caught.message : "Could not send the table action."); setPendingAction(null); } }; const chooseQuickAmount = (choice: "min" | "half" | "pot" | "all") => { const nextAmount = choice === "min" ? wagerMin : choice === "half" ? Math.round(potTotal / 2) : choice === "pot" ? potTotal : wagerMax; setAmount(clampAmount(nextAmount, wagerMin, wagerMax)); }; return (
{wagerKind && wagerMax >= wagerMin && ( {wagerKind === "bet" ? "Bet size" : "Raise to"}
setAmount(clampAmount(Number(event.target.value), wagerMin, wagerMax)) } disabled={!canAct} className="h-2 min-w-0 flex-1 cursor-pointer accent-primary disabled:cursor-not-allowed" aria-label={`${wagerKind === "bet" ? "Bet" : "Raise"} amount`} /> setAmount(clampAmount(Number(event.target.value), wagerMin, wagerMax)) } disabled={!canAct} className="w-24 font-mono tabular-nums" aria-label="Wager amount" />
{(["min", "half", "pot", "all"] as const).map((choice) => ( ))}
)}
{!yourTurn && Waiting for the table to act.} {pendingAction && ( Waiting for the table update... )} {error && {error}}
); }