Add React frontend and clean up legacy HTTP backend

This commit is contained in:
tim
2026-06-15 22:20:56 +02:00
parent b8e2d15e27
commit beaf142ee4
40 changed files with 8328 additions and 437 deletions
+52
View File
@@ -0,0 +1,52 @@
import type { CardColor, Hand } from '../types';
import CardView from './CardView';
import { emit } from '../lib/socket';
const COLOR_ORDER: CardColor[] = ['HEARTS', 'LEAVES', 'ACORNS', 'BELLS'];
const VALUE_ORDER = ['C7', 'C8', 'C9', 'C10', 'LOWER', 'UPPER', 'KING', 'ACE'];
function groupedByColor(hand: Hand): { color: CardColor; keys: string[] }[] {
return COLOR_ORDER
.map((color) => ({
color,
keys: Object.keys(hand)
.filter((k) => hand[k].color === color)
.sort((a, b) => VALUE_ORDER.indexOf(hand[a].value) - VALUE_ORDER.indexOf(hand[b].value)),
}))
.filter((g) => g.keys.length > 0);
}
interface Props {
hand: Hand;
myTurn: boolean;
isPlayPhase: boolean;
playableKeys?: Set<string>;
}
export default function Hand({ hand, myTurn, isPlayPhase, playableKeys }: Props) {
const groups = groupedByColor(hand);
if (groups.length === 0) return null;
return (
<div className="flex flex-wrap gap-3 justify-center py-2">
{groups.map(({ color, keys }) => (
<div key={color} className="flex gap-1">
{keys.map((key) => {
// During guessing phase cards are visible at full opacity (just not clickable).
// Only dim cards during the play phase when they can't be played.
const disabled = isPlayPhase && (!myTurn || (playableKeys !== undefined && !playableKeys.has(key)));
return (
<CardView
key={key}
card={hand[key]}
disabled={disabled}
onClick={() => emit.playCard(key)}
/>
);
})}
</div>
))}
</div>
);
}