From eb7aa42cef7a856f2f854363d7884f18913817e8 Mon Sep 17 00:00:00 2001 From: Chris Parsons <438676+chrisparsons83@users.noreply.github.com> Date: Fri, 20 Feb 2026 08:45:09 -0800 Subject: [PATCH 1/5] Add implementation plan for commissioner-without-team feature (#6) * Add implementation plan for commissioner-without-team feature https://claude.ai/code/session_01NSRMSYtb7jSFbngDS8okn3 * Allow commissioners to exist without owning a team - Add opt-out checkbox on league creation ("I want to play in this league") so the creator can be commissioner-only without claiming a team - Add Commissioner Management card to league settings with add/remove commissioner UI; guards against removing the last commissioner - Add countCommissionersByLeagueId model helper for the last-commissioner guard - Show "No team" indicator on the league homepage next to commissioners who don't own a team in the current season https://claude.ai/code/session_01NSRMSYtb7jSFbngDS8okn3 * Fix code review issues in commissioner management - Add success/error feedback banners to commissioner add/remove actions - Gate allUsers query to admin-only (prevents exposing all users to non-admin commissioners) - Prevent commissioner self-removal in both action (server) and UI (client) - Add "(you)" label next to current user in commissioner list - Remove all unnecessary `any` type casts in favor of inferred types https://claude.ai/code/session_01NSRMSYtb7jSFbngDS8okn3 * Allow commissioners to add league members as co-commissioners Non-admin commissioners can now add users who already own a team in the league as co-commissioners. Admins still see the full user list. Previously the add-commissioner form was admin-only. https://claude.ai/code/session_01NSRMSYtb7jSFbngDS8okn3 --------- Co-authored-by: Claude --- app/models/commissioner.ts | 10 + app/routes/leagues/$leagueId.settings.tsx | 227 ++++++++++++++++++++-- app/routes/leagues/$leagueId.tsx | 8 + app/routes/leagues/new.tsx | 14 +- plan.md | 66 +++++++ 5 files changed, 303 insertions(+), 22 deletions(-) create mode 100644 plan.md diff --git a/app/models/commissioner.ts b/app/models/commissioner.ts index 6d7a55b..e17a558 100644 --- a/app/models/commissioner.ts +++ b/app/models/commissioner.ts @@ -50,6 +50,16 @@ export async function isCommissioner( return !!commissioner; } +export async function countCommissionersByLeagueId( + leagueId: string +): Promise { + const db = database(); + const commissioners = await db.query.commissioners.findMany({ + where: eq(schema.commissioners.leagueId, leagueId), + }); + return commissioners.length; +} + export async function deleteCommissioner(id: string): Promise { const db = database(); await db.delete(schema.commissioners).where(eq(schema.commissioners.id, id)); diff --git a/app/routes/leagues/$leagueId.settings.tsx b/app/routes/leagues/$leagueId.settings.tsx index 6227078..d7ebfbb 100644 --- a/app/routes/leagues/$leagueId.settings.tsx +++ b/app/routes/leagues/$leagueId.settings.tsx @@ -4,7 +4,13 @@ import { getAuth } from "@clerk/react-router/server"; import { format } from "date-fns"; import { CalendarIcon } from "lucide-react"; import { findLeagueById, updateLeague, deleteLeague } from "~/models/league"; -import { isCommissioner } from "~/models/commissioner"; +import { + isCommissioner, + findCommissionersByLeagueId, + createCommissioner, + countCommissionersByLeagueId, + removeCommissionerByLeagueAndUser, +} from "~/models/commissioner"; import { findCurrentSeasonWithSports, updateSeason } from "~/models/season"; import { findTeamsBySeasonId, createManyTeams, deleteTeam, removeTeamOwner, assignTeamOwner } from "~/models/team"; import { findAllUsers, findUserByClerkId, isUserAdminByClerkId } from "~/models/user"; @@ -92,9 +98,21 @@ export async function loader(args: Route.LoaderArgs) { // Check if user is admin const isAdmin = await isUserAdminByClerkId(userId); - // Get all users if admin (for team assignment dropdown) + // Get all users (only for admins - used in team assignment dropdown) const allUsers = isAdmin ? await findAllUsers() : []; + // Get commissioners for this league with user info + const commissioners = await findCommissionersByLeagueId(leagueId); + const commissionerUserData = await Promise.all( + commissioners.map(async (c) => { + const user = await findUserByClerkId(c.userId); + return { + ...c, + userName: user?.username || user?.displayName || "Unknown User", + }; + }) + ); + // Get owner details for teams const ownerIds = teams .map((t) => t.ownerId) @@ -108,11 +126,15 @@ export async function loader(args: Route.LoaderArgs) { : null; }) ); - const ownerMap = new Map( - owners - .filter((o): o is NonNullable => o !== null) - .map((o) => [o.clerkId, o.name]) - ); + const validOwners = owners.filter((o): o is NonNullable => o !== null); + const ownerMap = new Map(validOwners.map((o) => [o.clerkId, o.name])); + + // League members (team owners) - available to all commissioners for adding co-commissioners + const leagueMembers = validOwners.map((o) => ({ + id: o.id, + clerkId: o.clerkId, + name: o.name, + })); return { league, @@ -124,7 +146,10 @@ export async function loader(args: Route.LoaderArgs) { draftSlots, isAdmin, allUsers, + leagueMembers, ownerMap: Object.fromEntries(ownerMap), + commissioners: commissionerUserData, + currentUserId: userId, }; } @@ -471,20 +496,69 @@ export async function action(args: Route.ActionArgs) { } } + if (intent === "add-commissioner") { + const userClerkId = formData.get("userClerkId") as string; + + if (!userClerkId) { + return { error: "User is required" }; + } + + // Check if user is already a commissioner + const alreadyCommissioner = await isCommissioner(leagueId, userClerkId); + if (alreadyCommissioner) { + return { error: "This user is already a commissioner" }; + } + + try { + await createCommissioner({ leagueId, userId: userClerkId }); + return { success: true, message: "Commissioner added successfully" }; + } catch (error) { + console.error("Error adding commissioner:", error); + return { error: "Failed to add commissioner. Please try again." }; + } + } + + if (intent === "remove-commissioner") { + const commissionerUserId = formData.get("commissionerUserId") as string; + + if (!commissionerUserId) { + return { error: "User is required" }; + } + + // Prevent self-removal + if (commissionerUserId === userId) { + return { error: "You cannot remove yourself as a commissioner" }; + } + + // Prevent removing the last commissioner + const count = await countCommissionersByLeagueId(leagueId); + if (count <= 1) { + return { error: "Cannot remove the last commissioner" }; + } + + try { + await removeCommissionerByLeagueAndUser(leagueId, commissionerUserId); + return { success: true, message: "Commissioner removed successfully" }; + } catch (error) { + console.error("Error removing commissioner:", error); + return { error: "Failed to remove commissioner. Please try again." }; + } + } + return { error: "Invalid action" }; } export default function LeagueSettings({ loaderData, actionData }: Route.ComponentProps) { - const { league, season, teams, teamCount, teamsWithOwners, allSportsSeasons, draftSlots, isAdmin, allUsers, ownerMap } = loaderData; + const { league, season, teams, teamCount, teamsWithOwners, allSportsSeasons, draftSlots, isAdmin, allUsers, leagueMembers, ownerMap, commissioners, currentUserId } = loaderData; const navigation = useNavigation(); const [isDeleteDialogOpen, setIsDeleteDialogOpen] = useState(false); const [selectedSports, setSelectedSports] = useState>( new Set(season?.seasonSports?.map((s: any) => s.sportsSeason.id) || []) ); const [draftOrderTeams, setDraftOrderTeams] = useState( - draftSlots.length > 0 - ? draftSlots.map((slot: any) => slot.teamId) - : teams.map((team: any) => team.id) + draftSlots.length > 0 + ? draftSlots.map((slot) => slot.teamId) + : teams.map((team) => team.id) ); const [draftRounds, setDraftRounds] = useState(season?.draftRounds || 20); const [draftDate, setDraftDate] = useState( @@ -498,9 +572,9 @@ export default function LeagueSettings({ loaderData, actionData }: Route.Compone // Update draft order when loader data changes (after randomization) useEffect(() => { - const newOrder = draftSlots.length > 0 - ? draftSlots.map((slot: any) => slot.teamId) - : teams.map((team: any) => team.id); + const newOrder = draftSlots.length > 0 + ? draftSlots.map((slot) => slot.teamId) + : teams.map((team) => team.id); setDraftOrderTeams(newOrder); }, [draftSlots, teams]); @@ -539,7 +613,7 @@ export default function LeagueSettings({ loaderData, actionData }: Route.Compone }; const getTeamById = (teamId: string) => { - return teams.find((t: any) => t.id === teamId); + return teams.find((t) => t.id === teamId); }; return ( @@ -972,7 +1046,7 @@ export default function LeagueSettings({ loaderData, actionData }: Route.Compone
- {teams.map((team: any) => { + {teams.map((team) => { const ownerName = team.ownerId ? ownerMap[team.ownerId] : null; return (
- {allUsers.map((user: any) => { - // Check if user already owns a team in this league - const userOwnsTeam = teams.some((t: any) => t.ownerId === user.clerkId); + {allUsers.map((user) => { + const userOwnsTeam = teams.some((t) => t.ownerId === user.clerkId); return ( - @@ -1047,6 +1120,118 @@ export default function LeagueSettings({ loaderData, actionData }: Route.Compone + {/* Commissioner Management */} + + + Commissioner Management + + Commissioners can manage league settings and oversee the draft. They do not need to own a team. + + + + {actionData?.error && !actionData?.success && ( +
+ {actionData.error} +
+ )} + {actionData?.success && actionData?.message && ( +
+ {actionData.message} +
+ )} + +
+ {commissioners.map((commissioner) => ( +
+
+

+ {commissioner.userName} + {commissioner.userId === currentUserId && ( + (you) + )} +

+ {!teams.some((t) => t.ownerId === commissioner.userId) && ( +

No team in current season

+ )} +
+ {commissioners.length > 1 && commissioner.userId !== currentUserId && ( +
+ + + +
+ )} +
+ ))} +
+ +
+

Add Commissioner

+
+ + + +
+
+
+
+ {/* Danger Zone */} diff --git a/app/routes/leagues/$leagueId.tsx b/app/routes/leagues/$leagueId.tsx index 121eb2b..17fe447 100644 --- a/app/routes/leagues/$leagueId.tsx +++ b/app/routes/leagues/$leagueId.tsx @@ -373,6 +373,9 @@ export default function LeagueHome({ loaderData }: Route.ComponentProps) {
{commissioners.map((commissioner) => { const commissionerName = commissionerMap[commissioner.userId]; + const hasTeam = teams.some( + (t) => t.ownerId === commissioner.userId + ); return (
)}

+ {!hasTeam && ( +

+ No team +

+ )}
); })} diff --git a/app/routes/leagues/new.tsx b/app/routes/leagues/new.tsx index e3bb3e5..7ee7110 100644 --- a/app/routes/leagues/new.tsx +++ b/app/routes/leagues/new.tsx @@ -194,11 +194,12 @@ export async function action(args: Route.ActionArgs) { } // Create teams with fun random names + const joinAsPlayer = formData.get("joinAsPlayer") === "on"; const teamNames = generateUniqueTeamNames(teamCountNum); const teams = teamNames.map((name, i) => ({ seasonId: season.id, name, - ownerId: i === 0 ? userId : null, // Assign first team to creator + ownerId: joinAsPlayer && i === 0 ? userId : null, })); await createManyTeams(teams); @@ -485,6 +486,17 @@ export default function NewLeague({ loaderData, actionData }: Route.ComponentPro
+
+ + +
+ {actionData?.error && (
{actionData.error} diff --git a/plan.md b/plan.md new file mode 100644 index 0000000..76c6b86 --- /dev/null +++ b/plan.md @@ -0,0 +1,66 @@ +# Plan: Commissioner Without a Team + +## Current State + +**At the database level**, commissioners and teams are already independent: +- Commissioners are stored in the `commissioners` table (league-level, keyed by `leagueId` + `userId`) +- Teams are stored in the `teams` table (season-level, keyed by `seasonId` + `ownerId`) +- A commissioner does NOT need a team to access the league homepage or draft room — this already works + +**The actual problem** is that there's no way to *become* a commissioner without also getting a team: +1. **League creation** (`app/routes/leagues/new.tsx:198-201`): The creator is always assigned the first team (`ownerId: i === 0 ? userId : null`) +2. **No "Add Commissioner" UI**: There's no way for a league creator to add additional commissioners at all +3. **Invite link flow** (`app/routes/i.$inviteCode.tsx`): Joining always assigns a team — there's no option to join as commissioner-only + +**Also**, the league creation flow has no opt-out for team ownership — the creator always gets Team 1. + +## Changes + +### 1. League creation: Add "Join as player" checkbox +**File**: `app/routes/leagues/new.tsx` + +- Add a checkbox: "I want to play in this league" (checked by default) +- When unchecked, skip assigning the creator to the first team (all teams get `ownerId: null`) +- When checked, behavior stays the same (creator gets first team) + +### 2. Add Commissioner Management UI to League Settings +**File**: `app/routes/leagues/$leagueId.settings.tsx` + +- Add a new "Commissioner Management" card section +- Show current commissioners with a "Remove" button (cannot remove if they're the last commissioner) +- Add an "Add Commissioner" form with a user search/select dropdown + - Only show users who are already members of the league (team owners) OR allow adding by searching all users + - Since only the site admin can currently assign users to teams, let's keep it simple: commissioners can add any registered user as a commissioner +- Backend actions: `add-commissioner` and `remove-commissioner` intents + +### 3. Model changes +**File**: `app/models/commissioner.ts` + +- Already has `createCommissioner`, `deleteCommissioner`, `removeCommissionerByLeagueAndUser` — these are sufficient +- Add `countCommissionersByLeagueId` to prevent removing the last commissioner + +### 4. Settings loader changes +**File**: `app/routes/leagues/$leagueId.settings.tsx` + +- Load commissioners list and user data for display +- Load all users list for the "add commissioner" dropdown (reuse existing `allUsers` pattern but make it available to commissioners, not just admins) + +### 5. League homepage: Show commissioner-only indicator +**File**: `app/routes/leagues/$leagueId.tsx` + +- In the commissioners list, if a commissioner doesn't own a team in the current season, show a "(No team)" indicator next to their name +- This helps distinguish playing commissioners from non-playing commissioners + +## Files to modify + +1. `app/routes/leagues/new.tsx` — Add "play in league" checkbox, conditionally assign first team +2. `app/models/commissioner.ts` — Add `countCommissionersByLeagueId` helper +3. `app/routes/leagues/$leagueId.settings.tsx` — Add commissioner management card (loader + action + UI) +4. `app/routes/leagues/$leagueId.server.ts` — Pass team ownership info for commissioners to the homepage +5. `app/routes/leagues/$leagueId.tsx` — Show "(No team)" indicator for commissioners without teams + +## Out of scope (not changing) + +- The invite link flow — this is for players joining, not commissioners. Commissioners should be added through settings. +- The `createdBy` vs commissioners table inconsistency in draft control endpoints — that's a separate bug +- Adding a way for a commissioner to later claim/release a team mid-season — possible future work but not needed here From 8c9b131c00e4252f9ab4db20742c5d0df1ebb563 Mon Sep 17 00:00:00 2001 From: Chris Parsons <438676+chrisparsons83@users.noreply.github.com> Date: Fri, 20 Feb 2026 10:00:35 -0800 Subject: [PATCH 2/5] Update scoring system and add Counter-Strike to special scoring (#7) * Update how-to-play scoring rules to match standard scoring - Update placement points: 1st 100, 2nd 70, 3rd-4th 45, 5th-8th 20 - Add Counter-Strike to the special majors scoring section alongside Golf & Tennis - Add qualifying points breakdown (8/5/3/2/1) per major tournament https://claude.ai/code/session_01JUAqVZgo7M7XRBLzpeaWpH * Fix scoring to show all 8 individual placements correctly Display each placement (1st-8th) with its own point value rather than grouping/averaging: 100, 70, 50, 40, 25, 25, 15, 15 https://claude.ai/code/session_01JUAqVZgo7M7XRBLzpeaWpH --------- Co-authored-by: Claude --- app/routes/how-to-play.tsx | 74 ++++++++++++++++++++++++++------------ 1 file changed, 51 insertions(+), 23 deletions(-) diff --git a/app/routes/how-to-play.tsx b/app/routes/how-to-play.tsx index b792e37..f4b3450 100644 --- a/app/routes/how-to-play.tsx +++ b/app/routes/how-to-play.tsx @@ -50,22 +50,40 @@ export default function HowToPlay() { Points are awarded based on final standings. The better your pick finishes, the more points you earn:

-
-
- 🥇 1st Place - 80 points -
-
- 🥈 2nd Place - 50 points -
-
- 🥉 3rd-4th Place - 30 points -
-
- 5th-8th Place - 20 points +
+
+
+ 🥇 1st Place + 100 pts +
+
+ 🥈 2nd Place + 70 pts +
+
+ 🥉 3rd Place + 50 pts +
+
+ 4th Place + 40 pts +
+
+ 5th Place + 25 pts +
+
+ 6th Place + 25 pts +
+
+ 7th Place + 15 pts +
+
+ 8th Place + 15 pts +

@@ -76,27 +94,37 @@ export default function HowToPlay() {

- ⛳ Special Scoring for Golf & Tennis + ⛳ Special Scoring for Golf, Tennis & Counter-Strike

- Golf and tennis work a bit differently since they have multiple - major tournaments: + Golf, tennis, and Counter-Strike work a bit differently since they + each have multiple major tournaments throughout the year:

  • - Players earn qualifying points at each major tournament throughout - the year + Players/teams earn qualifying points at each major tournament + throughout the year
  • After all majors are complete, we add up everyone's qualifying points
  • - The top 8 players by total qualifying points then earn the regular - scoring points (80, 50, 30, 20) + The top 8 by total qualifying points then earn the regular scoring + points (100, 70, 50, 40, 25, 25, 15, 15)
  • This rewards consistency across all the major tournaments!
+
+

Qualifying points per major:

+
+ 1st Place8 QP + 2nd Place5 QP + 3rd Place3 QP + 4th Place2 QP + 5th Place1 QP +
+
From 47b69ce9cf2621cd4ac4a9c505ea9bf05bc121fd Mon Sep 17 00:00:00 2001 From: Chris Parsons <438676+chrisparsons83@users.noreply.github.com> Date: Fri, 20 Feb 2026 10:28:37 -0800 Subject: [PATCH 3/5] Include commissioner leagues in user's active leagues (#8) * Show leagues where user is a member regardless of team assignment The homepage now uses a union query to show leagues where the user either has a team in the current season OR is a commissioner. Previously, commissioners without a team could not see their leagues on the homepage. https://claude.ai/code/session_01E3ugKTfatkEc5TcDGXvHiW * Fix stale empty state card title in home route Update 'No Active Leagues' to 'No Leagues' to match the updated description which is now about membership rather than active seasons. https://claude.ai/code/session_01E3ugKTfatkEc5TcDGXvHiW * Fix union import: use two queries with JS deduplication drizzle-orm 0.36.x does not export a standalone union() function. Replace with two awaited queries merged via a Map (dedup by id), then sorted by createdAt descending in JS. https://claude.ai/code/session_01E3ugKTfatkEc5TcDGXvHiW --------- Co-authored-by: Claude --- app/models/league.ts | 43 +++++++++++++++++++++++++++++-------------- app/routes/home.tsx | 4 ++-- 2 files changed, 31 insertions(+), 16 deletions(-) diff --git a/app/models/league.ts b/app/models/league.ts index 7f92e8b..50537e0 100644 --- a/app/models/league.ts +++ b/app/models/league.ts @@ -71,23 +71,38 @@ export async function findLeaguesWithActiveSeasonsByUserId( ): Promise { const db = database(); - // Query to find all leagues where user has a team in the current season - const leagues = await db - .selectDistinct({ - id: schema.leagues.id, - name: schema.leagues.name, - createdBy: schema.leagues.createdBy, - currentSeasonId: schema.leagues.currentSeasonId, - isPublicDraftBoard: schema.leagues.isPublicDraftBoard, - createdAt: schema.leagues.createdAt, - updatedAt: schema.leagues.updatedAt, - }) + const leagueFields = { + id: schema.leagues.id, + name: schema.leagues.name, + createdBy: schema.leagues.createdBy, + currentSeasonId: schema.leagues.currentSeasonId, + isPublicDraftBoard: schema.leagues.isPublicDraftBoard, + createdAt: schema.leagues.createdAt, + updatedAt: schema.leagues.updatedAt, + }; + + // Leagues where user has a team in the current season + const leaguesWithTeam = await db + .select(leagueFields) .from(schema.leagues) .innerJoin(schema.teams, eq(schema.teams.seasonId, schema.leagues.currentSeasonId)) - .where(eq(schema.teams.ownerId, userId)) - .orderBy(desc(schema.leagues.createdAt)); + .where(eq(schema.teams.ownerId, userId)); - return leagues; + // Leagues where user is a commissioner (with or without a team) + const leaguesAsCommissioner = await db + .select(leagueFields) + .from(schema.leagues) + .innerJoin(schema.commissioners, eq(schema.commissioners.leagueId, schema.leagues.id)) + .where(eq(schema.commissioners.userId, userId)); + + // Deduplicate by id in case user is both a commissioner and has a team, then sort + const leagueMap = new Map(); + for (const league of [...leaguesWithTeam, ...leaguesAsCommissioner]) { + leagueMap.set(league.id, league); + } + return [...leagueMap.values()].sort( + (a, b) => b.createdAt.getTime() - a.createdAt.getTime() + ); } /** diff --git a/app/routes/home.tsx b/app/routes/home.tsx index e2dac3b..4674a79 100644 --- a/app/routes/home.tsx +++ b/app/routes/home.tsx @@ -92,9 +92,9 @@ export default function Home({ loaderData }: Route.ComponentProps) { {leagues.length === 0 ? ( - No Active Leagues + No Leagues - You don't have any teams in leagues with active seasons yet + You aren't a member of any leagues yet From 83994b7a746cca971971349a1a99344113471d78 Mon Sep 17 00:00:00 2001 From: Chris Parsons <438676+chrisparsons83@users.noreply.github.com> Date: Fri, 20 Feb 2026 11:16:34 -0800 Subject: [PATCH 4/5] Fix draft round showing Infinity before draft starts (#9) When draftSlots is empty (pre-draft state), dividing by zero caused Math.ceil to return Infinity. Guard the division to fall back to round 1. https://claude.ai/code/session_017mLmGc5wTrESaDeRQHkSPy Co-authored-by: Claude --- app/routes/leagues/$leagueId.draft-board.$seasonId.tsx | 2 +- app/routes/leagues/$leagueId.draft.$seasonId.tsx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/routes/leagues/$leagueId.draft-board.$seasonId.tsx b/app/routes/leagues/$leagueId.draft-board.$seasonId.tsx index a254e44..490f8f3 100644 --- a/app/routes/leagues/$leagueId.draft-board.$seasonId.tsx +++ b/app/routes/leagues/$leagueId.draft-board.$seasonId.tsx @@ -150,7 +150,7 @@ export default function DraftBoard() { {season.league.name} - {season.year} Draft Board
- Round: {Math.ceil(currentPick / totalTeams)} + Round: {totalTeams > 0 ? Math.ceil(currentPick / totalTeams) : 1} Pick: {currentPick} {season.status.replace("_", " ")} diff --git a/app/routes/leagues/$leagueId.draft.$seasonId.tsx b/app/routes/leagues/$leagueId.draft.$seasonId.tsx index a06917f..76c0ffc 100644 --- a/app/routes/leagues/$leagueId.draft.$seasonId.tsx +++ b/app/routes/leagues/$leagueId.draft.$seasonId.tsx @@ -654,7 +654,7 @@ export default function DraftRoom() { }; // Calculate current round - const currentRound = Math.ceil(currentPick / draftSlots.length); + const currentRound = draftSlots.length > 0 ? Math.ceil(currentPick / draftSlots.length) : 1; // Determine whose turn it is const totalTeams = draftSlots.length; From 332352278221bebe32e10b0d07a1d535faab3aa2 Mon Sep 17 00:00:00 2001 From: Chris Parsons <438676+chrisparsons83@users.noreply.github.com> Date: Fri, 20 Feb 2026 11:35:35 -0800 Subject: [PATCH 5/5] Fix sports season page 404 when sports season is linked to multiple leagues (#10) The loader was grabbing seasonSports[0] blindly instead of filtering by the current league, causing a 404 when the first linked fantasy season belonged to a different league. https://claude.ai/code/session_01Mq6BqFhiYFuJyDcc8ueVJc Co-authored-by: Claude --- ...agueId.sports-seasons.$sportsSeasonId.server.ts | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.server.ts b/app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.server.ts index 31eaaaf..27bf6ee 100644 --- a/app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.server.ts +++ b/app/routes/leagues/$leagueId.sports-seasons.$sportsSeasonId.server.ts @@ -8,10 +8,8 @@ import { findUserByClerkId, } from "~/models"; import { getDraftPicks } from "~/models/draft-pick"; -import { findSportsSeasonById } from "~/models/sports-season"; import { getSeasonResults } from "~/models/participant-season-result"; import { getQPStandings } from "~/models/qualifying-points"; -import { findSeasonById } from "~/models/season"; import { database } from "~/database/context"; import * as schema from "~/database/schema"; import { eq } from "drizzle-orm"; @@ -64,19 +62,17 @@ export async function loader(args: Route.LoaderArgs) { // Get the fantasy season for this league (to get teams and draft picks) // We need to find which season in this league includes this sports season - const seasonSport = sportsSeason.seasonSports?.[0]; + // Filter by leagueId since a sports season can be linked to multiple leagues + const seasonSport = sportsSeason.seasonSports?.find( + (ss) => ss.season.leagueId === leagueId + ); if (!seasonSport) { throw new Response("This sports season is not linked to this league", { status: 404, }); } - const season = await findSeasonById(seasonSport.seasonId); - if (!season || season.leagueId !== leagueId) { - throw new Response("This sports season is not linked to this league", { - status: 404, - }); - } + const season = seasonSport.season; // Fetch teams and draft picks to determine ownership const teams = await findTeamsBySeasonId(season.id);