import { Form } from "react-router"; import { Button } from "~/components/ui/button"; import { TeamOwnerBadge } from "~/components/ui/team-owner-badge"; import { Card, CardContent, CardDescription, CardHeader, CardTitle, } from "~/components/ui/card"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from "~/components/ui/table"; import { Trophy, CheckCircle2 } from "lucide-react"; interface TeamOwnership { participantId: string; teamName: string; teamId: string; ownerName?: string; } interface QPStanding { id: string; totalQualifyingPoints: string; eventsScored: number; finalRanking: number | null; participant: { id: string; name: string; }; } interface ScoringRules { pointsFor1st: number; pointsFor2nd: number; pointsFor3rd: number; pointsFor4th: number; pointsFor5th: number; pointsFor6th: number; pointsFor7th: number; pointsFor8th: number; } interface QualifyingPointsStandingsProps { standings: QPStanding[]; scoringRules: ScoringRules | null; isFinalized: boolean; totalMajors?: number | null; majorsCompleted?: number; canFinalize: boolean; // Whether all majors are complete teamOwnerships?: TeamOwnership[]; } export function QualifyingPointsStandings({ standings, scoringRules, isFinalized, totalMajors, majorsCompleted = 0, canFinalize, teamOwnerships = [], }: QualifyingPointsStandingsProps) { const ownershipMap = new Map(teamOwnerships.map((o) => [o.participantId, o])); const pointsMap: Record = scoringRules ? { 1: scoringRules.pointsFor1st, 2: scoringRules.pointsFor2nd, 3: scoringRules.pointsFor3rd, 4: scoringRules.pointsFor4th, 5: scoringRules.pointsFor5th, 6: scoringRules.pointsFor6th, 7: scoringRules.pointsFor7th, 8: scoringRules.pointsFor8th, } : {}; // Assign current ranks with tie handling const rankedStandings: Array = []; let previousQP = -1; let previousRankStart = -1; for (let index = 0; index < standings.length; index++) { const standing = standings[index]; const currentQP = parseFloat(standing.totalQualifyingPoints); let currentRank: number; if (index > 0 && Math.abs(currentQP - previousQP) < 0.001) { currentRank = previousRankStart; } else { currentRank = index + 1; } rankedStandings.push({ ...standing, currentRank }); previousRankStart = currentRank; previousQP = currentQP; } // Pre-compute tied counts per rank to avoid O(N²) scanning per row const tiedCountByRank = new Map(); for (const s of rankedStandings) { tiedCountByRank.set(s.currentRank, (tiedCountByRank.get(s.currentRank) ?? 0) + 1); } // Calculate EV for a rank by averaging points across all tied positions const calculateEV = (currentRank: number): number => { if (!scoringRules || currentRank > 8) return 0; const tiedCount = tiedCountByRank.get(currentRank) ?? 1; let total = 0; for (let i = 0; i < tiedCount; i++) { total += pointsMap[currentRank + i] ?? 0; } return total / tiedCount; }; return ( Qualifying Points Standings {isFinalized ? ( Finalized - Fantasy points have been assigned ) : ( <> Current standings based on {majorsCompleted} of {totalMajors || '?'} major tournaments completed. {scoringRules ? ( Projected points account for ties — tied participants share the available points equally. ) : ( Projected points will be shown when linked to a fantasy season. )} )} {standings.length === 0 ? (

No qualifying points awarded yet. Complete a qualifying event to see standings.

) : ( <> Rank Participant {teamOwnerships.length > 0 && ( Owner )} Total QP Events {scoringRules && !isFinalized && ( Projected Points )} {rankedStandings.map((standing) => { const projectedPoints = calculateEV(standing.currentRank); const isTop8 = standing.currentRank <= 8; const isTied = (tiedCountByRank.get(standing.currentRank) ?? 1) > 1; return (
{standing.currentRank === 1 && !isTied && šŸ„‡} {standing.currentRank === 2 && !isTied && 🄈} {standing.currentRank === 3 && !isTied && šŸ„‰} {isTied && "T"} {standing.currentRank} {standing.currentRank === 1 ? "st" : standing.currentRank === 2 ? "nd" : standing.currentRank === 3 ? "rd" : "th"}
{standing.participant.name} {teamOwnerships.length > 0 && ( {(() => { const ownership = ownershipMap.get(standing.participant.id); return ownership ? ( ) : ( Undrafted ); })()} )} {parseFloat(standing.totalQualifyingPoints).toFixed(2)} QP {standing.eventsScored} {scoringRules && !isFinalized && ( {isTop8 ? ( {projectedPoints % 1 === 0 ? projectedPoints : projectedPoints.toFixed(1)}{" "} pts ) : ( 0 pts )} )}
); })}
{!isFinalized && canFinalize && (

Ready to finalize?

All {totalMajors} major tournaments are complete. Finalizing will:

  • Convert the top 8 participants to fantasy placements 1-8
  • Award fantasy points based on league scoring rules
  • Update all league standings
  • Lock the qualifying points (no further changes)
)} {!isFinalized && !canFinalize && (

Not ready to finalize yet. Complete all {totalMajors} major tournaments before finalizing qualifying points. ({majorsCompleted} of {totalMajors} completed)

)} )}
); }