fix(poker): dev panel card pickers ignored the first selection
CardPicker propagated null into onChange when no card was selected yet, so the board picker's parent coerced every first pick to 2 of spades and the hole-card pickers could never hold a value, keeping Queue hand disabled forever. Picks now build a complete card, preserving any already-chosen half, with regression tests on the extracted helper. Co-authored-by: Sisyphus <sisyphus@ohmyopencode.dev>
This commit is contained in:
parent
074fdb02bf
commit
dce6f8f2ee
2 changed files with 37 additions and 13 deletions
|
|
@ -58,6 +58,24 @@ function cardLabel(card: Card): string {
|
|||
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,
|
||||
|
|
@ -67,13 +85,13 @@ function CardPicker({
|
|||
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 (
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="w-10 text-xs text-muted-foreground">{label}</span>
|
||||
<Select
|
||||
value={value ? String(value.rank) : ""}
|
||||
onValueChange={(next) => onChange(value ? { ...value, rank: Number(next) as Rank } : null)}
|
||||
>
|
||||
<Select value={value ? String(value.rank) : ""} onValueChange={setRank}>
|
||||
<SelectTrigger className="h-8 w-20 text-xs">
|
||||
<SelectValue placeholder="Rank" />
|
||||
</SelectTrigger>
|
||||
|
|
@ -85,10 +103,7 @@ function CardPicker({
|
|||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
<Select
|
||||
value={value?.suit ?? ""}
|
||||
onValueChange={(next) => onChange(value ? { ...value, suit: next as Suit } : null)}
|
||||
>
|
||||
<Select value={value?.suit ?? ""} onValueChange={setSuit}>
|
||||
<SelectTrigger className="h-8 w-24 text-xs">
|
||||
<SelectValue placeholder="Suit" />
|
||||
</SelectTrigger>
|
||||
|
|
@ -191,11 +206,7 @@ export function PokerDevPanel({
|
|||
<section className="flex flex-col gap-2 border border-border p-3">
|
||||
<h3 className="text-xs font-semibold uppercase tracking-wider">Board queue</h3>
|
||||
<div className="flex flex-col gap-1.5">
|
||||
<CardPicker
|
||||
label="Card"
|
||||
value={boardDraft}
|
||||
onChange={(card) => setBoardDraft(card ?? { rank: 2, suit: "s" })}
|
||||
/>
|
||||
<CardPicker label="Card" value={boardDraft} onChange={setBoardDraft} />
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import {
|
|||
type PokerTableState,
|
||||
} from "@/lib/minigames/poker";
|
||||
import type { Card } from "@/lib/minigames/poker";
|
||||
import { applyCardPick } from "@/components/frontend/minigames/poker/PokerDevPanel";
|
||||
import {
|
||||
adjustStack,
|
||||
createTable,
|
||||
|
|
@ -148,3 +149,15 @@ describe("dev stack adjustment", () => {
|
|||
expect(() => adjustStack(tableId, 9611, 9, 100)).toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe("dev panel card picker", () => {
|
||||
it("builds a complete card from a first rank pick and preserves the chosen suit", () => {
|
||||
expect(applyCardPick(null, "rank", "13")).toEqual(card(13, "s"));
|
||||
expect(applyCardPick(card(13, "h"), "rank", "14")).toEqual(card(14, "h"));
|
||||
});
|
||||
|
||||
it("builds a complete card from a first suit pick and preserves the chosen rank", () => {
|
||||
expect(applyCardPick(null, "suit", "c")).toEqual(card(2, "c"));
|
||||
expect(applyCardPick(card(14, "h"), "suit", "d")).toEqual(card(14, "d"));
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue