diff --git a/packages/client/src/components/bingo-card.tsx b/packages/client/src/components/bingo-card.tsx index 47c83bc..2c70f66 100644 --- a/packages/client/src/components/bingo-card.tsx +++ b/packages/client/src/components/bingo-card.tsx @@ -1,13 +1,16 @@ import type { BingoCard as BingoCardType } from "@celebrate-esc/shared" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Badge } from "@/components/ui/badge" +import { Button } from "@/components/ui/button" interface BingoCardProps { card: BingoCardType onTap: (tropeId: string) => void + readonly?: boolean + onRequestNewCard?: () => void } -export function BingoCard({ card, onTap }: BingoCardProps) { +export function BingoCard({ card, onTap, readonly, onRequestNewCard }: BingoCardProps) { return ( @@ -20,23 +23,31 @@ export function BingoCard({ card, onTap }: BingoCardProps) { )} - +
{card.squares.map((square) => ( ))}
+ {card.hasBingo && onRequestNewCard && ( + + )}
) diff --git a/packages/client/src/components/bottom-nav.tsx b/packages/client/src/components/bottom-nav.tsx new file mode 100644 index 0000000..6196569 --- /dev/null +++ b/packages/client/src/components/bottom-nav.tsx @@ -0,0 +1,96 @@ +import { Link, useMatchRoute } from "@tanstack/react-router" + +interface BottomNavProps { + basePath: "/play/$roomCode" | "/host/$roomCode" + roomCode: string + isHost: boolean +} + +function GameIcon({ active }: { active: boolean }) { + return ( + + + + + ) +} + +function BingoIcon({ active }: { active: boolean }) { + return ( + + + + + + + ) +} + +function TrophyIcon({ active }: { active: boolean }) { + return ( + + + + + + + + + ) +} + +function WrenchIcon({ active }: { active: boolean }) { + return ( + + + + ) +} + +interface TabConfig { + to: string + label: string + icon: (props: { active: boolean }) => React.ReactNode +} + +export function BottomNav({ basePath, roomCode, isHost }: BottomNavProps) { + const matchRoute = useMatchRoute() + + const tabs: TabConfig[] = [ + { to: `${basePath}/game`, label: "Game", icon: GameIcon }, + { to: `${basePath}/bingo`, label: "Bingo", icon: BingoIcon }, + { to: `${basePath}/board`, label: isHost ? "Board" : "Leaderboard", icon: TrophyIcon }, + ] + + if (isHost) { + tabs.push({ to: `${basePath}/host`, label: "Host", icon: WrenchIcon }) + } + + return ( + + ) +} diff --git a/packages/client/src/components/leaderboard.tsx b/packages/client/src/components/leaderboard.tsx index b1482ee..e154e16 100644 --- a/packages/client/src/components/leaderboard.tsx +++ b/packages/client/src/components/leaderboard.tsx @@ -4,15 +4,16 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" interface LeaderboardProps { entries: LeaderboardEntry[] resultsEntered?: boolean + lobbyMode?: boolean } -export function Leaderboard({ entries, resultsEntered }: LeaderboardProps) { +export function Leaderboard({ entries, resultsEntered, lobbyMode }: LeaderboardProps) { if (entries.length === 0) return null return ( - Leaderboard + {lobbyMode ? `Players (${entries.length})` : "Leaderboard"}
@@ -24,25 +25,29 @@ export function Leaderboard({ entries, resultsEntered }: LeaderboardProps) { {entry.displayName}
-
- P:{resultsEntered ? entry.predictionPoints : "?"} - J:{entry.juryPoints} - B:{entry.bingoPoints} - Q:{entry.quizPoints} - {entry.totalPoints} -
+ {!lobbyMode && ( +
+ P:{resultsEntered ? entry.predictionPoints : "?"} + J:{entry.juryPoints} + B:{entry.bingoPoints} + Q:{entry.quizPoints} + {entry.totalPoints} +
+ )} ))} -
-

How scoring works

-
    -
  • P = Prediction points — 25 for correct winner, 10 each for 2nd/3rd, 15 for last place
  • -
  • J = Jury points — rate each act 1-12, earn up to 5 pts per round for matching the group consensus
  • -
  • B = Bingo points — 2 pts per tapped trope + 10 bonus for a full bingo line
  • -
  • Q = Quiz points — 5 easy, 10 medium, 15 hard
  • -
-
+ {!lobbyMode && ( +
+

How scoring works

+
    +
  • P = Prediction points — 25 for correct winner, 10 each for 2nd/3rd, 15 for last place
  • +
  • J = Jury points — rate each act 1-12, earn up to 5 pts per round for matching the group consensus
  • +
  • B = Bingo points — 2 pts per tapped trope + 10 bonus for a full bingo line
  • +
  • Q = Quiz points — 5 easy, 10 medium, 15 hard
  • +
+
+ )}
) diff --git a/packages/client/src/components/room-layout.tsx b/packages/client/src/components/room-layout.tsx new file mode 100644 index 0000000..99328f1 --- /dev/null +++ b/packages/client/src/components/room-layout.tsx @@ -0,0 +1,32 @@ +import { Outlet } from "@tanstack/react-router" + +interface RoomLayoutProps { + roomCode: string + connectionStatus: "disconnected" | "connecting" | "connected" +} + +export function RoomLayout({ roomCode, connectionStatus }: RoomLayoutProps) { + return ( +
+
+ I❤️ESC +
+ {roomCode} + +
+
+
+ +
+
+ ) +}