brackt/app/components/DraftGrid.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

181 lines
6.2 KiB
TypeScript

import { Card } from "~/components/ui/card";
import {
ContextMenu,
ContextMenuContent,
ContextMenuItem,
ContextMenuTrigger,
} from "~/components/ui/context-menu";
interface DraftGridProps {
draftSlots: Array<{
id: string;
draftOrder: number;
team: {
id: string;
name: string;
};
}>;
draftGrid: any[][];
currentPick: number;
teamTimers?: Record<string, number>;
formatTime?: (seconds: number | undefined) => string;
title?: string;
isCommissioner?: boolean;
onForceAutopick?: (pickNumber: number, teamId: string) => void;
onForceManualPick?: (pickNumber: number, teamId: string) => void;
autodraftStatus?: Record<string, boolean>;
connectedTeams?: Set<string>;
ownerMap?: Record<string, string>;
}
export function DraftGrid({
draftSlots,
draftGrid,
currentPick,
teamTimers,
formatTime,
title,
isCommissioner = false,
onForceAutopick,
onForceManualPick,
autodraftStatus = {},
connectedTeams = new Set(),
ownerMap = {},
}: DraftGridProps) {
const totalTeams = draftSlots.length;
return (
<Card className="p-4">
{title && <h2 className="text-xl font-semibold mb-4">{title}</h2>}
<div className="w-full">
{/* Team Headers */}
<div className="flex gap-2 mb-2">
{draftSlots.map((slot) => {
const teamTime = teamTimers?.[slot.team.id];
const isAutodraft = autodraftStatus[slot.team.id] || false;
const isConnected = connectedTeams.has(slot.team.id);
return (
<div key={slot.id} className="flex-1 min-w-32 text-center">
<div
className={`font-semibold text-sm truncate px-2 ${!isConnected ? "italic text-muted-foreground" : ""}`}
>
{ownerMap[slot.team.id] || slot.team.name}
</div>
{teamTimers && formatTime && (
<div
className={`text-xs font-mono ${
teamTime === undefined
? "text-muted-foreground"
: teamTime > 60
? "text-emerald-400"
: teamTime > 30
? "text-amber-accent"
: "text-coral-accent"
}`}
>
{formatTime(teamTime)}
{isAutodraft && (
<span className="ml-1 text-muted-foreground">(auto)</span>
)}
</div>
)}
</div>
);
})}
</div>
{/* Draft Grid Rows */}
<div className="space-y-2">
{draftGrid.map((roundPicks, roundIndex) => {
const round = roundIndex + 1;
const isEvenRound = round % 2 === 0;
const displayPicks = isEvenRound
? [...roundPicks].reverse()
: roundPicks;
return (
<div key={roundIndex} className="flex gap-2">
{displayPicks.map((cell, index) => {
const actualIndex = isEvenRound
? roundPicks.length - 1 - index
: index;
const slot = draftSlots[actualIndex];
const pickNumber = roundIndex * totalTeams + actualIndex + 1;
const isCurrent = pickNumber === currentPick;
const isPicked = !!cell;
const cellContent = (
<div
className={`flex-1 min-w-0 h-20 border-2 rounded-lg p-2 transition-all ${
isPicked
? "bg-emerald-500/10 border-emerald-500/30"
: isCurrent
? "border-electric bg-electric/15 shadow-lg shadow-electric/10"
: "border-border bg-card"
}`}
title={`Overall Pick #${pickNumber}`}
>
<div className="text-xs font-mono text-muted-foreground mb-1">
{round}.{String(actualIndex + 1).padStart(2, "0")}
</div>
{isPicked ? (
<div className="text-xs">
<div className="font-semibold truncate">
{cell.participant.name}
</div>
<div className="text-muted-foreground truncate">
{cell.sport.name}
</div>
</div>
) : isCurrent ? (
<div className="text-xs font-semibold text-electric">
On Clock
</div>
) : null}
</div>
);
// Wrap with context menu if commissioner and current unpicked cell
if (
isCommissioner &&
!isPicked &&
isCurrent &&
onForceAutopick &&
onForceManualPick
) {
return (
<ContextMenu key={pickNumber}>
<ContextMenuTrigger asChild>
{cellContent}
</ContextMenuTrigger>
<ContextMenuContent>
<ContextMenuItem
onClick={() =>
onForceAutopick(pickNumber, slot.team.id)
}
>
Force Auto Pick
</ContextMenuItem>
<ContextMenuItem
onClick={() =>
onForceManualPick(pickNumber, slot.team.id)
}
>
Force Manual Pick
</ContextMenuItem>
</ContextMenuContent>
</ContextMenu>
);
}
return cellContent;
})}
</div>
);
})}
</div>
</div>
</Card>
);
}