From fb392c4fddad82cc1fc5eb433981f9df2712eeb4 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 15 May 2026 23:45:34 +0000 Subject: [PATCH] Improve pre-draft rankings UX: mobile tabs, add/remove toggle, rename On mobile, show tabs ("All Players" / "My Rankings") instead of a stacked layout that buries the queue below a long participant list. On the All Players list, the button now toggles between Add and Remove so users never need to switch tabs just to drop someone. Desktop keeps the side-by-side panel layout. Also renames the feature throughout from "queue builder" to "pre-draft rankings" and the DraftInfoCard button to "Set Pre-Draft Rankings". https://claude.ai/code/session_01Gu2DkTWL3nv74EMuGPpxhG --- app/components/league/DraftInfoCard.tsx | 2 +- .../$leagueId.draft-queue.$seasonId.tsx | 186 ++++++++++++------ 2 files changed, 123 insertions(+), 65 deletions(-) diff --git a/app/components/league/DraftInfoCard.tsx b/app/components/league/DraftInfoCard.tsx index f22f1c9..e2b7d5b 100644 --- a/app/components/league/DraftInfoCard.tsx +++ b/app/components/league/DraftInfoCard.tsx @@ -121,7 +121,7 @@ function DraftInfoCardVariant({ ) : queueBuilderHref ? ( ) : null} diff --git a/app/routes/leagues/$leagueId.draft-queue.$seasonId.tsx b/app/routes/leagues/$leagueId.draft-queue.$seasonId.tsx index 38a97e6..7679e60 100644 --- a/app/routes/leagues/$leagueId.draft-queue.$seasonId.tsx +++ b/app/routes/leagues/$leagueId.draft-queue.$seasonId.tsx @@ -1,6 +1,6 @@ import { redirect, useFetcher, useLoaderData, useRevalidator, Link } from "react-router"; import { useCallback, useEffect, useMemo, useState } from "react"; -import { ArrowLeft, ListOrdered } from "lucide-react"; +import { ArrowLeft, ListOrdered, Users } from "lucide-react"; import { auth } from "~/lib/auth.server"; import { findSeasonWithTeamsAndLeague } from "~/models/season"; import { getDraftParticipants } from "~/models/season-participant"; @@ -8,10 +8,11 @@ import { getTeamQueue } from "~/models/draft-queue"; import { QueueSection } from "~/components/draft/QueueSection"; import { Button } from "~/components/ui/button"; import { Badge } from "~/components/ui/badge"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/components/ui/tabs"; import type { Route } from "./+types/$leagueId.draft-queue.$seasonId"; export function meta({ data }: Route.MetaArgs): Route.MetaDescriptors { - return [{ title: `Pre-Draft Queue — ${data?.season?.league?.name ?? "League"} - Brackt` }]; + return [{ title: `Pre-Draft Rankings — ${data?.season?.league?.name ?? "League"} - Brackt` }]; } export async function loader(args: Route.LoaderArgs) { @@ -39,7 +40,7 @@ export async function loader(args: Route.LoaderArgs) { } if (!userId) { - throw new Response("You must be logged in to build a queue", { status: 401 }); + throw new Response("You must be logged in to set pre-draft rankings", { status: 401 }); } const userTeam = season.teams.find((t) => t.ownerId === userId); @@ -63,7 +64,7 @@ export async function loader(args: Route.LoaderArgs) { type QueueItem = { id: string; participantId: string }; -export default function PreDraftQueue() { +export default function PreDraftRankings() { const { season, leagueId, userTeam, availableParticipants, userQueue } = useLoaderData(); const { revalidate } = useRevalidator(); @@ -94,7 +95,6 @@ export default function PreDraftQueue() { const handleAdd = useCallback( (participantId: string) => { if (queuedParticipantIds.has(participantId)) return; - // Optimistic update with a temporary ID const tempId = `temp-${Date.now()}-${participantId}`; setLocalQueue((prev) => [...prev, { id: tempId, participantId }]); const formData = new FormData(); @@ -117,6 +117,15 @@ export default function PreDraftQueue() { [userTeam.id, removeFetcher] ); + // Remove by participantId — used from the All Players list + const handleRemoveByParticipantId = useCallback( + (participantId: string) => { + const item = localQueue.find((q) => q.participantId === participantId); + if (item) handleRemove(item.id); + }, + [localQueue, handleRemove] + ); + const handleReorder = useCallback( (participantIds: string[]) => { setLocalQueue((prev) => { @@ -134,6 +143,77 @@ export default function PreDraftQueue() { [userTeam.id, season.id, reorderFetcher] ); + // ── Shared panel content ──────────────────────────────────────────────────── + + const allPlayersPanel = ( +
+ {availableParticipants.map((p) => { + const inQueue = queuedParticipantIds.has(p.id); + return ( +
+
+

{p.name}

+

{p.sport.name}

+
+ {p.vorpValue != null && ( + + {Number(p.vorpValue).toFixed(1)} + + )} + {inQueue ? ( + + ) : ( + + )} +
+ ); + })} + {availableParticipants.length === 0 && ( +

+ No participants have been added to this season yet. +

+ )} +
+ ); + + const rankingsPanel = ( + <> +
+ +
+ {localQueue.length > 0 && ( +

+ Drag to reorder · Autopick follows this order +

+ )} + + ); + + // ── Render ────────────────────────────────────────────────────────────────── + return (
{/* Header */} @@ -147,79 +227,57 @@ export default function PreDraftQueue() {
-

Pre-Draft Queue

+

Pre-Draft Rankings

- Rank the participants you want. Your queue will carry into the live draft and determine - your autopick order. + Rank the players you want. Your rankings carry into the live draft as your autopick + priority order.

- {/* Two-panel layout */} -
- {/* Participant list */} + {/* Mobile: tabs */} +
+ + + + + All Players + + + + My Rankings + {localQueue.length > 0 && ( + + {localQueue.length} + + )} + + + +
+ Sorted by VORP +
+ {allPlayersPanel} +
+ {rankingsPanel} +
+
+ + {/* Desktop: side-by-side */} +
-

All Participants

+

All Players

Sorted by VORP
-
- {availableParticipants.map((p) => { - const inQueue = queuedParticipantIds.has(p.id); - return ( -
-
-

{p.name}

-

{p.sport.name}

-
- {p.vorpValue != null && ( - - {Number(p.vorpValue).toFixed(1)} - - )} - -
- ); - })} - {availableParticipants.length === 0 && ( -

- No participants have been added to this season yet. -

- )} -
+ {allPlayersPanel}
- - {/* Queue panel */}
-

Your Queue

+

My Rankings

{localQueue.length}
-
- -
- {localQueue.length > 0 && ( -

- Drag to reorder · Autopick follows this order -

- )} + {rankingsPanel}