From 82f95f4078ea74f50c161c62872cc24c868dbecc Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 16 Jun 2026 04:45:23 +0000 Subject: [PATCH] Fix oxlint errors across event view changes - Use !== instead of != for null/undefined comparisons - Replace non-null assertions with type-predicate filters - Use Array#toSorted() instead of mutating sort() - Replace manual thenable mock with a real Promise + attached orderBy - Remove unused imports/variables (Badge, CardHeader, CardTitle, eliminated, advanced, ownershipMap) - Hoist makeChampionsQpConfig to module scope since it captures no closure variables https://claude.ai/code/session_01RYK7NCjcaah545979wupcM --- app/components/events/Cs2MajorEventView.tsx | 8 +------- app/models/__tests__/cs2-major-stage.test.ts | 16 ++++++++-------- app/models/__tests__/upcoming-calendar.test.ts | 18 +++++++----------- app/models/scoring-event.ts | 2 +- ...s.$sportsSeasonId.events.$eventId.server.ts | 10 +++++----- ...seasons.$sportsSeasonId.events.$eventId.tsx | 4 ++-- 6 files changed, 24 insertions(+), 34 deletions(-) diff --git a/app/components/events/Cs2MajorEventView.tsx b/app/components/events/Cs2MajorEventView.tsx index 474b80f..07d203c 100644 --- a/app/components/events/Cs2MajorEventView.tsx +++ b/app/components/events/Cs2MajorEventView.tsx @@ -1,6 +1,5 @@ import { useState } from "react"; -import { Badge } from "~/components/ui/badge"; -import { Card, CardContent, CardHeader, CardTitle } from "~/components/ui/card"; +import { Card, CardContent } from "~/components/ui/card"; import { Table, TableBody, @@ -72,10 +71,6 @@ function SwissStageTab({ const matchesInStage = matches.filter((m) => m.matchStage === stage); - // Group teams by W-L record (approximated from stageEliminatedWins if eliminated, else unknown) - const eliminated = stageResults.filter((r) => r.stageEliminated === stage); - const advanced = stageResults.filter((r) => r.stageEliminated === null && r.stageEntry <= stage); - if (stageResults.length === 0) { return (

No teams assigned to this stage yet.

@@ -197,7 +192,6 @@ export function Cs2MajorEventView({ ); const tabs: StageTab[] = [1, 2, 3, "champions"]; - const ownershipMap = new Map(teamOwnerships.map((o) => [o.participantId, o])); return (
diff --git a/app/models/__tests__/cs2-major-stage.test.ts b/app/models/__tests__/cs2-major-stage.test.ts index cb730e2..90a2d04 100644 --- a/app/models/__tests__/cs2-major-stage.test.ts +++ b/app/models/__tests__/cs2-major-stage.test.ts @@ -226,15 +226,15 @@ describe("byStageFullField computation", () => { // floor scores for the 8 advancing Champions Stage teams. The floor is a tie-split // of placement slots 5–8 (the QF-loser tier that all bracket teams are guaranteed // to reach at minimum). -describe("Champions Stage floor QP calculation", () => { - function makeChampionsQpConfig(): Map { - // Default QP: 1→20, 2→14, 3→10, 4→8, 5→5, 6→5, 7→3, 8→3 - return new Map([ - [1, 20], [2, 14], [3, 10], [4, 8], - [5, 5], [6, 5], [7, 3], [8, 3], - ]); - } +function makeChampionsQpConfig(): Map { + // Default QP: 1→20, 2→14, 3→10, 4→8, 5→5, 6→5, 7→3, 8→3 + return new Map([ + [1, 20], [2, 14], [3, 10], [4, 8], + [5, 5], [6, 5], [7, 3], [8, 3], + ]); +} +describe("Champions Stage floor QP calculation", () => { it("floor QP with default config equals tie-split of slots 5–8 = 4", () => { const config = makeChampionsQpConfig(); // 4 QF losers fill slots 5,6,7,8: (5+5+3+3)/4 = 4 diff --git a/app/models/__tests__/upcoming-calendar.test.ts b/app/models/__tests__/upcoming-calendar.test.ts index 814f35a..ffa5587 100644 --- a/app/models/__tests__/upcoming-calendar.test.ts +++ b/app/models/__tests__/upcoming-calendar.test.ts @@ -36,22 +36,18 @@ vi.mock("~/database/schema", () => ({ // Works for both resolution points: // - await chain.from().innerJoin().where() (game-time lookup) // - await chain.from().innerJoin().where().orderBy() (group stage) -// The chain is thenable, so `await chain` (or any sub-chain that returns `this`) -// resolves to `rows`. orderBy also returns a resolved promise for the same rows. +// `where()` resolves to a real Promise (so `await` works directly) with an +// `orderBy` method attached for callers that chain further before awaiting. function makeGroupStageSelectChain(rows: unknown[]) { - const chain: Record & { - then?: (resolve: (v: unknown[]) => unknown, reject?: (e: unknown) => unknown) => Promise; - } = { + const whereResult = Object.assign(Promise.resolve(rows), { + orderBy: vi.fn().mockResolvedValue(rows), + }); + const chain = { from: vi.fn().mockReturnThis(), innerJoin: vi.fn().mockReturnThis(), leftJoin: vi.fn().mockReturnThis(), - where: vi.fn(), - orderBy: vi.fn().mockResolvedValue(rows), - then: (resolve: (v: unknown[]) => unknown, reject?: (e: unknown) => unknown) => - Promise.resolve(rows).then(resolve, reject), + where: vi.fn().mockReturnValue(whereResult), }; - // where returns the chain (which is thenable), so await chain.where() → rows - (chain.where as ReturnType).mockReturnValue(chain); return chain; } diff --git a/app/models/scoring-event.ts b/app/models/scoring-event.ts index 6d26263..4752663 100644 --- a/app/models/scoring-event.ts +++ b/app/models/scoring-event.ts @@ -352,7 +352,7 @@ export async function getUpcomingScoringEvents( return candidates.length > 0 ? Math.min(...candidates) : Infinity; }; - return [...events].sort((a, b) => getEffectiveMs(a) - getEffectiveMs(b)).slice(0, limit); + return events.toSorted((a, b) => getEffectiveMs(a) - getEffectiveMs(b)).slice(0, limit); } /** diff --git a/app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.events.$eventId.server.ts b/app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.events.$eventId.server.ts index 17473a1..abcb194 100644 --- a/app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.events.$eventId.server.ts +++ b/app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.events.$eventId.server.ts @@ -177,8 +177,8 @@ export async function loader({ params, request }: Route.LoaderArgs) { groupStandings = groups.map((group) => { const groupMatches = matchesByGroupId.get(group.id) ?? []; const members = (group.members ?? []) - .filter((m) => m.participant != null) - .map((m) => ({ participantId: m.participant!.id, participantName: m.participant!.name })); + .filter((m): m is typeof m & { participant: NonNullable } => m.participant !== null) + .map((m) => ({ participantId: m.participant.id, participantName: m.participant.name })); return { groupName: group.groupName, standings: computeGroupStandings( @@ -208,7 +208,7 @@ export async function loader({ params, request }: Route.LoaderArgs) { with: { participant: true }, }); const preEliminatedParticipants = allResults - .filter((r): r is typeof r & { participant: NonNullable } => r.participant != null) + .filter((r): r is typeof r & { participant: NonNullable } => r.participant !== null) .map((r) => ({ id: r.participant.id, name: r.participant.name })); return { @@ -229,10 +229,10 @@ export async function loader({ params, request }: Route.LoaderArgs) { // QP major tournament / racing / other const eventResultRows = await getEventResults(eventId); const eventResults = eventResultRows - .filter((r) => r.placement !== null && !r.notParticipating) + .filter((r): r is typeof r & { placement: number } => r.placement !== null && !r.notParticipating) .map((r) => ({ id: r.id, - placement: r.placement!, + placement: r.placement, qualifyingPointsAwarded: r.qualifyingPointsAwarded, rawScore: r.rawScore, seasonParticipantId: r.seasonParticipantId, diff --git a/app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.events.$eventId.tsx b/app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.events.$eventId.tsx index ba8b92e..ce01de8 100644 --- a/app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.events.$eventId.tsx +++ b/app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.events.$eventId.tsx @@ -146,9 +146,9 @@ export default function EventDetailPage({ loaderData }: Route.ComponentProps) { {ownership?.teamName ?? "—"} - {r.qualifyingPointsAwarded != null + {r.qualifyingPointsAwarded !== null ? parseFloat(r.qualifyingPointsAwarded).toFixed(2) - : r.rawScore != null + : r.rawScore !== null ? r.rawScore : "—"}