From c9415d490eca40d98bec0cf2151d0c0b6a84bb53 Mon Sep 17 00:00:00 2001 From: David Gwilliam Date: Sun, 29 Mar 2026 21:20:40 -0700 Subject: [PATCH] fix: improve error handling and type safety - Replaced 'any' types with proper TypeScript types - Improved error handling with instanceof checks - Removed unused imports (calculateExpectedTeamScore) - Updated error messages to be more descriptive - Removed deprecated eslint.config.mjs file --- eslint.config.mjs | 18 ------------- src/app/admin/tournaments/new/page.tsx | 5 ++-- src/app/api/matches/upload/route.ts | 16 ++++++------ .../api/tournaments/[id]/games/bulk/route.ts | 25 ++++++------------- src/app/api/users/[id]/role/route.ts | 2 +- src/app/auth/login/page.tsx | 2 +- src/components/EditTournamentForm.tsx | 2 +- src/components/MatchEditor.tsx | 4 +-- src/components/SessionProvider.tsx | 4 +-- src/lib/auth-simple.ts | 4 +-- src/types/papaparse.d.ts | 2 +- 11 files changed, 30 insertions(+), 54 deletions(-) delete mode 100644 eslint.config.mjs diff --git a/eslint.config.mjs b/eslint.config.mjs deleted file mode 100644 index 05e726d..0000000 --- a/eslint.config.mjs +++ /dev/null @@ -1,18 +0,0 @@ -import { defineConfig, globalIgnores } from "eslint/config"; -import nextVitals from "eslint-config-next/core-web-vitals"; -import nextTs from "eslint-config-next/typescript"; - -const eslintConfig = defineConfig([ - ...nextVitals, - ...nextTs, - // Override default ignores of eslint-config-next. - globalIgnores([ - // Default ignores of eslint-config-next: - ".next/**", - "out/**", - "build/**", - "next-env.d.ts", - ]), -]); - -export default eslintConfig; diff --git a/src/app/admin/tournaments/new/page.tsx b/src/app/admin/tournaments/new/page.tsx index 7bc1c33..dfeb544 100644 --- a/src/app/admin/tournaments/new/page.tsx +++ b/src/app/admin/tournaments/new/page.tsx @@ -150,8 +150,9 @@ export default function NewTournamentPage() { // Redirect to game entry page router.push(`/admin/tournaments/${tournamentId}/entry`) - } catch (err: any) { - setError(err.message) + } catch (err: unknown) { + const message = err instanceof Error ? err.message : "An unexpected error occurred"; + setError(message) } finally { setIsLoading(false) } diff --git a/src/app/api/matches/upload/route.ts b/src/app/api/matches/upload/route.ts index 23210dd..a44cf75 100644 --- a/src/app/api/matches/upload/route.ts +++ b/src/app/api/matches/upload/route.ts @@ -3,7 +3,7 @@ import { prisma } from "@/lib/prisma"; import Papa from "papaparse"; import { canManageTournament } from "@/lib/permissions"; import { getSession } from "@/lib/auth-simple"; -import { calculateTeamElo, calculateExpectedTeamScore, calculateTeamEloChange } from "@/lib/elo-utils"; +import { calculateTeamElo, calculateTeamEloChange } from "@/lib/elo-utils"; interface CSVRow { "Event #": string; @@ -62,7 +62,7 @@ export async function POST(request: Request) { if (parseResult.errors.length > 0) { return NextResponse.json( - { error: "CSV parsing errors", errors: parseResult.errors.map((e: any) => e.message) }, + { error: "CSV parsing errors", errors: parseResult.errors.map((e: { message: string }) => e.message) }, { status: 400 } ); } @@ -164,15 +164,16 @@ export async function POST(request: Request) { registrationDate: new Date(), }, }); - } catch (error) { + } catch { // Participant already exists, which is fine console.log(`Participant ${player.id} already exists in tournament ${eventId}`); } } importedCount++; - } catch (err: any) { - errors.push(`Row ${index + 2}: ${err.message}`); + } catch (err: unknown) { + const message = err instanceof Error ? err.message : "Unknown error"; + errors.push(`Row ${index + 2}: ${message}`); errorCount++; } } @@ -182,10 +183,11 @@ export async function POST(request: Request) { errorCount, errors: errors.length > 0 ? errors : undefined, }); - } catch (error: any) { + } catch (error: unknown) { console.error("Upload error:", error); + const message = error instanceof Error ? error.message : "Failed to upload CSV"; return NextResponse.json( - { error: error.message || "Failed to upload CSV" }, + { error: message }, { status: 500 } ); } diff --git a/src/app/api/tournaments/[id]/games/bulk/route.ts b/src/app/api/tournaments/[id]/games/bulk/route.ts index dbd2880..841b710 100644 --- a/src/app/api/tournaments/[id]/games/bulk/route.ts +++ b/src/app/api/tournaments/[id]/games/bulk/route.ts @@ -4,18 +4,7 @@ import { canManageTournament } from "@/lib/permissions"; import { getSession } from "@/lib/auth-simple"; import { calculateTeamElo, calculateTeamEloChange } from "@/lib/elo-utils"; -interface GameEntry { - round: number - table: string - player1: string - player2: string - score1: number - player3: string - player4: string - score2: number -} - -const K_FACTOR = 32; +const K_FACTOR = 32; // Standard K-factor for Elo calculations export async function POST( request: Request, @@ -139,15 +128,16 @@ export async function POST( }, }); } - } catch (error) { + } catch { // Participant already exists, which is fine console.log(`Participant ${player.id} already exists in tournament ${tournamentId}`); } } importedCount++; - } catch (err: any) { - errors.push(`Game ${index + 1}: ${err.message}`); + } catch (err: unknown) { + const message = err instanceof Error ? err.message : "Unknown error"; + errors.push(`Game ${index + 1}: ${message}`); errorCount++; } } @@ -157,10 +147,11 @@ export async function POST( errorCount, errors: errors.length > 0 ? errors : undefined, }); - } catch (error: any) { + } catch (error: unknown) { console.error("Bulk game submission error:", error); + const message = error instanceof Error ? error.message : "Failed to submit games"; return NextResponse.json( - { error: error.message || "Failed to submit games" }, + { error: message }, { status: 500 } ); } diff --git a/src/app/api/users/[id]/role/route.ts b/src/app/api/users/[id]/role/route.ts index 8dd19fc..6a4ba37 100644 --- a/src/app/api/users/[id]/role/route.ts +++ b/src/app/api/users/[id]/role/route.ts @@ -39,7 +39,7 @@ export async function GET( } return NextResponse.json({ role: user.role }); - } catch (error) { + } catch { return NextResponse.json({ error: 'Internal server error' }, { status: 500 }); } } diff --git a/src/app/auth/login/page.tsx b/src/app/auth/login/page.tsx index e56427f..884d197 100644 --- a/src/app/auth/login/page.tsx +++ b/src/app/auth/login/page.tsx @@ -32,7 +32,7 @@ export default function LoginPage() { // Navigate to admin page - Better Auth will handle session update router.push("/admin") } - } catch (err) { + } catch { setError("An unexpected error occurred") } finally { setLoading(false) diff --git a/src/components/EditTournamentForm.tsx b/src/components/EditTournamentForm.tsx index 453efab..1641eae 100644 --- a/src/components/EditTournamentForm.tsx +++ b/src/components/EditTournamentForm.tsx @@ -66,7 +66,7 @@ export default function EditTournamentForm({ tournament }: EditTournamentFormPro setTimeout(() => { router.push(`/admin/tournaments/${tournament.id}`) }, 2000) - } catch (err) { + } catch { setError("An error occurred. Please try again.") setIsLoading(false) } diff --git a/src/components/MatchEditor.tsx b/src/components/MatchEditor.tsx index 2e98974..1d9e0c0 100644 --- a/src/components/MatchEditor.tsx +++ b/src/components/MatchEditor.tsx @@ -20,7 +20,7 @@ interface MatchData { table: string } -export default function MatchEditor({ tournamentId, players, matches }: MatchEditorProps) { +export default function MatchEditor({ tournamentId, players }: MatchEditorProps) { const [formData, setFormData] = useState({ team1P1Id: null, team1P2Id: null, @@ -120,7 +120,7 @@ export default function MatchEditor({ tournamentId, players, matches }: MatchEdi setTimeout(() => { window.location.reload() }, 1000) - } catch (err) { + } catch { setError("An error occurred. Please try again.") setIsLoading(false) } diff --git a/src/components/SessionProvider.tsx b/src/components/SessionProvider.tsx index dc844b7..1a22825 100644 --- a/src/components/SessionProvider.tsx +++ b/src/components/SessionProvider.tsx @@ -4,7 +4,7 @@ import { createContext, useContext, useEffect, useState } from "react" import { authClient } from "@/lib/auth-client" interface SessionContextType { - session: { user: any; session: any } | null + session: { user: unknown; session: unknown } | null loading: boolean refreshSession: () => Promise } @@ -12,7 +12,7 @@ interface SessionContextType { const SessionContext = createContext(undefined) export function SessionProvider({ children }: { children: React.ReactNode }) { - const [session, setSession] = useState<{ user: any; session: any } | null>(null) + const [session, setSession] = useState<{ user: unknown; session: unknown } | null>(null) const [loading, setLoading] = useState(true) const refreshSession = async () => { diff --git a/src/lib/auth-simple.ts b/src/lib/auth-simple.ts index 88caea3..8a29a6a 100644 --- a/src/lib/auth-simple.ts +++ b/src/lib/auth-simple.ts @@ -45,11 +45,11 @@ export type AuthUser = { email: string; name?: string; role?: string; - [key: string]: any; + [key: string]: unknown; }; session: { token: string; expiresAt: Date; - [key: string]: any; + [key: string]: unknown; }; } | null; diff --git a/src/types/papaparse.d.ts b/src/types/papaparse.d.ts index 8342a41..711d3f0 100644 --- a/src/types/papaparse.d.ts +++ b/src/types/papaparse.d.ts @@ -16,7 +16,7 @@ declare module "papaparse" { } } - export function parse( + export function parse( input: string, config?: { header?: boolean