import React, { useState } from 'react'; import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { Button } from "@/components/ui/button"; import { Trophy, Crown } from "lucide-react"; import { PlayingCard } from "./PlayingCard"; import type { RevealedHandsResponse } from "../apiclient/data-contracts"; import { toast } from 'sonner'; import apiclient from '../apiclient'; export interface Props { isOpen: boolean; onClose: () => void; data: RevealedHandsResponse | null; players: Array<{ user_id: string; display_name?: string | null }>; currentUserId: string; tableId: string; hostUserId: string; onNextRound?: () => void; } export const ScoreboardModal: React.FC = ({ isOpen, onClose, data, players, currentUserId, tableId, hostUserId, onNextRound }) => { const [startingNextRound, setStartingNextRound] = useState(false); if (!data) return null; // Sort players by score (lowest first, as lower is better) const sortedPlayers = players .filter(p => data.scores[p.user_id] !== undefined) .map(p => ({ ...p, score: data.scores[p.user_id], cards: data.revealed_hands[p.user_id] || [], organized: data.organized_melds?.[p.user_id] || null, isWinner: p.user_id === data.winner_user_id })) .sort((a, b) => a.score - b.score); const winnerName = sortedPlayers.find(p => p.isWinner)?.display_name || "Winner"; const isHost = currentUserId === hostUserId; const handleStartNextRound = async () => { setStartingNextRound(true); try { await apiclient.start_next_round({ table_id: tableId }); toast.success('Starting next round!'); onClose(); if (onNextRound) onNextRound(); } catch (error: any) { const errorMessage = error?.error?.detail || error?.message || 'Failed to start next round'; toast.error(errorMessage); } finally { setStartingNextRound(false); } }; return ( Round {data.round_number} Complete!
{/* Winner announcement */}
{winnerName} wins with {sortedPlayers[0]?.score || 0} points!
{/* Players list */}
{sortedPlayers.map((player, idx) => (
{player.isWinner && } {idx + 1}. {player.display_name || `Player ${player.user_id.slice(0, 8)}`} {player.user_id === currentUserId && ( You )}
{player.score} pts
{/* Organized melds display */} {player.organized ? (
Hand Organization (4-3-3-3 Format):
{/* Pure Sequences - HIGHEST PRIORITY */} {player.organized.pure_sequences?.length > 0 && (
✓ PURE SEQUENCE (No Jokers)
{player.organized.pure_sequences.map((meld: any[], meldIdx: number) => (
Meld {meldIdx + 1} ({meld.length} cards)
{meld.map((card: any, cardIdx: number) => (
))}
))}
)} {/* Impure Sequences - MEDIUM PRIORITY */} {player.organized.impure_sequences?.length > 0 && (
✓ IMPURE SEQUENCE (With Jokers)
{player.organized.impure_sequences.map((meld: any[], meldIdx: number) => (
Meld {meldIdx + 1} ({meld.length} cards)
{meld.map((card: any, cardIdx: number) => (
))}
))}
)} {/* Sets - LOWER PRIORITY */} {player.organized.sets?.length > 0 && (
✓ SET (Same Rank, Different Suits)
{player.organized.sets.map((meld: any[], meldIdx: number) => (
Meld {meldIdx + 1} ({meld.length} cards)
{meld.map((card: any, cardIdx: number) => (
))}
))}
)} {/* Ungrouped cards (deadwood) - PENALTY */} {player.organized.ungrouped?.length > 0 && (
✗ DEADWOOD (Ungrouped - Counts as Points)
{player.organized.ungrouped.length} ungrouped card(s)
{player.organized.ungrouped.map((card: any, cardIdx: number) => (
))}
)} {/* Summary */}
Total: {(player.organized.pure_sequences?.length || 0) + (player.organized.impure_sequences?.length || 0) + (player.organized.sets?.length || 0)} valid melds, {player.organized.ungrouped?.length || 0} deadwood cards
) : ( // Fallback: show all cards if no organization
{player.cards.map((card: any, idx: number) => (
))}
)}
))}
{isHost && ( )}
); };