brackt/app/routes/leagues/$leagueId.standings.$seasonId.teams.$teamId.tsx
Chris Parsons 0ba2b391f8
Fix draft order initialization for teams added mid-season (#449)
* Fix draft order showing only new team when league size increased before order was set

When a commissioner increased the team count on a league that had teams
but no draft order set yet, the server created draft slots only for the
newly-added teams. This left the DB with N+K teams but only K slots,
causing the drag-and-drop list to display only the K new teams.

Two fixes:
1. Server: only append new draft slots when an order was already set
   (existingSlots.length > 0). If no order exists yet, skip slot creation
   so the page correctly treats the order as unset for all teams.
2. Frontend: buildDraftOrderTeams() appends any unslotted teams after the
   slotted ones, so a partially-corrupt DB state still shows all teams.

https://claude.ai/code/session_01M3H55gnMxRztJK9KMXqqZo

* Address code review feedback on draft order bug fix

- Move buildDraftOrderTeams to app/lib/draft-order.ts so it is importable
  and testable; remove the local copy from the settings component
- Add unit tests for buildDraftOrderTeams covering the empty, full, and
  partial-slot cases
- Tighten the draft slot guard from existingSlots.length > 0 to
  existingSlots.length === currentTeamCount so partial legacy states are
  treated the same as "order not set"
- Condense the 3-line server comment to a single line per project style
- Rename getNumTeamsInSeason → getNumDraftSlotsBySeasonId to reflect what
  the function actually counts, and update its one call site
- Add tests for the server-side slot-creation guard logic

https://claude.ai/code/session_01M3H55gnMxRztJK9KMXqqZo

* Fix lint: move shouldAppendDraftSlots to outer scope

oxlint (consistent-function-scoping) requires functions that don't close
over any variables to be defined at the outer scope rather than inside a
describe block.

https://claude.ai/code/session_01M3H55gnMxRztJK9KMXqqZo

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-19 18:22:47 -07:00

58 lines
1.6 KiB
TypeScript

import { useLoaderData } from "react-router";
import type { Route } from "./+types/$leagueId.standings.$seasonId.teams.$teamId";
import { TeamScoreBreakdown } from "~/components/standings/TeamScoreBreakdown";
import { getTeamScoreBreakdown, getTeamStanding } from "~/models/standings";
import { getNumDraftSlotsBySeasonId } from "~/models/draft-slot";
export function meta({ data }: Route.MetaArgs): Route.MetaDescriptors {
return [{ title: `${data?.breakdown?.team?.name ?? "Team"} - Brackt` }];
}
/**
* Team score breakdown page
* Shows all drafted participants with their placements and points
* Phase 4.3: Team breakdown pages
*/
export async function loader({ params }: Route.LoaderArgs) {
const { leagueId, seasonId, teamId } = params;
// Get team breakdown with all picks
const [breakdown, standing, numTeams] = await Promise.all([
getTeamScoreBreakdown(teamId, seasonId),
getTeamStanding(teamId, seasonId),
getNumDraftSlotsBySeasonId(seasonId),
]);
if (!breakdown) {
throw new Response("Team not found", { status: 404 });
}
return {
leagueId,
seasonId,
teamId,
breakdown: {
...breakdown,
team: breakdown.team || null,
},
standing,
numTeams,
};
}
export default function TeamBreakdownPage() {
const { leagueId, seasonId, breakdown, standing, numTeams } = useLoaderData<typeof loader>();
return (
<div className="container mx-auto py-8 px-4">
<TeamScoreBreakdown
leagueId={leagueId}
seasonId={seasonId}
breakdown={breakdown}
standing={standing}
numTeams={numTeams}
/>
</div>
);
}