brackt/app/components/scoring/QualifyingPointsStandings.tsx

263 lines
10 KiB
TypeScript
Raw Normal View History

Fix standings table bugs and polish across all three scoring patterns (#415) * Fix standings table bugs and polish across all three scoring patterns - Fix isLastBeforePointsLine/PlayoffLine: was incorrectly suppressing the bottom border on the last row of a section when no divider followed (nextStanding === undefined case). Now correctly requires nextStanding to exist and exceed the cutoff rank. - Fix totalCols colSpan overcounting: hidden sm:table-cell columns (Drafted By / Mgr) don't occupy column slots on mobile, so counting them caused the divider rows to span one too many. Replaced with colSpan={100} (browser caps to actual column count). - Move pointsLinePushed mutation out of render in SeasonStandings and QualifyingPointsStandings: replaced let+mutation+array-push pattern with pre-computed firstOver8Idx and React.Fragment per row. - Replace array-returning .map() with keyed React.Fragment in both files. - Remove unused description prop from SeasonStandingsProps. - Use useId() for Switch id props in all three components to prevent id collisions when mounted multiple times. - Fix formatQP NaN fallback from "0" to "—". - Add comment noting canFinalize guards the admin-only finalize UI. - Drop dead description prop pass-through in SportSeasonDisplay. Fixes #408 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix RegularSeasonStandings tests broken by showStats toggle - STK/streak tests: click the Details switch before asserting on stats columns, which are now hidden behind the toggle by default - Ownership badge test: use getAllByText since the badge renders in both the mobile inline slot and the hidden-sm desktop Mgr cell - Division label test: remove expectation for inline per-row division labels, which were intentionally removed to fix mobile scroll Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 10:39:25 -07:00
import { Fragment } from "react";
import { Form } from "react-router";
import { Button } from "~/components/ui/button";
2026-04-12 17:06:20 -07:00
import { TeamOwnerBadge } from "~/components/ui/team-owner-badge";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "~/components/ui/card";
import { Trophy, CheckCircle2 } from "lucide-react";
2026-04-12 17:06:20 -07:00
interface TeamOwnership {
participantId: string;
teamName: string;
teamId: string;
ownerName?: string;
}
interface QPStanding {
id: string;
totalQualifyingPoints: string;
eventsScored: number;
finalRanking: number | null;
globalRank: number;
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;
Fix standings table bugs and polish across all three scoring patterns (#415) * Fix standings table bugs and polish across all three scoring patterns - Fix isLastBeforePointsLine/PlayoffLine: was incorrectly suppressing the bottom border on the last row of a section when no divider followed (nextStanding === undefined case). Now correctly requires nextStanding to exist and exceed the cutoff rank. - Fix totalCols colSpan overcounting: hidden sm:table-cell columns (Drafted By / Mgr) don't occupy column slots on mobile, so counting them caused the divider rows to span one too many. Replaced with colSpan={100} (browser caps to actual column count). - Move pointsLinePushed mutation out of render in SeasonStandings and QualifyingPointsStandings: replaced let+mutation+array-push pattern with pre-computed firstOver8Idx and React.Fragment per row. - Replace array-returning .map() with keyed React.Fragment in both files. - Remove unused description prop from SeasonStandingsProps. - Use useId() for Switch id props in all three components to prevent id collisions when mounted multiple times. - Fix formatQP NaN fallback from "0" to "—". - Add comment noting canFinalize guards the admin-only finalize UI. - Drop dead description prop pass-through in SportSeasonDisplay. Fixes #408 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix RegularSeasonStandings tests broken by showStats toggle - STK/streak tests: click the Details switch before asserting on stats columns, which are now hidden behind the toggle by default - Ownership badge test: use getAllByText since the badge renders in both the mobile inline slot and the hidden-sm desktop Mgr cell - Division label test: remove expectation for inline per-row division labels, which were intentionally removed to fix mobile scroll Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 10:39:25 -07:00
canFinalize: boolean;
2026-04-12 17:06:20 -07:00
teamOwnerships?: TeamOwnership[];
Fix standings table bugs and polish across all three scoring patterns (#415) * Fix standings table bugs and polish across all three scoring patterns - Fix isLastBeforePointsLine/PlayoffLine: was incorrectly suppressing the bottom border on the last row of a section when no divider followed (nextStanding === undefined case). Now correctly requires nextStanding to exist and exceed the cutoff rank. - Fix totalCols colSpan overcounting: hidden sm:table-cell columns (Drafted By / Mgr) don't occupy column slots on mobile, so counting them caused the divider rows to span one too many. Replaced with colSpan={100} (browser caps to actual column count). - Move pointsLinePushed mutation out of render in SeasonStandings and QualifyingPointsStandings: replaced let+mutation+array-push pattern with pre-computed firstOver8Idx and React.Fragment per row. - Replace array-returning .map() with keyed React.Fragment in both files. - Remove unused description prop from SeasonStandingsProps. - Use useId() for Switch id props in all three components to prevent id collisions when mounted multiple times. - Fix formatQP NaN fallback from "0" to "—". - Add comment noting canFinalize guards the admin-only finalize UI. - Drop dead description prop pass-through in SportSeasonDisplay. Fixes #408 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix RegularSeasonStandings tests broken by showStats toggle - STK/streak tests: click the Details switch before asserting on stats columns, which are now hidden behind the toggle by default - Ownership badge test: use getAllByText since the badge renders in both the mobile inline slot and the hidden-sm desktop Mgr cell - Division label test: remove expectation for inline per-row division labels, which were intentionally removed to fix mobile scroll Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 10:39:25 -07:00
userParticipantIds?: string[];
}
function formatQP(raw: string): string {
const n = parseFloat(raw);
if (isNaN(n)) return "—";
return n % 1 === 0 ? n.toString() : n.toFixed(2);
}
export function QualifyingPointsStandings({
standings,
scoringRules,
isFinalized,
totalMajors,
majorsCompleted = 0,
canFinalize,
2026-04-12 17:06:20 -07:00
teamOwnerships = [],
Fix standings table bugs and polish across all three scoring patterns (#415) * Fix standings table bugs and polish across all three scoring patterns - Fix isLastBeforePointsLine/PlayoffLine: was incorrectly suppressing the bottom border on the last row of a section when no divider followed (nextStanding === undefined case). Now correctly requires nextStanding to exist and exceed the cutoff rank. - Fix totalCols colSpan overcounting: hidden sm:table-cell columns (Drafted By / Mgr) don't occupy column slots on mobile, so counting them caused the divider rows to span one too many. Replaced with colSpan={100} (browser caps to actual column count). - Move pointsLinePushed mutation out of render in SeasonStandings and QualifyingPointsStandings: replaced let+mutation+array-push pattern with pre-computed firstOver8Idx and React.Fragment per row. - Replace array-returning .map() with keyed React.Fragment in both files. - Remove unused description prop from SeasonStandingsProps. - Use useId() for Switch id props in all three components to prevent id collisions when mounted multiple times. - Fix formatQP NaN fallback from "0" to "—". - Add comment noting canFinalize guards the admin-only finalize UI. - Drop dead description prop pass-through in SportSeasonDisplay. Fixes #408 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix RegularSeasonStandings tests broken by showStats toggle - STK/streak tests: click the Details switch before asserting on stats columns, which are now hidden behind the toggle by default - Ownership badge test: use getAllByText since the badge renders in both the mobile inline slot and the hidden-sm desktop Mgr cell - Division label test: remove expectation for inline per-row division labels, which were intentionally removed to fix mobile scroll Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 10:39:25 -07:00
userParticipantIds = [],
}: QualifyingPointsStandingsProps) {
2026-04-12 17:06:20 -07:00
const ownershipMap = new Map(teamOwnerships.map((o) => [o.participantId, o]));
Fix standings table bugs and polish across all three scoring patterns (#415) * Fix standings table bugs and polish across all three scoring patterns - Fix isLastBeforePointsLine/PlayoffLine: was incorrectly suppressing the bottom border on the last row of a section when no divider followed (nextStanding === undefined case). Now correctly requires nextStanding to exist and exceed the cutoff rank. - Fix totalCols colSpan overcounting: hidden sm:table-cell columns (Drafted By / Mgr) don't occupy column slots on mobile, so counting them caused the divider rows to span one too many. Replaced with colSpan={100} (browser caps to actual column count). - Move pointsLinePushed mutation out of render in SeasonStandings and QualifyingPointsStandings: replaced let+mutation+array-push pattern with pre-computed firstOver8Idx and React.Fragment per row. - Replace array-returning .map() with keyed React.Fragment in both files. - Remove unused description prop from SeasonStandingsProps. - Use useId() for Switch id props in all three components to prevent id collisions when mounted multiple times. - Fix formatQP NaN fallback from "0" to "—". - Add comment noting canFinalize guards the admin-only finalize UI. - Drop dead description prop pass-through in SportSeasonDisplay. Fixes #408 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix RegularSeasonStandings tests broken by showStats toggle - STK/streak tests: click the Details switch before asserting on stats columns, which are now hidden behind the toggle by default - Ownership badge test: use getAllByText since the badge renders in both the mobile inline slot and the hidden-sm desktop Mgr cell - Division label test: remove expectation for inline per-row division labels, which were intentionally removed to fix mobile scroll Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 10:39:25 -07:00
const userParticipantSet = new Set(userParticipantIds);
// Use the pre-computed global rank from the loader so displayed ranks reflect the full
// participant field even when the array has been filtered down to drafted/points-earning rows.
const rankedStandings: Array<QPStanding & { currentRank: number }> = standings.map(
(standing) => ({ ...standing, currentRank: standing.globalRank })
);
2026-04-12 17:06:20 -07:00
const tiedCountByRank = new Map<number, number>();
for (const s of rankedStandings) {
tiedCountByRank.set(s.currentRank, (tiedCountByRank.get(s.currentRank) ?? 0) + 1);
}
Fix standings table bugs and polish across all three scoring patterns (#415) * Fix standings table bugs and polish across all three scoring patterns - Fix isLastBeforePointsLine/PlayoffLine: was incorrectly suppressing the bottom border on the last row of a section when no divider followed (nextStanding === undefined case). Now correctly requires nextStanding to exist and exceed the cutoff rank. - Fix totalCols colSpan overcounting: hidden sm:table-cell columns (Drafted By / Mgr) don't occupy column slots on mobile, so counting them caused the divider rows to span one too many. Replaced with colSpan={100} (browser caps to actual column count). - Move pointsLinePushed mutation out of render in SeasonStandings and QualifyingPointsStandings: replaced let+mutation+array-push pattern with pre-computed firstOver8Idx and React.Fragment per row. - Replace array-returning .map() with keyed React.Fragment in both files. - Remove unused description prop from SeasonStandingsProps. - Use useId() for Switch id props in all three components to prevent id collisions when mounted multiple times. - Fix formatQP NaN fallback from "0" to "—". - Add comment noting canFinalize guards the admin-only finalize UI. - Drop dead description prop pass-through in SportSeasonDisplay. Fixes #408 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix RegularSeasonStandings tests broken by showStats toggle - STK/streak tests: click the Details switch before asserting on stats columns, which are now hidden behind the toggle by default - Ownership badge test: use getAllByText since the badge renders in both the mobile inline slot and the hidden-sm desktop Mgr cell - Division label test: remove expectation for inline per-row division labels, which were intentionally removed to fix mobile scroll Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 10:39:25 -07:00
const showOwnership = teamOwnerships.length > 0;
// Index of first row with rank > 8; -1 when there is no cutoff.
const firstOver8Idx = rankedStandings.findIndex((s) => s.currentRank > 8);
2026-04-12 17:06:20 -07:00
return (
Fix standings table bugs and polish across all three scoring patterns (#415) * Fix standings table bugs and polish across all three scoring patterns - Fix isLastBeforePointsLine/PlayoffLine: was incorrectly suppressing the bottom border on the last row of a section when no divider followed (nextStanding === undefined case). Now correctly requires nextStanding to exist and exceed the cutoff rank. - Fix totalCols colSpan overcounting: hidden sm:table-cell columns (Drafted By / Mgr) don't occupy column slots on mobile, so counting them caused the divider rows to span one too many. Replaced with colSpan={100} (browser caps to actual column count). - Move pointsLinePushed mutation out of render in SeasonStandings and QualifyingPointsStandings: replaced let+mutation+array-push pattern with pre-computed firstOver8Idx and React.Fragment per row. - Replace array-returning .map() with keyed React.Fragment in both files. - Remove unused description prop from SeasonStandingsProps. - Use useId() for Switch id props in all three components to prevent id collisions when mounted multiple times. - Fix formatQP NaN fallback from "0" to "—". - Add comment noting canFinalize guards the admin-only finalize UI. - Drop dead description prop pass-through in SportSeasonDisplay. Fixes #408 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix RegularSeasonStandings tests broken by showStats toggle - STK/streak tests: click the Details switch before asserting on stats columns, which are now hidden behind the toggle by default - Ownership badge test: use getAllByText since the badge renders in both the mobile inline slot and the hidden-sm desktop Mgr cell - Division label test: remove expectation for inline per-row division labels, which were intentionally removed to fix mobile scroll Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 10:39:25 -07:00
<Card>
<CardHeader>
Fix standings table bugs and polish across all three scoring patterns (#415) * Fix standings table bugs and polish across all three scoring patterns - Fix isLastBeforePointsLine/PlayoffLine: was incorrectly suppressing the bottom border on the last row of a section when no divider followed (nextStanding === undefined case). Now correctly requires nextStanding to exist and exceed the cutoff rank. - Fix totalCols colSpan overcounting: hidden sm:table-cell columns (Drafted By / Mgr) don't occupy column slots on mobile, so counting them caused the divider rows to span one too many. Replaced with colSpan={100} (browser caps to actual column count). - Move pointsLinePushed mutation out of render in SeasonStandings and QualifyingPointsStandings: replaced let+mutation+array-push pattern with pre-computed firstOver8Idx and React.Fragment per row. - Replace array-returning .map() with keyed React.Fragment in both files. - Remove unused description prop from SeasonStandingsProps. - Use useId() for Switch id props in all three components to prevent id collisions when mounted multiple times. - Fix formatQP NaN fallback from "0" to "—". - Add comment noting canFinalize guards the admin-only finalize UI. - Drop dead description prop pass-through in SportSeasonDisplay. Fixes #408 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix RegularSeasonStandings tests broken by showStats toggle - STK/streak tests: click the Details switch before asserting on stats columns, which are now hidden behind the toggle by default - Ownership badge test: use getAllByText since the badge renders in both the mobile inline slot and the hidden-sm desktop Mgr cell - Division label test: remove expectation for inline per-row division labels, which were intentionally removed to fix mobile scroll Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 10:39:25 -07:00
<CardTitle>
<Trophy className="inline mr-2 h-5 w-5" />
Qualifying Points Standings
</CardTitle>
<CardDescription>
{isFinalized ? (
Redesign to dark-mode-only with navy palette and accent colors (#13) Removes light mode entirely in favour of a permanent dark theme with a navy-tinted background and three signature accents (electric blue, amber/gold, coral) exposed as CSS custom properties and Tailwind utilities (bg-electric, text-amber-accent, text-coral-accent). - Set class="dark" on <html> and apply Clerk dark base theme - Rewrite app.css: single :root palette (oklch navy values), custom --electric / --amber-accent / --coral-accent variables, remove duplicate .dark block and light-mode bg-white/bg-gray-950 rule - Install @clerk/themes for Clerk dark modal support - Replace hardcoded Tailwind colors across 30+ files: - Draft grid cells: blue-50/blue-950 → electric/15, green-50/950 → emerald/10 - Timer: green-600/yellow-600/red-600 → emerald-400/amber-accent/coral-accent - Status badges: blue-50/green-50/gray-50 → electric/emerald/muted variants - Success messages: green-500/15 text-green-700 dark:text-green-400 → emerald-500/15 text-emerald-400 - Info cards: blue-50 dark:bg-blue-950 → electric/10 - Warning cards: yellow-500 → amber-accent variants - Medal/placement badges: yellow-500/orange-600 → amber-accent/coral-accent - Movement indicators: green-600/red-600 → emerald-400/coral-accent - Connection dots: green-500/red-500 → emerald-500/coral-accent - Remove dark:hidden/dark:block logo toggle in welcome.tsx (always dark) - Update DraftGrid test assertions to match new class names Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-20 19:26:11 -08:00
<span className="text-emerald-400 font-semibold">
<CheckCircle2 className="inline h-4 w-4 mr-1" />
Fix standings table bugs and polish across all three scoring patterns (#415) * Fix standings table bugs and polish across all three scoring patterns - Fix isLastBeforePointsLine/PlayoffLine: was incorrectly suppressing the bottom border on the last row of a section when no divider followed (nextStanding === undefined case). Now correctly requires nextStanding to exist and exceed the cutoff rank. - Fix totalCols colSpan overcounting: hidden sm:table-cell columns (Drafted By / Mgr) don't occupy column slots on mobile, so counting them caused the divider rows to span one too many. Replaced with colSpan={100} (browser caps to actual column count). - Move pointsLinePushed mutation out of render in SeasonStandings and QualifyingPointsStandings: replaced let+mutation+array-push pattern with pre-computed firstOver8Idx and React.Fragment per row. - Replace array-returning .map() with keyed React.Fragment in both files. - Remove unused description prop from SeasonStandingsProps. - Use useId() for Switch id props in all three components to prevent id collisions when mounted multiple times. - Fix formatQP NaN fallback from "0" to "—". - Add comment noting canFinalize guards the admin-only finalize UI. - Drop dead description prop pass-through in SportSeasonDisplay. Fixes #408 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix RegularSeasonStandings tests broken by showStats toggle - STK/streak tests: click the Details switch before asserting on stats columns, which are now hidden behind the toggle by default - Ownership badge test: use getAllByText since the badge renders in both the mobile inline slot and the hidden-sm desktop Mgr cell - Division label test: remove expectation for inline per-row division labels, which were intentionally removed to fix mobile scroll Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 10:39:25 -07:00
Finalized fantasy points have been assigned
</span>
) : (
<>
Fix standings table bugs and polish across all three scoring patterns (#415) * Fix standings table bugs and polish across all three scoring patterns - Fix isLastBeforePointsLine/PlayoffLine: was incorrectly suppressing the bottom border on the last row of a section when no divider followed (nextStanding === undefined case). Now correctly requires nextStanding to exist and exceed the cutoff rank. - Fix totalCols colSpan overcounting: hidden sm:table-cell columns (Drafted By / Mgr) don't occupy column slots on mobile, so counting them caused the divider rows to span one too many. Replaced with colSpan={100} (browser caps to actual column count). - Move pointsLinePushed mutation out of render in SeasonStandings and QualifyingPointsStandings: replaced let+mutation+array-push pattern with pre-computed firstOver8Idx and React.Fragment per row. - Replace array-returning .map() with keyed React.Fragment in both files. - Remove unused description prop from SeasonStandingsProps. - Use useId() for Switch id props in all three components to prevent id collisions when mounted multiple times. - Fix formatQP NaN fallback from "0" to "—". - Add comment noting canFinalize guards the admin-only finalize UI. - Drop dead description prop pass-through in SportSeasonDisplay. Fixes #408 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix RegularSeasonStandings tests broken by showStats toggle - STK/streak tests: click the Details switch before asserting on stats columns, which are now hidden behind the toggle by default - Ownership badge test: use getAllByText since the badge renders in both the mobile inline slot and the hidden-sm desktop Mgr cell - Division label test: remove expectation for inline per-row division labels, which were intentionally removed to fix mobile scroll Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 10:39:25 -07:00
Current standings based on {majorsCompleted} of {totalMajors || "?"} major
tournaments completed.
</>
)}
</CardDescription>
</CardHeader>
<CardContent>
{standings.length === 0 ? (
<p className="text-sm text-muted-foreground">
No qualifying points awarded yet. Complete a qualifying event to see standings.
</p>
) : (
<>
Fix standings table bugs and polish across all three scoring patterns (#415) * Fix standings table bugs and polish across all three scoring patterns - Fix isLastBeforePointsLine/PlayoffLine: was incorrectly suppressing the bottom border on the last row of a section when no divider followed (nextStanding === undefined case). Now correctly requires nextStanding to exist and exceed the cutoff rank. - Fix totalCols colSpan overcounting: hidden sm:table-cell columns (Drafted By / Mgr) don't occupy column slots on mobile, so counting them caused the divider rows to span one too many. Replaced with colSpan={100} (browser caps to actual column count). - Move pointsLinePushed mutation out of render in SeasonStandings and QualifyingPointsStandings: replaced let+mutation+array-push pattern with pre-computed firstOver8Idx and React.Fragment per row. - Replace array-returning .map() with keyed React.Fragment in both files. - Remove unused description prop from SeasonStandingsProps. - Use useId() for Switch id props in all three components to prevent id collisions when mounted multiple times. - Fix formatQP NaN fallback from "0" to "—". - Add comment noting canFinalize guards the admin-only finalize UI. - Drop dead description prop pass-through in SportSeasonDisplay. Fixes #408 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix RegularSeasonStandings tests broken by showStats toggle - STK/streak tests: click the Details switch before asserting on stats columns, which are now hidden behind the toggle by default - Ownership badge test: use getAllByText since the badge renders in both the mobile inline slot and the hidden-sm desktop Mgr cell - Division label test: remove expectation for inline per-row division labels, which were intentionally removed to fix mobile scroll Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 10:39:25 -07:00
<div className="overflow-x-auto -mx-6 px-6">
<table className="w-full text-sm">
<thead>
<tr className="text-xs text-muted-foreground uppercase tracking-wide border-b">
<th className="text-left py-1.5 pl-2 pr-2 w-10">#</th>
<th className="text-left py-1.5">Participant</th>
<th className="text-right py-1.5 px-2 w-24">Total QP</th>
{showOwnership && (
<th className="hidden sm:table-cell text-right py-1.5 pl-4 w-40">Drafted By</th>
)}
</tr>
</thead>
<tbody>
{rankedStandings.map((standing, idx) => {
const ownership = showOwnership
? ownershipMap.get(standing.participant.id)
: undefined;
const isTop8 = standing.currentRank <= 8;
const isTied = (tiedCountByRank.get(standing.currentRank) ?? 1) > 1;
const isOwned = userParticipantSet.has(standing.participant.id);
const showPointsLineBefore = firstOver8Idx !== -1 && idx === firstOver8Idx;
const isLastBeforePointsLine = firstOver8Idx !== -1 && idx === firstOver8Idx - 1;
Fix standings table bugs and polish across all three scoring patterns (#415) * Fix standings table bugs and polish across all three scoring patterns - Fix isLastBeforePointsLine/PlayoffLine: was incorrectly suppressing the bottom border on the last row of a section when no divider followed (nextStanding === undefined case). Now correctly requires nextStanding to exist and exceed the cutoff rank. - Fix totalCols colSpan overcounting: hidden sm:table-cell columns (Drafted By / Mgr) don't occupy column slots on mobile, so counting them caused the divider rows to span one too many. Replaced with colSpan={100} (browser caps to actual column count). - Move pointsLinePushed mutation out of render in SeasonStandings and QualifyingPointsStandings: replaced let+mutation+array-push pattern with pre-computed firstOver8Idx and React.Fragment per row. - Replace array-returning .map() with keyed React.Fragment in both files. - Remove unused description prop from SeasonStandingsProps. - Use useId() for Switch id props in all three components to prevent id collisions when mounted multiple times. - Fix formatQP NaN fallback from "0" to "—". - Add comment noting canFinalize guards the admin-only finalize UI. - Drop dead description prop pass-through in SportSeasonDisplay. Fixes #408 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix RegularSeasonStandings tests broken by showStats toggle - STK/streak tests: click the Details switch before asserting on stats columns, which are now hidden behind the toggle by default - Ownership badge test: use getAllByText since the badge renders in both the mobile inline slot and the hidden-sm desktop Mgr cell - Division label test: remove expectation for inline per-row division labels, which were intentionally removed to fix mobile scroll Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 10:39:25 -07:00
return (
<Fragment key={standing.id}>
{showPointsLineBefore && (
<tr>
<td colSpan={100} className="py-0.5">
<div className="flex w-full items-center gap-2">
<div className="flex-1 border-t border-dashed border-amber-500/40" />
<span className="text-[10px] text-amber-600/70 dark:text-amber-500/70 uppercase tracking-wide whitespace-nowrap font-medium px-1">
Points Line
</span>
<div className="flex-1 border-t border-dashed border-amber-500/40" />
</div>
</td>
</tr>
)}
<tr
className={`${isLastBeforePointsLine ? "" : "border-b border-border/50 last:border-0"} ${
isOwned
? "bg-primary/5"
: isTop8
? "hover:bg-muted/30"
: "opacity-60 hover:bg-muted/30"
}`}
>
<td className="py-2 pl-2 pr-2 text-muted-foreground tabular-nums">
{isTied ? `T${standing.currentRank}` : standing.currentRank}
</td>
<td className="py-2 font-medium">
<span className={isOwned ? "text-primary" : ""}>
{standing.participant.name}
</span>
{ownership && (
<div className="sm:hidden mt-0.5">
<TeamOwnerBadge
teamName={ownership.teamName}
ownerName={ownership.ownerName}
/>
</div>
)}
</td>
<td className="py-2 px-2 text-right tabular-nums">
<span className={`font-semibold ${isTop8 ? "text-amber-accent" : "text-muted-foreground"}`}>
{formatQP(standing.totalQualifyingPoints)} QP
</span>
Fix standings table bugs and polish across all three scoring patterns (#415) * Fix standings table bugs and polish across all three scoring patterns - Fix isLastBeforePointsLine/PlayoffLine: was incorrectly suppressing the bottom border on the last row of a section when no divider followed (nextStanding === undefined case). Now correctly requires nextStanding to exist and exceed the cutoff rank. - Fix totalCols colSpan overcounting: hidden sm:table-cell columns (Drafted By / Mgr) don't occupy column slots on mobile, so counting them caused the divider rows to span one too many. Replaced with colSpan={100} (browser caps to actual column count). - Move pointsLinePushed mutation out of render in SeasonStandings and QualifyingPointsStandings: replaced let+mutation+array-push pattern with pre-computed firstOver8Idx and React.Fragment per row. - Replace array-returning .map() with keyed React.Fragment in both files. - Remove unused description prop from SeasonStandingsProps. - Use useId() for Switch id props in all three components to prevent id collisions when mounted multiple times. - Fix formatQP NaN fallback from "0" to "—". - Add comment noting canFinalize guards the admin-only finalize UI. - Drop dead description prop pass-through in SportSeasonDisplay. Fixes #408 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix RegularSeasonStandings tests broken by showStats toggle - STK/streak tests: click the Details switch before asserting on stats columns, which are now hidden behind the toggle by default - Ownership badge test: use getAllByText since the badge renders in both the mobile inline slot and the hidden-sm desktop Mgr cell - Division label test: remove expectation for inline per-row division labels, which were intentionally removed to fix mobile scroll Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 10:39:25 -07:00
</td>
{showOwnership && (
<td className="hidden sm:table-cell py-2 pl-4 text-right">
{ownership ? (
<div className="flex justify-end">
<TeamOwnerBadge
teamName={ownership.teamName}
ownerName={ownership.ownerName}
align="right"
/>
</div>
) : (
<span className="text-xs text-muted-foreground"></span>
)}
</td>
)}
Fix standings table bugs and polish across all three scoring patterns (#415) * Fix standings table bugs and polish across all three scoring patterns - Fix isLastBeforePointsLine/PlayoffLine: was incorrectly suppressing the bottom border on the last row of a section when no divider followed (nextStanding === undefined case). Now correctly requires nextStanding to exist and exceed the cutoff rank. - Fix totalCols colSpan overcounting: hidden sm:table-cell columns (Drafted By / Mgr) don't occupy column slots on mobile, so counting them caused the divider rows to span one too many. Replaced with colSpan={100} (browser caps to actual column count). - Move pointsLinePushed mutation out of render in SeasonStandings and QualifyingPointsStandings: replaced let+mutation+array-push pattern with pre-computed firstOver8Idx and React.Fragment per row. - Replace array-returning .map() with keyed React.Fragment in both files. - Remove unused description prop from SeasonStandingsProps. - Use useId() for Switch id props in all three components to prevent id collisions when mounted multiple times. - Fix formatQP NaN fallback from "0" to "—". - Add comment noting canFinalize guards the admin-only finalize UI. - Drop dead description prop pass-through in SportSeasonDisplay. Fixes #408 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix RegularSeasonStandings tests broken by showStats toggle - STK/streak tests: click the Details switch before asserting on stats columns, which are now hidden behind the toggle by default - Ownership badge test: use getAllByText since the badge renders in both the mobile inline slot and the hidden-sm desktop Mgr cell - Division label test: remove expectation for inline per-row division labels, which were intentionally removed to fix mobile scroll Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 10:39:25 -07:00
</tr>
</Fragment>
);
})}
</tbody>
</table>
</div>
Fix standings table bugs and polish across all three scoring patterns (#415) * Fix standings table bugs and polish across all three scoring patterns - Fix isLastBeforePointsLine/PlayoffLine: was incorrectly suppressing the bottom border on the last row of a section when no divider followed (nextStanding === undefined case). Now correctly requires nextStanding to exist and exceed the cutoff rank. - Fix totalCols colSpan overcounting: hidden sm:table-cell columns (Drafted By / Mgr) don't occupy column slots on mobile, so counting them caused the divider rows to span one too many. Replaced with colSpan={100} (browser caps to actual column count). - Move pointsLinePushed mutation out of render in SeasonStandings and QualifyingPointsStandings: replaced let+mutation+array-push pattern with pre-computed firstOver8Idx and React.Fragment per row. - Replace array-returning .map() with keyed React.Fragment in both files. - Remove unused description prop from SeasonStandingsProps. - Use useId() for Switch id props in all three components to prevent id collisions when mounted multiple times. - Fix formatQP NaN fallback from "0" to "—". - Add comment noting canFinalize guards the admin-only finalize UI. - Drop dead description prop pass-through in SportSeasonDisplay. Fixes #408 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix RegularSeasonStandings tests broken by showStats toggle - STK/streak tests: click the Details switch before asserting on stats columns, which are now hidden behind the toggle by default - Ownership badge test: use getAllByText since the badge renders in both the mobile inline slot and the hidden-sm desktop Mgr cell - Division label test: remove expectation for inline per-row division labels, which were intentionally removed to fix mobile scroll Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 10:39:25 -07:00
{/* canFinalize is controlled by the route loader; this section is only shown to league admins */}
{!isFinalized && canFinalize && (
<div className="mt-6 p-4 border-t">
<div className="space-y-3">
<div>
<h4 className="font-semibold text-sm mb-1">Ready to finalize?</h4>
<p className="text-sm text-muted-foreground">
All {totalMajors} major tournaments are complete. Finalizing will:
</p>
<ul className="list-disc list-inside text-sm text-muted-foreground mt-2 space-y-1">
Fix standings table bugs and polish across all three scoring patterns (#415) * Fix standings table bugs and polish across all three scoring patterns - Fix isLastBeforePointsLine/PlayoffLine: was incorrectly suppressing the bottom border on the last row of a section when no divider followed (nextStanding === undefined case). Now correctly requires nextStanding to exist and exceed the cutoff rank. - Fix totalCols colSpan overcounting: hidden sm:table-cell columns (Drafted By / Mgr) don't occupy column slots on mobile, so counting them caused the divider rows to span one too many. Replaced with colSpan={100} (browser caps to actual column count). - Move pointsLinePushed mutation out of render in SeasonStandings and QualifyingPointsStandings: replaced let+mutation+array-push pattern with pre-computed firstOver8Idx and React.Fragment per row. - Replace array-returning .map() with keyed React.Fragment in both files. - Remove unused description prop from SeasonStandingsProps. - Use useId() for Switch id props in all three components to prevent id collisions when mounted multiple times. - Fix formatQP NaN fallback from "0" to "—". - Add comment noting canFinalize guards the admin-only finalize UI. - Drop dead description prop pass-through in SportSeasonDisplay. Fixes #408 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix RegularSeasonStandings tests broken by showStats toggle - STK/streak tests: click the Details switch before asserting on stats columns, which are now hidden behind the toggle by default - Ownership badge test: use getAllByText since the badge renders in both the mobile inline slot and the hidden-sm desktop Mgr cell - Division label test: remove expectation for inline per-row division labels, which were intentionally removed to fix mobile scroll Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 10:39:25 -07:00
<li>Convert the top 8 participants to fantasy placements 18</li>
<li>Award fantasy points based on league scoring rules</li>
<li>Update all league standings</li>
<li>Lock the qualifying points (no further changes)</li>
</ul>
</div>
<Form method="post">
<input type="hidden" name="intent" value="finalize-qp" />
<Button
type="submit"
className="bg-amber-600 hover:bg-amber-700"
onClick={(e) => {
Fix standings table bugs and polish across all three scoring patterns (#415) * Fix standings table bugs and polish across all three scoring patterns - Fix isLastBeforePointsLine/PlayoffLine: was incorrectly suppressing the bottom border on the last row of a section when no divider followed (nextStanding === undefined case). Now correctly requires nextStanding to exist and exceed the cutoff rank. - Fix totalCols colSpan overcounting: hidden sm:table-cell columns (Drafted By / Mgr) don't occupy column slots on mobile, so counting them caused the divider rows to span one too many. Replaced with colSpan={100} (browser caps to actual column count). - Move pointsLinePushed mutation out of render in SeasonStandings and QualifyingPointsStandings: replaced let+mutation+array-push pattern with pre-computed firstOver8Idx and React.Fragment per row. - Replace array-returning .map() with keyed React.Fragment in both files. - Remove unused description prop from SeasonStandingsProps. - Use useId() for Switch id props in all three components to prevent id collisions when mounted multiple times. - Fix formatQP NaN fallback from "0" to "—". - Add comment noting canFinalize guards the admin-only finalize UI. - Drop dead description prop pass-through in SportSeasonDisplay. Fixes #408 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix RegularSeasonStandings tests broken by showStats toggle - STK/streak tests: click the Details switch before asserting on stats columns, which are now hidden behind the toggle by default - Ownership badge test: use getAllByText since the badge renders in both the mobile inline slot and the hidden-sm desktop Mgr cell - Division label test: remove expectation for inline per-row division labels, which were intentionally removed to fix mobile scroll Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 10:39:25 -07:00
if (
!confirm(
"Are you sure you want to finalize qualifying points? This action cannot be undone."
)
) {
e.preventDefault();
}
}}
>
<CheckCircle2 className="mr-2 h-4 w-4" />
Finalize Qualifying Points & Assign Fantasy Points
</Button>
</Form>
</div>
</div>
)}
Fix standings table bugs and polish across all three scoring patterns (#415) * Fix standings table bugs and polish across all three scoring patterns - Fix isLastBeforePointsLine/PlayoffLine: was incorrectly suppressing the bottom border on the last row of a section when no divider followed (nextStanding === undefined case). Now correctly requires nextStanding to exist and exceed the cutoff rank. - Fix totalCols colSpan overcounting: hidden sm:table-cell columns (Drafted By / Mgr) don't occupy column slots on mobile, so counting them caused the divider rows to span one too many. Replaced with colSpan={100} (browser caps to actual column count). - Move pointsLinePushed mutation out of render in SeasonStandings and QualifyingPointsStandings: replaced let+mutation+array-push pattern with pre-computed firstOver8Idx and React.Fragment per row. - Replace array-returning .map() with keyed React.Fragment in both files. - Remove unused description prop from SeasonStandingsProps. - Use useId() for Switch id props in all three components to prevent id collisions when mounted multiple times. - Fix formatQP NaN fallback from "0" to "—". - Add comment noting canFinalize guards the admin-only finalize UI. - Drop dead description prop pass-through in SportSeasonDisplay. Fixes #408 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix RegularSeasonStandings tests broken by showStats toggle - STK/streak tests: click the Details switch before asserting on stats columns, which are now hidden behind the toggle by default - Ownership badge test: use getAllByText since the badge renders in both the mobile inline slot and the hidden-sm desktop Mgr cell - Division label test: remove expectation for inline per-row division labels, which were intentionally removed to fix mobile scroll Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 10:39:25 -07:00
{!isFinalized && !canFinalize && scoringRules && (
<div className="mt-6 p-4 border-t">
<p className="text-sm text-muted-foreground">
Fix standings table bugs and polish across all three scoring patterns (#415) * Fix standings table bugs and polish across all three scoring patterns - Fix isLastBeforePointsLine/PlayoffLine: was incorrectly suppressing the bottom border on the last row of a section when no divider followed (nextStanding === undefined case). Now correctly requires nextStanding to exist and exceed the cutoff rank. - Fix totalCols colSpan overcounting: hidden sm:table-cell columns (Drafted By / Mgr) don't occupy column slots on mobile, so counting them caused the divider rows to span one too many. Replaced with colSpan={100} (browser caps to actual column count). - Move pointsLinePushed mutation out of render in SeasonStandings and QualifyingPointsStandings: replaced let+mutation+array-push pattern with pre-computed firstOver8Idx and React.Fragment per row. - Replace array-returning .map() with keyed React.Fragment in both files. - Remove unused description prop from SeasonStandingsProps. - Use useId() for Switch id props in all three components to prevent id collisions when mounted multiple times. - Fix formatQP NaN fallback from "0" to "—". - Add comment noting canFinalize guards the admin-only finalize UI. - Drop dead description prop pass-through in SportSeasonDisplay. Fixes #408 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix RegularSeasonStandings tests broken by showStats toggle - STK/streak tests: click the Details switch before asserting on stats columns, which are now hidden behind the toggle by default - Ownership badge test: use getAllByText since the badge renders in both the mobile inline slot and the hidden-sm desktop Mgr cell - Division label test: remove expectation for inline per-row division labels, which were intentionally removed to fix mobile scroll Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-12 10:39:25 -07:00
<span className="font-semibold">Not ready to finalize yet.</span> Complete all{" "}
{totalMajors} major tournaments before finalizing qualifying points. (
{majorsCompleted} of {totalMajors} completed)
</p>
</div>
)}
</>
)}
</CardContent>
</Card>
);
}