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}
+
+
+
+
+
+
+
+ )
+}