- Added a comprehensive plan for bracket expansion, including support for 4, 8, 16, 32, and 68 team formats. - Introduced a template-based bracket system with predefined templates for NCAA March Madness, NFL Playoffs, NBA Playoffs, and simple brackets. - Updated UI flow for bracket creation, allowing admins to select templates and assign participants flexibly. - Enhanced database schema to accommodate new scoring rules and bracket templates. - Proposed updates to scoring logic to handle non-scoring rounds and participant placements correctly. - Documented implementation phases for gradual rollout of new features. - Addressed critical bugs in the playoff event processing and scoring logic, ensuring proper advancement and scoring rules across multiple leagues.
166 lines
5.9 KiB
TypeScript
166 lines
5.9 KiB
TypeScript
import {
|
|
Table,
|
|
TableBody,
|
|
TableCell,
|
|
TableHead,
|
|
TableHeader,
|
|
TableRow,
|
|
} from "~/components/ui/table";
|
|
import { Badge } from "~/components/ui/badge";
|
|
import { TrendingUp, TrendingDown, Minus, Trophy, Medal, Award } from "lucide-react";
|
|
|
|
export interface StandingsRow {
|
|
teamId: string;
|
|
teamName: string;
|
|
totalPoints: number;
|
|
currentRank: number;
|
|
previousRank?: number | null;
|
|
firstPlaceCount: number;
|
|
secondPlaceCount: number;
|
|
thirdPlaceCount: number;
|
|
fourthPlaceCount: number;
|
|
fifthPlaceCount: number;
|
|
sixthPlaceCount: number;
|
|
seventhPlaceCount: number;
|
|
eighthPlaceCount: number;
|
|
participantsRemaining: number;
|
|
}
|
|
|
|
interface StandingsTableProps {
|
|
standings: StandingsRow[];
|
|
showMovement?: boolean;
|
|
showPlacementBreakdown?: boolean;
|
|
}
|
|
|
|
export function StandingsTable({
|
|
standings,
|
|
showMovement = true,
|
|
showPlacementBreakdown = false,
|
|
}: StandingsTableProps) {
|
|
const getMovementIndicator = (current: number, previous?: number | null) => {
|
|
if (!showMovement || !previous) return null;
|
|
|
|
const change = previous - current; // Positive means moved up
|
|
|
|
if (change > 0) {
|
|
return (
|
|
<div className="flex items-center gap-1 text-green-600 dark:text-green-400">
|
|
<TrendingUp className="h-3 w-3" />
|
|
<span className="text-xs">+{change}</span>
|
|
</div>
|
|
);
|
|
} else if (change < 0) {
|
|
return (
|
|
<div className="flex items-center gap-1 text-red-600 dark:text-red-400">
|
|
<TrendingDown className="h-3 w-3" />
|
|
<span className="text-xs">{change}</span>
|
|
</div>
|
|
);
|
|
} else {
|
|
return (
|
|
<div className="flex items-center gap-1 text-muted-foreground">
|
|
<Minus className="h-3 w-3" />
|
|
<span className="text-xs">-</span>
|
|
</div>
|
|
);
|
|
}
|
|
};
|
|
|
|
const getRankBadge = (rank: number) => {
|
|
if (rank === 1) {
|
|
return (
|
|
<div className="flex items-center gap-2">
|
|
<Trophy className="h-5 w-5 text-yellow-500" />
|
|
<span className="font-bold text-lg">{rank}</span>
|
|
</div>
|
|
);
|
|
} else if (rank === 2) {
|
|
return (
|
|
<div className="flex items-center gap-2">
|
|
<Medal className="h-5 w-5 text-gray-400" />
|
|
<span className="font-semibold">{rank}</span>
|
|
</div>
|
|
);
|
|
} else if (rank === 3) {
|
|
return (
|
|
<div className="flex items-center gap-2">
|
|
<Award className="h-5 w-5 text-amber-700" />
|
|
<span className="font-semibold">{rank}</span>
|
|
</div>
|
|
);
|
|
} else {
|
|
return <span className="font-medium">{rank}</span>;
|
|
}
|
|
};
|
|
|
|
return (
|
|
<Table>
|
|
<TableHeader>
|
|
<TableRow>
|
|
<TableHead className="w-16">Rank</TableHead>
|
|
{showMovement && <TableHead className="w-20">Change</TableHead>}
|
|
<TableHead>Team</TableHead>
|
|
<TableHead className="text-right">Total Points</TableHead>
|
|
{showPlacementBreakdown && (
|
|
<>
|
|
<TableHead className="text-center w-12" title="1st place finishes">🥇</TableHead>
|
|
<TableHead className="text-center w-12" title="2nd place finishes">🥈</TableHead>
|
|
<TableHead className="text-center w-12" title="3rd place finishes">🥉</TableHead>
|
|
<TableHead className="text-center w-12" title="4th place finishes">4th</TableHead>
|
|
<TableHead className="text-center w-12" title="5th place finishes">5th</TableHead>
|
|
<TableHead className="text-center w-12" title="6th place finishes">6th</TableHead>
|
|
<TableHead className="text-center w-12" title="7th place finishes">7th</TableHead>
|
|
<TableHead className="text-center w-12" title="8th place finishes">8th</TableHead>
|
|
</>
|
|
)}
|
|
<TableHead className="text-right">Remaining</TableHead>
|
|
</TableRow>
|
|
</TableHeader>
|
|
<TableBody>
|
|
{standings.length === 0 ? (
|
|
<TableRow>
|
|
<TableCell colSpan={showPlacementBreakdown ? 13 : 5} className="text-center text-muted-foreground">
|
|
No standings data available yet
|
|
</TableCell>
|
|
</TableRow>
|
|
) : (
|
|
standings.map((row) => (
|
|
<TableRow key={row.teamId} className={row.currentRank <= 3 ? "bg-muted/30" : undefined}>
|
|
<TableCell>
|
|
{getRankBadge(row.currentRank)}
|
|
</TableCell>
|
|
{showMovement && (
|
|
<TableCell>
|
|
{getMovementIndicator(row.currentRank, row.previousRank)}
|
|
</TableCell>
|
|
)}
|
|
<TableCell className="font-medium">{row.teamName}</TableCell>
|
|
<TableCell className="text-right font-semibold">
|
|
{row.totalPoints.toFixed(1)}
|
|
</TableCell>
|
|
{showPlacementBreakdown && (
|
|
<>
|
|
<TableCell className="text-center">{row.firstPlaceCount || "-"}</TableCell>
|
|
<TableCell className="text-center">{row.secondPlaceCount || "-"}</TableCell>
|
|
<TableCell className="text-center">{row.thirdPlaceCount || "-"}</TableCell>
|
|
<TableCell className="text-center">{row.fourthPlaceCount || "-"}</TableCell>
|
|
<TableCell className="text-center">{row.fifthPlaceCount || "-"}</TableCell>
|
|
<TableCell className="text-center">{row.sixthPlaceCount || "-"}</TableCell>
|
|
<TableCell className="text-center">{row.seventhPlaceCount || "-"}</TableCell>
|
|
<TableCell className="text-center">{row.eighthPlaceCount || "-"}</TableCell>
|
|
</>
|
|
)}
|
|
<TableCell className="text-right">
|
|
{row.participantsRemaining > 0 ? (
|
|
<Badge variant="secondary">{row.participantsRemaining}</Badge>
|
|
) : (
|
|
<span className="text-muted-foreground">-</span>
|
|
)}
|
|
</TableCell>
|
|
</TableRow>
|
|
))
|
|
)}
|
|
</TableBody>
|
|
</Table>
|
|
);
|
|
}
|