Address code review: type hygiene, explicit types, parallel fetching, style fix

- Remove teamOwnerId from TeamStanding type (was an internal server concern, not display data)
- Remove teamOwnerId from getSeasonStandings return value
- Add TeamStandingWithChange interface extending TeamStanding with sevenDayRankChange/sevenDayOldRank fields
- Annotate getSevenDayStandingsChange with explicit Promise<TeamStandingWithChange[]> return type
- Re-export TeamStandingWithChange from models/standings.ts
- Standings loader: query teams table directly for ownerIds instead of relying on teamOwnerId in standings data
- Standings loader: parallelize all independent queries (standings, teams, progressionData, seasonComplete, completionPercentage) with Promise.all
- Restore font-medium on StandingsTable team TableCell

https://claude.ai/code/session_01EYgGnuTBaRVdBDapJRTxDZ
This commit is contained in:
Claude 2026-03-20 03:16:51 +00:00
parent 5fd9c6410b
commit fb8e672f77
No known key found for this signature in database
4 changed files with 36 additions and 31 deletions

View file

@ -54,7 +54,7 @@ export function StandingsTable({
)} )}
</div> </div>
</TableCell> </TableCell>
<TableCell> <TableCell className="font-medium">
<TeamNameDisplay <TeamNameDisplay
teamName={standing.teamName} teamName={standing.teamName}
ownerName={standing.ownerName} ownerName={standing.ownerName}

View file

@ -1,11 +1,11 @@
import { database } from "~/database/context"; import { database } from "~/database/context";
import * as schema from "~/database/schema"; import * as schema from "~/database/schema";
import { eq, and, desc } from "drizzle-orm"; import { eq, and, desc } from "drizzle-orm";
import type { TeamStanding, TeamStandingSnapshot } from "~/types/standings"; import type { TeamStanding, TeamStandingSnapshot, TeamStandingWithChange } from "~/types/standings";
import { calculateBracketPoints, calculateFantasyPoints } from "~/models/scoring-rules"; import { calculateBracketPoints, calculateFantasyPoints } from "~/models/scoring-rules";
// Re-export types from shared types file // Re-export types from shared types file
export type { TeamStanding, TeamStandingSnapshot } from "~/types/standings"; export type { TeamStanding, TeamStandingSnapshot, TeamStandingWithChange } from "~/types/standings";
/** /**
* Get current standings for a season * Get current standings for a season
@ -29,7 +29,6 @@ export async function getSeasonStandings(
return sorted.map((standing) => ({ return sorted.map((standing) => ({
teamId: standing.teamId, teamId: standing.teamId,
teamName: standing.team.name, teamName: standing.team.name,
teamOwnerId: standing.team.ownerId,
totalPoints: parseFloat(standing.totalPoints), totalPoints: parseFloat(standing.totalPoints),
currentRank: standing.currentRank, currentRank: standing.currentRank,
previousRank: standing.previousRank, previousRank: standing.previousRank,
@ -292,7 +291,7 @@ export async function getTeamStandingsHistory(
export async function getSevenDayStandingsChange( export async function getSevenDayStandingsChange(
seasonId: string, seasonId: string,
providedDb?: ReturnType<typeof database> providedDb?: ReturnType<typeof database>
) { ): Promise<TeamStandingWithChange[]> {
const db = providedDb || database(); const db = providedDb || database();
const sevenDaysAgo = new Date(); const sevenDaysAgo = new Date();

View file

@ -71,36 +71,38 @@ export async function loader(args: Route.LoaderArgs) {
}); });
} }
// Fetch standings with 7-day comparison // Fetch standings, teams (for owner lookup), and independent data in parallel
const standingsWithComparison = await getSevenDayStandingsChange(seasonId, db); const [standingsWithComparison, teams, progressionData, seasonComplete, completionPercentage] =
await Promise.all([
getSevenDayStandingsChange(seasonId, db),
db.query.teams.findMany({
where: eq(schema.teams.seasonId, seasonId),
columns: { id: true, ownerId: true },
}),
getSeasonPointProgression(seasonId, db),
isSeasonComplete(seasonId, db),
getSeasonCompletionPercentage(seasonId, db),
]);
// Fetch owner display names for all teams in standings // Build teamId -> ownerName map
const ownerIds = [ const ownerIds = [...new Set(teams.map((t) => t.ownerId).filter((id): id is string => id != null))];
...new Set(
standingsWithComparison
.map((s) => s.teamOwnerId)
.filter((id): id is string => id != null)
),
];
const userRows = await findUsersByClerkIds(ownerIds); const userRows = await findUsersByClerkIds(ownerIds);
const userByClerkId = new Map(userRows.map((u) => [u.clerkId, u])); const userByClerkId = new Map(userRows.map((u) => [u.clerkId, u]));
const ownerNameByTeamId = new Map(
teams
.filter((t): t is typeof t & { ownerId: string } => t.ownerId != null)
.map((t) => {
const user = userByClerkId.get(t.ownerId);
return [t.id, user ? getUserDisplayName(user) : null] as const;
})
);
// Map to format expected by component (use sevenDayRankChange instead of previousRank) // Map to format expected by component (use sevenDayRankChange instead of previousRank)
const formattedStandings = standingsWithComparison.map((standing) => { const formattedStandings = standingsWithComparison.map((standing) => ({
const user = standing.teamOwnerId ? userByClerkId.get(standing.teamOwnerId) : undefined; ...standing,
return { rankChange: standing.sevenDayRankChange,
...standing, ownerName: ownerNameByTeamId.get(standing.teamId) ?? null,
rankChange: standing.sevenDayRankChange, }));
ownerName: user ? getUserDisplayName(user) : null,
};
});
// Fetch historical progression data
const progressionData = await getSeasonPointProgression(seasonId, db);
// Check season completion status
const seasonComplete = await isSeasonComplete(seasonId, db);
const completionPercentage = await getSeasonCompletionPercentage(seasonId, db);
return { return {
league, league,

View file

@ -5,7 +5,6 @@
export interface TeamStanding { export interface TeamStanding {
teamId: string; teamId: string;
teamName: string; teamName: string;
teamOwnerId?: string | null;
ownerName?: string | null; ownerName?: string | null;
totalPoints: number; totalPoints: number;
currentRank: number; currentRank: number;
@ -34,3 +33,8 @@ export interface TeamStandingSnapshot {
rank: number; rank: number;
totalPoints: number; totalPoints: number;
} }
export interface TeamStandingWithChange extends TeamStanding {
sevenDayRankChange: number;
sevenDayOldRank: number | null;
}