brackt/app/components/draft/TeamsDraftedGrid.tsx
Chris Parsons ab3437cd73
Display team owner names instead of team names in draft UI (#24)
* Show manager username instead of team name on draft board

Both the read-only draft board and the active draft room now display
the manager's username (falling back to displayName if no username is
set, and to team name if the team is unassigned) in the column headers
of the draft grid.

https://claude.ai/code/session_01C97GauJaAB83NVWxdpNVh1

* 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

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-02-22 16:56:07 -08:00

168 lines
5.4 KiB
TypeScript

import { Badge } from "~/components/ui/badge";
import { useMemo } from "react";
interface TeamsDraftedGridProps {
draftSlots: Array<{
id: string;
team: {
id: string;
name: string;
};
}>;
ownerMap?: Record<string, string>;
picks: Array<{
id: string;
team: {
id: string;
name: string;
};
participant: {
id: string;
name: string;
};
sport: {
id: string;
name: string;
};
}>;
sports: Array<{
id: string;
name: string;
}>;
season: {
numFlexPicks: number;
};
}
export function TeamsDraftedGrid({
draftSlots,
picks,
sports,
season,
ownerMap = {},
}: TeamsDraftedGridProps) {
// Calculate picks by team and sport
const picksByTeamAndSport = useMemo(() => {
const map = new Map<string, Map<string, typeof picks>>();
picks.forEach((pick) => {
if (!map.has(pick.team.id)) {
map.set(pick.team.id, new Map());
}
const teamMap = map.get(pick.team.id)!;
if (!teamMap.has(pick.sport.id)) {
teamMap.set(pick.sport.id, []);
}
teamMap.get(pick.sport.id)!.push(pick);
});
return map;
}, [picks]);
// Calculate flex picks used by each team
const flexPicksByTeam = useMemo(() => {
const map = new Map<string, number>();
draftSlots.forEach((slot) => {
const teamPicks = picks.filter((p) => p.team.id === slot.team.id);
const uniqueSportsPicked = new Set(teamPicks.map((p) => p.sport.id)).size;
const flexUsed = Math.max(0, teamPicks.length - uniqueSportsPicked);
map.set(slot.team.id, flexUsed);
});
return map;
}, [picks, draftSlots]);
if (sports.length === 0) {
return (
<div className="flex items-center justify-center h-full p-8 text-muted-foreground">
<p>No sports have been configured for this season.</p>
</div>
);
}
return (
<div className="w-full h-full overflow-auto">
<table className="w-full border-collapse">
<thead className="sticky top-0 bg-background z-10">
<tr>
<th className="border-r border-b border-border p-2 text-left font-semibold min-w-[150px] bg-muted/50">
Sport
</th>
{draftSlots.map((slot, index) => {
const flexUsed = flexPicksByTeam.get(slot.team.id) || 0;
const isLast = index === draftSlots.length - 1;
return (
<th
key={slot.id}
className={`border-b border-border p-2 text-left font-semibold min-w-[180px] bg-muted/50 ${!isLast ? 'border-r' : ''}`}
>
<div className="flex flex-col gap-1">
<div className="font-semibold text-sm">
{ownerMap[slot.team.id] || slot.team.name}
</div>
<div className="text-xs text-muted-foreground font-normal">
{flexUsed} of {season.numFlexPicks} flex
</div>
</div>
</th>
);
})}
</tr>
</thead>
<tbody className="border-b border-border">
{sports.map((sport, sportIndex) => {
const isLastRow = sportIndex === sports.length - 1;
return (
<tr key={sport.id}>
<td className={`border-r border-border p-2 font-medium bg-muted/30 ${!isLastRow ? 'border-b' : ''}`}>
{sport.name}
</td>
{draftSlots.map((slot, slotIndex) => {
const teamSportPicks =
picksByTeamAndSport
.get(slot.team.id)
?.get(sport.id) || [];
const hasMultiplePicks = teamSportPicks.length > 1;
const isLastCol = slotIndex === draftSlots.length - 1;
return (
<td
key={`${slot.team.id}-${sport.id}`}
className={`border-border p-2 ${
hasMultiplePicks
? "bg-accent/30 font-semibold"
: "bg-background"
} ${!isLastRow ? 'border-b' : ''} ${!isLastCol ? 'border-r' : ''}`}
>
{teamSportPicks.length > 0 ? (
<div className="flex flex-col gap-1">
{teamSportPicks.map((pick, i) => (
<div
key={pick.id}
className="text-sm flex items-center gap-1"
>
<span>{pick.participant.name}</span>
{hasMultiplePicks && (
<Badge
variant="secondary"
className="text-xs px-1 py-0"
>
{i + 1}
</Badge>
)}
</div>
))}
</div>
) : null}
</td>
);
})}
</tr>
);
})}
</tbody>
</table>
</div>
);
}