Address code review: extract ownerMap helper, fix TeamsDraftedGrid

- Extract ownerMap building into app/lib/owner-map.ts to eliminate
  duplication across the two loaders
- Guard against null username AND displayName so the map only contains
  real string values
- Apply username display to TeamsDraftedGrid (the "Teams" tab in the
  active draft room), which was missed in the initial change

https://claude.ai/code/session_01C97GauJaAB83NVWxdpNVh1
This commit is contained in:
Claude 2026-02-23 00:53:58 +00:00
parent 4aa3309e61
commit 7d7b2598c5
No known key found for this signature in database
4 changed files with 47 additions and 47 deletions

View file

@ -9,6 +9,7 @@ interface TeamsDraftedGridProps {
name: string; name: string;
}; };
}>; }>;
ownerMap?: Record<string, string>;
picks: Array<{ picks: Array<{
id: string; id: string;
team: { team: {
@ -38,6 +39,7 @@ export function TeamsDraftedGrid({
picks, picks,
sports, sports,
season, season,
ownerMap = {},
}: TeamsDraftedGridProps) { }: TeamsDraftedGridProps) {
// Calculate picks by team and sport // Calculate picks by team and sport
const picksByTeamAndSport = useMemo(() => { const picksByTeamAndSport = useMemo(() => {
@ -97,7 +99,7 @@ export function TeamsDraftedGrid({
> >
<div className="flex flex-col gap-1"> <div className="flex flex-col gap-1">
<div className="font-semibold text-sm"> <div className="font-semibold text-sm">
{slot.team.name} {ownerMap[slot.team.id] || slot.team.name}
</div> </div>
<div className="text-xs text-muted-foreground font-normal"> <div className="text-xs text-muted-foreground font-normal">
{flexUsed} of {season.numFlexPicks} flex {flexUsed} of {season.numFlexPicks} flex

39
app/lib/owner-map.ts Normal file
View file

@ -0,0 +1,39 @@
import { findUserByClerkId } from "~/models/user";
/**
* Builds a map of teamId -> manager username (or displayName fallback).
* Accepts any array of objects that have a `team` with `id` and `ownerId`.
*/
export async function buildOwnerMap(
slots: Array<{ team: { id: string; ownerId: string | null } }>
): Promise<Record<string, string>> {
const ownerIds = slots
.map((s) => s.team.ownerId)
.filter((id): id is string => id !== null);
const uniqueOwnerIds = [...new Set(ownerIds)];
const ownerEntries = await Promise.all(
uniqueOwnerIds.map(async (ownerId) => {
const user = await findUserByClerkId(ownerId);
const name = user?.username || user?.displayName || null;
return name ? { ownerId, name } : null;
})
);
const ownerByClerkId = new Map(
ownerEntries
.filter((e): e is NonNullable<typeof e> => e !== null)
.map((e) => [e.ownerId, e.name])
);
const ownerMap: Record<string, string> = {};
for (const slot of slots) {
const name = slot.team.ownerId
? ownerByClerkId.get(slot.team.ownerId)
: undefined;
if (name) {
ownerMap[slot.team.id] = name;
}
}
return ownerMap;
}

View file

@ -6,7 +6,7 @@ import * as schema from "~/database/schema";
import { DraftGrid } from "~/components/DraftGrid"; import { DraftGrid } from "~/components/DraftGrid";
import { useDraftSocket } from "~/hooks/useDraftSocket"; import { useDraftSocket } from "~/hooks/useDraftSocket";
import { useState, useEffect } from "react"; import { useState, useEffect } from "react";
import { findUserByClerkId } from "~/models/user"; import { buildOwnerMap } from "~/lib/owner-map";
export async function loader(args: any) { export async function loader(args: any) {
const { params } = args; const { params } = args;
@ -103,28 +103,7 @@ export async function loader(args: any) {
.where(eq(schema.draftPicks.seasonId, seasonId)) .where(eq(schema.draftPicks.seasonId, seasonId))
.orderBy(asc(schema.draftPicks.pickNumber)); .orderBy(asc(schema.draftPicks.pickNumber));
// Build ownerMap: teamId -> username/displayName const ownerMap = await buildOwnerMap(draftSlots);
const ownerIds = draftSlots
.map((s) => s.team.ownerId)
.filter((id): id is string => id !== null);
const uniqueOwnerIds = [...new Set(ownerIds)];
const ownerEntries = await Promise.all(
uniqueOwnerIds.map(async (ownerId) => {
const user = await findUserByClerkId(ownerId);
return user ? { ownerId, name: user.username || user.displayName } : null;
})
);
const ownerByClerkId = new Map(
ownerEntries
.filter((e): e is NonNullable<typeof e> => e !== null)
.map((e) => [e.ownerId, e.name])
);
const ownerMap: Record<string, string> = {};
for (const slot of draftSlots) {
if (slot.team.ownerId && ownerByClerkId.has(slot.team.ownerId)) {
ownerMap[slot.team.id] = ownerByClerkId.get(slot.team.ownerId)!;
}
}
return { return {
season, season,

View file

@ -11,7 +11,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/components/ui/tabs";
import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "~/components/ui/dialog"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle } from "~/components/ui/dialog";
import { getAuth } from "@clerk/react-router/server"; import { getAuth } from "@clerk/react-router/server";
import { getTeamQueue } from "~/models/draft-queue"; import { getTeamQueue } from "~/models/draft-queue";
import { findUserByClerkId } from "~/models/user"; import { buildOwnerMap } from "~/lib/owner-map";
import { DraftSidebar } from "~/components/DraftSidebar"; import { DraftSidebar } from "~/components/DraftSidebar";
import { TeamsDraftedGrid } from "~/components/draft/TeamsDraftedGrid"; import { TeamsDraftedGrid } from "~/components/draft/TeamsDraftedGrid";
import { QueueSection } from "~/components/draft/QueueSection"; import { QueueSection } from "~/components/draft/QueueSection";
@ -167,28 +167,7 @@ export async function loader(args: any) {
? (autodraftSettings.find((s) => s.teamId === userTeam.id) ?? null) ? (autodraftSettings.find((s) => s.teamId === userTeam.id) ?? null)
: null; : null;
// Build ownerMap: teamId -> username/displayName const ownerMap = await buildOwnerMap(draftSlots);
const ownerIds = draftSlots
.map((s) => s.team.ownerId)
.filter((id): id is string => id !== null);
const uniqueOwnerIds = [...new Set(ownerIds)];
const ownerEntries = await Promise.all(
uniqueOwnerIds.map(async (ownerId) => {
const user = await findUserByClerkId(ownerId);
return user ? { ownerId, name: user.username || user.displayName } : null;
})
);
const ownerByClerkId = new Map(
ownerEntries
.filter((e): e is NonNullable<typeof e> => e !== null)
.map((e) => [e.ownerId, e.name])
);
const ownerMap: Record<string, string> = {};
for (const slot of draftSlots) {
if (slot.team.ownerId && ownerByClerkId.has(slot.team.ownerId)) {
ownerMap[slot.team.id] = ownerByClerkId.get(slot.team.ownerId)!;
}
}
return { return {
season, season,
@ -1271,6 +1250,7 @@ export default function DraftRoom() {
picks={picks} picks={picks}
sports={seasonSportsData} sports={seasonSportsData}
season={{ numFlexPicks }} season={{ numFlexPicks }}
ownerMap={ownerMap}
/> />
</div> </div>
</TabsContent> </TabsContent>