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
This commit is contained in:
Claude 2026-02-23 00:42:11 +00:00
parent b35791e76a
commit 4aa3309e61
No known key found for this signature in database
4 changed files with 60 additions and 3 deletions

View file

@ -25,6 +25,7 @@ interface DraftGridProps {
onForceManualPick?: (pickNumber: number, teamId: string) => void; onForceManualPick?: (pickNumber: number, teamId: string) => void;
autodraftStatus?: Record<string, boolean>; autodraftStatus?: Record<string, boolean>;
connectedTeams?: Set<string>; connectedTeams?: Set<string>;
ownerMap?: Record<string, string>;
} }
export function DraftGrid({ export function DraftGrid({
@ -39,6 +40,7 @@ export function DraftGrid({
onForceManualPick, onForceManualPick,
autodraftStatus = {}, autodraftStatus = {},
connectedTeams = new Set(), connectedTeams = new Set(),
ownerMap = {},
}: DraftGridProps) { }: DraftGridProps) {
const totalTeams = draftSlots.length; const totalTeams = draftSlots.length;
@ -58,7 +60,7 @@ export function DraftGrid({
<div <div
className={`font-semibold text-sm truncate px-2 ${!isConnected ? "italic text-muted-foreground" : ""}`} className={`font-semibold text-sm truncate px-2 ${!isConnected ? "italic text-muted-foreground" : ""}`}
> >
{slot.team.name} {ownerMap[slot.team.id] || slot.team.name}
</div> </div>
{teamTimers && formatTime && ( {teamTimers && formatTime && (
<div <div

View file

@ -42,6 +42,7 @@ interface DraftGridSectionProps {
onForceManualPickOpen: (pickNumber: number, teamId: string) => void; onForceManualPickOpen: (pickNumber: number, teamId: string) => void;
onReplacePick?: (pickNumber: number, teamId: string) => void; onReplacePick?: (pickNumber: number, teamId: string) => void;
onRollbackToPick?: (pickNumber: number) => void; onRollbackToPick?: (pickNumber: number) => void;
ownerMap?: Record<string, string>;
} }
export function DraftGridSection({ export function DraftGridSection({
@ -57,6 +58,7 @@ export function DraftGridSection({
onForceManualPickOpen, onForceManualPickOpen,
onReplacePick, onReplacePick,
onRollbackToPick, onRollbackToPick,
ownerMap = {},
}: DraftGridSectionProps) { }: DraftGridSectionProps) {
const currentTeamId = useMemo( const currentTeamId = useMemo(
() => draftGrid.flat().find((c) => c.pickNumber === currentPick)?.teamId ?? null, () => draftGrid.flat().find((c) => c.pickNumber === currentPick)?.teamId ?? null,
@ -86,7 +88,7 @@ export function DraftGridSection({
: "" : ""
}`} }`}
> >
{slot.team.name} {ownerMap[slot.team.id] || slot.team.name}
</div> </div>
<div <div
className={`text-xs font-mono ${getTimerColorClass(teamTime)}`} className={`text-xs font-mono ${getTimerColorClass(teamTime)}`}

View file

@ -6,6 +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";
export async function loader(args: any) { export async function loader(args: any) {
const { params } = args; const { params } = args;
@ -102,15 +103,39 @@ 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 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,
draftSlots, draftSlots,
draftPicks, draftPicks,
ownerMap,
}; };
} }
export default function DraftBoard() { export default function DraftBoard() {
const { season, draftSlots, draftPicks: initialPicks } = useLoaderData<typeof loader>(); const { season, draftSlots, draftPicks: initialPicks, ownerMap } = useLoaderData<typeof loader>();
const { isConnected, on, off } = useDraftSocket(season.id); const { isConnected, on, off } = useDraftSocket(season.id);
const [picks, setPicks] = useState(initialPicks); const [picks, setPicks] = useState(initialPicks);
const [currentPick, setCurrentPick] = useState(season.currentPickNumber || 1); const [currentPick, setCurrentPick] = useState(season.currentPickNumber || 1);
@ -186,6 +211,7 @@ export default function DraftBoard() {
draftSlots={draftSlots} draftSlots={draftSlots}
draftGrid={draftGrid} draftGrid={draftGrid}
currentPick={currentPick} currentPick={currentPick}
ownerMap={ownerMap}
/> />
</div> </div>
</div> </div>

View file

@ -11,6 +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 { 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";
@ -166,6 +167,29 @@ 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 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,
draftSlots, draftSlots,
@ -179,6 +203,7 @@ export async function loader(args: any) {
isCommissioner: !!isCommissioner, isCommissioner: !!isCommissioner,
currentUserId: userId, currentUserId: userId,
numFlexPicks: Math.max(0, season.draftRounds - seasonSports.length), numFlexPicks: Math.max(0, season.draftRounds - seasonSports.length),
ownerMap,
}; };
} }
@ -196,6 +221,7 @@ export default function DraftRoom() {
isCommissioner, isCommissioner,
currentUserId, currentUserId,
numFlexPicks, numFlexPicks,
ownerMap,
} = useLoaderData<typeof loader>(); } = useLoaderData<typeof loader>();
const { revalidate } = useRevalidator(); const { revalidate } = useRevalidator();
const { isConnected, connectionError, isReconnecting, on, off } = useDraftSocket(season.id, userTeam?.id); const { isConnected, connectionError, isReconnecting, on, off } = useDraftSocket(season.id, userTeam?.id);
@ -1224,6 +1250,7 @@ export default function DraftRoom() {
autodraftStatus={autodraftStatus} autodraftStatus={autodraftStatus}
connectedTeams={connectedTeams} connectedTeams={connectedTeams}
isCommissioner={isCommissioner} isCommissioner={isCommissioner}
ownerMap={ownerMap}
onAdjustTimeBankOpen={isCommissioner ? handleAdjustTimeBankOpen : undefined} onAdjustTimeBankOpen={isCommissioner ? handleAdjustTimeBankOpen : undefined}
onForceAutopick={handleForceAutopick} onForceAutopick={handleForceAutopick}
onForceManualPickOpen={(pickNumber, teamId) => { onForceManualPickOpen={(pickNumber, teamId) => {