brackt/app/components/scoring/SeasonStandings.tsx

238 lines
9 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, useId, useState } from "react";
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "~/components/ui/card";
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 { Switch } from "~/components/ui/switch";
import { TrendingUp, TrendingDown, Minus, Flag, CheckCircle2 } from "lucide-react";
User/chris/bracket UI redesign (#95) * feat: redesign playoff bracket UI with compact layout and owner display - Replace per-match Card wrappers with compact left-vs-right rows (stacks on mobile) - Add TeamOwnerBadge component (colored avatar + team name + username), matching the standings "Drafted By" style - Show owner info below each participant name in bracket matches - Add TBD forward-reference labels ("Winner of Quarterfinals M1") computed via buildFeederMap - Add Final Rankings table below bracket showing all participants ranked by elimination round - Fix round ordering in loader: sort by match count descending (more matches = earlier round) - Add loser relation to playoff matches query for elimination tracking - Extract getAvatarColor to app/lib/color-hash.ts (shared across GroupStageDisplay, SeasonStandings, TeamOwnerBadge) - Extract groupMatchesByRound helper; share grouped map between feeder computation and render - Remove dead getTeamAvatar from SeasonStandings after TeamOwnerBadge adoption - Add tests for groupMatchesByRound and buildFeederMap (7 tests) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: add draft order card to league home page Show draft order on the league home page when order is set and season is in pre_draft or draft status. Includes team name, owner name, and a link to the draft room. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * feat: enhance playoff bracket with eliminated teams, points, and ownership highlights - Show eliminated teams (including group-stage losers) in Final Rankings card - Display computed fantasy points per participant in rankings table - Card title switches between "Eliminated Teams" and "Final Rankings" based on bracket completion - Highlight owned participants with electric blue name, dot indicator, and card border in bracket matches - Highlight owned rows with electric border/background in rankings table - Suppress EventSchedule for playoff_bracket sports seasons - Fix title redundancy: bracket section title no longer repeats sport name - Two-column match grid on desktop (md:grid-cols-2) - Code review fixes: parallel DB fetches, targeted query for pre-eliminated, single-pass parseFloat, remove IIFE Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-09 22:44:33 -07:00
import { TeamOwnerBadge } from "~/components/ui/team-owner-badge";
interface SeasonStanding {
id: string;
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
championshipPoints: string;
position: 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
previousPosition?: number | null;
participant: {
id: string;
name: string;
};
}
interface TeamOwnership {
participantId: string;
teamName: string;
teamId: string;
ownerName?: string;
}
interface SeasonStandingsProps {
standings: SeasonStanding[];
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
teamOwnerships?: TeamOwnership[];
userParticipantIds?: string[];
showOwnership?: boolean;
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?: boolean;
title?: string;
}
Fix oxlint warnings: no-shadow, consistent-function-scoping, no-non-null-assertion, and others (#196) * Fix no-shadow and consistent-function-scoping lint violations Resolves all 11 no-shadow and 16 consistent-function-scoping oxlint warnings and promotes both rules to errors in .oxlintrc.json. no-shadow: renamed Drizzle callback params (sports→s, matches→m, seasons→s) to avoid shadowing outer imports; removed shadowed destructures (eq, inArray) from where callbacks; renamed inner template→bracketTemplate, prev→currentTimers, season→ss, name→teamName (with name: teamName fix to preserve semantics). consistent-function-scoping: moved formatDate, getRankBadge, getMovementIndicator, getPositionBadge, getStatusBadge, toDateStr, elo (×2), weightedPick, sortByMatchNumber (×2) to module scope; moved formatTime (×2), isValidLeagueName, getDraftTimes, makeSeasonQueues to file scope in test files. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix no-non-null-assertion lint violations and promote to error Eliminates all 208 no-non-null-assertion warnings across 38 files. Promotes typescript/no-non-null-assertion from warn to error in .oxlintrc.json. Fix patterns applied: - Map.get(key)! after .has() check → extract with get() + null guard - Map.get(key)! on pre-populated count maps → ?? 0 default - .set(id, map.get(id)! + 1) increment → ?? 0 before adding - participant1Id!/participant2Id! on DB matches → ?? "" fallback - array.find()! in tests → guard + throw or expect().toBeDefined() - bracketTemplateCache.get(id)! → null guard extract - Various nullable field accesses → optional chain or ?? default Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix prefer-add-event-listener, no-unassigned-import, require-module-specifiers Resolves all 9 remaining non-console lint warnings and promotes all three rules to errors in .oxlintrc.json. - prefer-add-event-listener: converted onchange/onclick/onload assignments to addEventListener in useDraftNotifications.ts and admin.data-sync.tsx; stored changeHandler ref for proper cleanup with removeEventListener - no-unassigned-import: configured rule with allow list for legitimate side-effect imports (*.css, @testing-library/jest-dom, @testing-library/cypress/add-commands) - require-module-specifiers: removed redundant `export {}` from cypress/support/e2e.ts (file already has an import) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix TypeScript errors from no-non-null-assertion fixes Two fixes introduced by the non-null assertion cleanup produced type errors: - scoring-event.ts: `?? ""` was wrong type for a participant object map; restructured to explicit null guards so TypeScript can narrow correctly - standings-sync/index.ts: `?? null` after name-match lookup lost the truthy guarantee, causing TS18047 on the write-back block; added `participant &&` guard before accessing its properties Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Add npm run typecheck as Stop hook in Claude settings Runs a full project typecheck at the end of each Claude turn so type errors surface as feedback before the next message. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 10:59:51 -07:00
function getMovementIndicator(
currentPosition: number,
previousPosition?: number | null
) {
if (!previousPosition) return null;
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 change = previousPosition - currentPosition;
Fix oxlint warnings: no-shadow, consistent-function-scoping, no-non-null-assertion, and others (#196) * Fix no-shadow and consistent-function-scoping lint violations Resolves all 11 no-shadow and 16 consistent-function-scoping oxlint warnings and promotes both rules to errors in .oxlintrc.json. no-shadow: renamed Drizzle callback params (sports→s, matches→m, seasons→s) to avoid shadowing outer imports; removed shadowed destructures (eq, inArray) from where callbacks; renamed inner template→bracketTemplate, prev→currentTimers, season→ss, name→teamName (with name: teamName fix to preserve semantics). consistent-function-scoping: moved formatDate, getRankBadge, getMovementIndicator, getPositionBadge, getStatusBadge, toDateStr, elo (×2), weightedPick, sortByMatchNumber (×2) to module scope; moved formatTime (×2), isValidLeagueName, getDraftTimes, makeSeasonQueues to file scope in test files. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix no-non-null-assertion lint violations and promote to error Eliminates all 208 no-non-null-assertion warnings across 38 files. Promotes typescript/no-non-null-assertion from warn to error in .oxlintrc.json. Fix patterns applied: - Map.get(key)! after .has() check → extract with get() + null guard - Map.get(key)! on pre-populated count maps → ?? 0 default - .set(id, map.get(id)! + 1) increment → ?? 0 before adding - participant1Id!/participant2Id! on DB matches → ?? "" fallback - array.find()! in tests → guard + throw or expect().toBeDefined() - bracketTemplateCache.get(id)! → null guard extract - Various nullable field accesses → optional chain or ?? default Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix prefer-add-event-listener, no-unassigned-import, require-module-specifiers Resolves all 9 remaining non-console lint warnings and promotes all three rules to errors in .oxlintrc.json. - prefer-add-event-listener: converted onchange/onclick/onload assignments to addEventListener in useDraftNotifications.ts and admin.data-sync.tsx; stored changeHandler ref for proper cleanup with removeEventListener - no-unassigned-import: configured rule with allow list for legitimate side-effect imports (*.css, @testing-library/jest-dom, @testing-library/cypress/add-commands) - require-module-specifiers: removed redundant `export {}` from cypress/support/e2e.ts (file already has an import) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Fix TypeScript errors from no-non-null-assertion fixes Two fixes introduced by the non-null assertion cleanup produced type errors: - scoring-event.ts: `?? ""` was wrong type for a participant object map; restructured to explicit null guards so TypeScript can narrow correctly - standings-sync/index.ts: `?? null` after name-match lookup lost the truthy guarantee, causing TS18047 on the write-back block; added `participant &&` guard before accessing its properties Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * Add npm run typecheck as Stop hook in Claude settings Runs a full project typecheck at the end of each Claude turn so type errors surface as feedback before the next message. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 10:59:51 -07:00
if (change > 0) {
return (
<div className="flex items-center gap-1 text-emerald-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-coral-accent">
<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" />
</div>
);
}
}
export function SeasonStandings({
standings,
teamOwnerships = [],
userParticipantIds = [],
showOwnership = true,
isFinalized = false,
title = "Championship Standings",
}: SeasonStandingsProps) {
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 switchId = useId();
const [showStats, setShowStats] = useState(false);
const userParticipantSet = new Set(userParticipantIds);
const ownershipMap = new Map<string, 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
teamOwnerships.forEach((o) => ownershipMap.set(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 checkIfTied = (standing: SeasonStanding): boolean =>
standings.some((o) => o.id !== standing.id && o.position === standing.position);
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 hasChangeColumn = standings.some((s) => s.previousPosition);
const sorted = [...standings].toSorted((a, b) => a.position - b.position);
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
// Index of first row with position > 8; -1 when there is no cutoff.
const firstOver8Idx = sorted.findIndex((s) => s.position > 8);
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>
<Flag className="inline mr-2 h-5 w-5" />
{title}
</CardTitle>
{isFinalized && (
<CardDescription>
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
Season complete fantasy points assigned to top 8 finishers
</span>
</CardDescription>
)}
</CardHeader>
<CardContent>
{standings.length === 0 ? (
<div className="text-center py-8 text-muted-foreground">
<p className="text-sm">No standings data available yet.</p>
<p className="text-xs mt-1">
Championship points will appear here once results are entered.
</p>
</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
{/* Details toggle only shown when position-change data is available */}
{hasChangeColumn && (
<div className="flex items-center justify-end gap-2 mb-2">
<label className="text-xs text-muted-foreground cursor-pointer" htmlFor={switchId}>
Details
</label>
<Switch
id={switchId}
checked={showStats}
onCheckedChange={setShowStats}
/>
</div>
)}
<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>
{hasChangeColumn && showStats && (
<th className="text-left py-1.5 w-16">Change</th>
)}
<th className="text-left py-1.5">Participant</th>
<th className="text-right py-1.5 px-2 w-20">Points</th>
{showOwnership && (
<th className="hidden sm:table-cell text-right py-1.5 pl-4 w-40">Drafted By</th>
)}
</tr>
</thead>
<tbody>
{sorted.map((standing, idx) => {
const ownership = showOwnership
? ownershipMap.get(standing.participant.id) ?? null
: null;
const isTied = checkIfTied(standing);
const isOwned = userParticipantSet.has(standing.participant.id);
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 isTop8 = standing.position <= 8;
const showPointsLineBefore = firstOver8Idx !== -1 && idx === firstOver8Idx;
const isLastBeforePointsLine = firstOver8Idx !== -1 && idx === firstOver8Idx - 1;
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
<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>
)}
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
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.position}` : standing.position}
</td>
{hasChangeColumn && showStats && (
<td className="py-2">
{getMovementIndicator(standing.position, standing.previousPosition)}
</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
<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>
)}
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>
<td className="py-2 px-2 text-right tabular-nums">
<span
className={`font-semibold ${
isTop8 ? "text-electric" : "text-muted-foreground"
}`}
>
{Math.round(parseFloat(standing.championshipPoints))}
</span>
</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>
)}
</tr>
</Fragment>
);
})}
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
</tbody>
</table>
</div>
</>
)}
</CardContent>
</Card>
);
}