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>
This commit is contained in:
parent
b35791e76a
commit
ab3437cd73
6 changed files with 61 additions and 4 deletions
|
|
@ -25,6 +25,7 @@ interface DraftGridProps {
|
|||
onForceManualPick?: (pickNumber: number, teamId: string) => void;
|
||||
autodraftStatus?: Record<string, boolean>;
|
||||
connectedTeams?: Set<string>;
|
||||
ownerMap?: Record<string, string>;
|
||||
}
|
||||
|
||||
export function DraftGrid({
|
||||
|
|
@ -39,6 +40,7 @@ export function DraftGrid({
|
|||
onForceManualPick,
|
||||
autodraftStatus = {},
|
||||
connectedTeams = new Set(),
|
||||
ownerMap = {},
|
||||
}: DraftGridProps) {
|
||||
const totalTeams = draftSlots.length;
|
||||
|
||||
|
|
@ -58,7 +60,7 @@ export function DraftGrid({
|
|||
<div
|
||||
className={`font-semibold text-sm truncate px-2 ${!isConnected ? "italic text-muted-foreground" : ""}`}
|
||||
>
|
||||
{slot.team.name}
|
||||
{ownerMap[slot.team.id] || slot.team.name}
|
||||
</div>
|
||||
{teamTimers && formatTime && (
|
||||
<div
|
||||
|
|
|
|||
|
|
@ -42,6 +42,7 @@ interface DraftGridSectionProps {
|
|||
onForceManualPickOpen: (pickNumber: number, teamId: string) => void;
|
||||
onReplacePick?: (pickNumber: number, teamId: string) => void;
|
||||
onRollbackToPick?: (pickNumber: number) => void;
|
||||
ownerMap?: Record<string, string>;
|
||||
}
|
||||
|
||||
export function DraftGridSection({
|
||||
|
|
@ -57,6 +58,7 @@ export function DraftGridSection({
|
|||
onForceManualPickOpen,
|
||||
onReplacePick,
|
||||
onRollbackToPick,
|
||||
ownerMap = {},
|
||||
}: DraftGridSectionProps) {
|
||||
const currentTeamId = useMemo(
|
||||
() => 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
|
||||
className={`text-xs font-mono ${getTimerColorClass(teamTime)}`}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ interface TeamsDraftedGridProps {
|
|||
name: string;
|
||||
};
|
||||
}>;
|
||||
ownerMap?: Record<string, string>;
|
||||
picks: Array<{
|
||||
id: string;
|
||||
team: {
|
||||
|
|
@ -38,6 +39,7 @@ export function TeamsDraftedGrid({
|
|||
picks,
|
||||
sports,
|
||||
season,
|
||||
ownerMap = {},
|
||||
}: TeamsDraftedGridProps) {
|
||||
// Calculate picks by team and sport
|
||||
const picksByTeamAndSport = useMemo(() => {
|
||||
|
|
@ -97,7 +99,7 @@ export function TeamsDraftedGrid({
|
|||
>
|
||||
<div className="flex flex-col gap-1">
|
||||
<div className="font-semibold text-sm">
|
||||
{slot.team.name}
|
||||
{ownerMap[slot.team.id] || slot.team.name}
|
||||
</div>
|
||||
<div className="text-xs text-muted-foreground font-normal">
|
||||
{flexUsed} of {season.numFlexPicks} flex
|
||||
|
|
|
|||
39
app/lib/owner-map.ts
Normal file
39
app/lib/owner-map.ts
Normal 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;
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@ import * as schema from "~/database/schema";
|
|||
import { DraftGrid } from "~/components/DraftGrid";
|
||||
import { useDraftSocket } from "~/hooks/useDraftSocket";
|
||||
import { useState, useEffect } from "react";
|
||||
import { buildOwnerMap } from "~/lib/owner-map";
|
||||
|
||||
export async function loader(args: any) {
|
||||
const { params } = args;
|
||||
|
|
@ -102,15 +103,18 @@ export async function loader(args: any) {
|
|||
.where(eq(schema.draftPicks.seasonId, seasonId))
|
||||
.orderBy(asc(schema.draftPicks.pickNumber));
|
||||
|
||||
const ownerMap = await buildOwnerMap(draftSlots);
|
||||
|
||||
return {
|
||||
season,
|
||||
draftSlots,
|
||||
draftPicks,
|
||||
ownerMap,
|
||||
};
|
||||
}
|
||||
|
||||
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 [picks, setPicks] = useState(initialPicks);
|
||||
const [currentPick, setCurrentPick] = useState(season.currentPickNumber || 1);
|
||||
|
|
@ -186,6 +190,7 @@ export default function DraftBoard() {
|
|||
draftSlots={draftSlots}
|
||||
draftGrid={draftGrid}
|
||||
currentPick={currentPick}
|
||||
ownerMap={ownerMap}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -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 { getAuth } from "@clerk/react-router/server";
|
||||
import { getTeamQueue } from "~/models/draft-queue";
|
||||
import { buildOwnerMap } from "~/lib/owner-map";
|
||||
import { DraftSidebar } from "~/components/DraftSidebar";
|
||||
import { TeamsDraftedGrid } from "~/components/draft/TeamsDraftedGrid";
|
||||
import { QueueSection } from "~/components/draft/QueueSection";
|
||||
|
|
@ -166,6 +167,8 @@ export async function loader(args: any) {
|
|||
? (autodraftSettings.find((s) => s.teamId === userTeam.id) ?? null)
|
||||
: null;
|
||||
|
||||
const ownerMap = await buildOwnerMap(draftSlots);
|
||||
|
||||
return {
|
||||
season,
|
||||
draftSlots,
|
||||
|
|
@ -179,6 +182,7 @@ export async function loader(args: any) {
|
|||
isCommissioner: !!isCommissioner,
|
||||
currentUserId: userId,
|
||||
numFlexPicks: Math.max(0, season.draftRounds - seasonSports.length),
|
||||
ownerMap,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -196,6 +200,7 @@ export default function DraftRoom() {
|
|||
isCommissioner,
|
||||
currentUserId,
|
||||
numFlexPicks,
|
||||
ownerMap,
|
||||
} = useLoaderData<typeof loader>();
|
||||
const { revalidate } = useRevalidator();
|
||||
const { isConnected, connectionError, isReconnecting, on, off } = useDraftSocket(season.id, userTeam?.id);
|
||||
|
|
@ -1224,6 +1229,7 @@ export default function DraftRoom() {
|
|||
autodraftStatus={autodraftStatus}
|
||||
connectedTeams={connectedTeams}
|
||||
isCommissioner={isCommissioner}
|
||||
ownerMap={ownerMap}
|
||||
onAdjustTimeBankOpen={isCommissioner ? handleAdjustTimeBankOpen : undefined}
|
||||
onForceAutopick={handleForceAutopick}
|
||||
onForceManualPickOpen={(pickNumber, teamId) => {
|
||||
|
|
@ -1244,6 +1250,7 @@ export default function DraftRoom() {
|
|||
picks={picks}
|
||||
sports={seasonSportsData}
|
||||
season={{ numFlexPicks }}
|
||||
ownerMap={ownerMap}
|
||||
/>
|
||||
</div>
|
||||
</TabsContent>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue