diff --git a/app/routes/leagues/$leagueId.draft.$seasonId.tsx b/app/routes/leagues/$leagueId.draft.$seasonId.tsx index e4095e9..11bec40 100644 --- a/app/routes/leagues/$leagueId.draft.$seasonId.tsx +++ b/app/routes/leagues/$leagueId.draft.$seasonId.tsx @@ -1,5 +1,5 @@ import { useLoaderData, Link } from "react-router"; -import { eq, and, asc, notInArray } from "drizzle-orm"; +import { eq, and, asc, desc, notInArray, inArray } from "drizzle-orm"; import { database } from "~/database/context"; import * as schema from "~/database/schema"; import { useDraftSocket } from "~/hooks/useDraftSocket"; @@ -84,26 +84,41 @@ export async function loader(args: any) { .where(eq(schema.draftPicks.seasonId, seasonId)) .orderBy(asc(schema.draftPicks.pickNumber)); - // Get available participants (not yet picked) + // Get sports seasons for this season + const seasonSports = await db.query.seasonSports.findMany({ + where: eq(schema.seasonSports.seasonId, seasonId), + }); + + const sportsSeasonIds = seasonSports.map((ss) => ss.sportsSeasonId); + + // Get available participants (not yet picked) - sorted by EV desc, then name const pickedParticipantIds = draftPicks.map((p) => p.participant.id); - const availableParticipants = await db - .select({ - id: schema.participants.id, - name: schema.participants.name, - sport: schema.sports, - }) - .from(schema.participants) - .innerJoin(schema.sportsSeasons, eq(schema.participants.sportsSeasonId, schema.sportsSeasons.id)) - .innerJoin(schema.sports, eq(schema.sportsSeasons.sportId, schema.sports.id)) - .where( - and( - eq(schema.participants.sportsSeasonId, season.templateId!), - pickedParticipantIds.length > 0 - ? notInArray(schema.participants.id, pickedParticipantIds) - : undefined + + let availableParticipants: any[] = []; + + if (sportsSeasonIds.length > 0) { + availableParticipants = await db + .select({ + id: schema.participants.id, + name: schema.participants.name, + expectedValue: schema.participants.expectedValue, + sport: schema.sports, + }) + .from(schema.participants) + .innerJoin(schema.sportsSeasons, eq(schema.participants.sportsSeasonId, schema.sportsSeasons.id)) + .innerJoin(schema.sports, eq(schema.sportsSeasons.sportId, schema.sports.id)) + .where( + and( + sportsSeasonIds.length === 1 + ? eq(schema.participants.sportsSeasonId, sportsSeasonIds[0]) + : inArray(schema.participants.sportsSeasonId, sportsSeasonIds), + pickedParticipantIds.length > 0 + ? notInArray(schema.participants.id, pickedParticipantIds) + : undefined + ) ) - ) - .orderBy(asc(schema.participants.name)); + .orderBy(desc(schema.participants.expectedValue), asc(schema.participants.name)); + } // Load user's team queue if they have a team const userQueue = userTeam ? await getTeamQueue(userTeam.id) : []; @@ -126,6 +141,9 @@ export default function DraftRoom() { const { isConnected, on, off } = useDraftSocket(season.id); const [picks, setPicks] = useState(draftPicks); const [currentPick, setCurrentPick] = useState(season.currentPickNumber || 1); + const [searchQuery, setSearchQuery] = useState(""); + const [hideDrafted, setHideDrafted] = useState(true); + const [sportFilter, setSportFilter] = useState("all"); // Listen for new picks from other users useEffect(() => { @@ -180,6 +198,51 @@ export default function DraftRoom() { const draftGrid = generateDraftGrid(); + // Get drafted participant IDs for filtering + const draftedParticipantIds = new Set(picks.map((p: any) => p.participant.id)); + + // Get unique sports for filter dropdown + const uniqueSports = Array.from( + new Set(availableParticipants.map((p: any) => p.sport.name)) + ).sort(); + + // Get all participants (including drafted) for the show/hide drafted toggle + const allParticipants = [...availableParticipants]; + + // Add drafted participants if we're showing them + if (!hideDrafted) { + picks.forEach((pick: any) => { + if (!allParticipants.find((p: any) => p.id === pick.participant.id)) { + allParticipants.push({ + id: pick.participant.id, + name: pick.participant.name, + expectedValue: pick.participant.expectedValue || 0, + sport: pick.sport, + }); + } + }); + // Re-sort after adding drafted players + allParticipants.sort((a, b) => { + if (b.expectedValue !== a.expectedValue) { + return b.expectedValue - a.expectedValue; + } + return a.name.localeCompare(b.name); + }); + } + + // Filter participants based on search, sport, and drafted status + const filteredParticipants = allParticipants.filter((participant: any) => { + // Search filter + if (searchQuery && !participant.name.toLowerCase().includes(searchQuery.toLowerCase())) { + return false; + } + // Sport filter + if (sportFilter !== "all" && participant.sport.name !== sportFilter) { + return false; + } + return true; + }); + return (
{/* Standalone Header - No Main Nav */} @@ -341,21 +404,110 @@ export default function DraftRoom() { {/* Right Column: Available Participants */}
-

- Available Participants ({availableParticipants.length}) -

-
- {availableParticipants.map((participant: any) => ( -
-

{participant.name}

-

- {participant.sport.name} -

+
+

+ Available Participants ({filteredParticipants.length}) +

+ +
+ {/* Search Input */} + setSearchQuery(e.target.value)} + className="w-full px-3 py-2 border rounded-md text-sm" + /> + +
+ {/* Sport Filter Dropdown */} + + + {/* Show/Hide Drafted Toggle */} +
- ))} +
+
+ + {/* Table */} +
+
+ + + + + + + + + + {filteredParticipants.length === 0 ? ( + + + + ) : ( + filteredParticipants.map((participant: any) => { + const isDrafted = draftedParticipantIds.has(participant.id); + + return ( + { + if (!isDrafted) { + // TODO: Add to queue functionality (Phase 7) + console.log("Add to queue:", participant.name); + } + }} + > + + + + + ); + }) + )} + +
ParticipantSportEV
+ No participants found +
+
+ {participant.name} + {isDrafted && ( + + Drafted + + )} +
+
+ {participant.sport.name} + + {participant.expectedValue} +
+