brackt/app/components/draft/PlayerList.tsx

196 lines
6.2 KiB
TypeScript
Raw Normal View History

import { useState, useMemo } from "react";
import { Input } from "~/components/ui/input";
import { Button } from "~/components/ui/button";
import { Badge } from "~/components/ui/badge";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "~/components/ui/select";
import { Search, ArrowUpDown } from "lucide-react";
interface Participant {
id: string;
name: string;
sport: string;
expectedValue: number;
}
interface DraftPick {
participantId: string;
}
interface PlayerListProps {
participants: Participant[];
draftPicks: DraftPick[];
onAddToQueue?: (participantId: string) => void;
onDraftPlayer?: (participantId: string) => void;
canDraft: boolean;
}
export function PlayerList({
participants,
draftPicks,
onAddToQueue,
onDraftPlayer,
canDraft,
}: PlayerListProps) {
const [searchQuery, setSearchQuery] = useState("");
const [sportFilter, setSportFilter] = useState<string>("all");
const [sortBy, setSortBy] = useState<"ev" | "name">("ev");
// Get unique sports
const sports = useMemo(() => {
const uniqueSports = new Set(participants.map((p) => p.sport));
return Array.from(uniqueSports).sort();
}, [participants]);
// Get drafted participant IDs
const draftedIds = useMemo(() => {
return new Set(draftPicks.map((p) => p.participantId));
}, [draftPicks]);
// Filter and sort participants
const filteredParticipants = useMemo(() => {
let filtered = participants.filter((p) => {
// Filter out drafted players
if (draftedIds.has(p.id)) return false;
// Filter by search query
if (searchQuery && !p.name.toLowerCase().includes(searchQuery.toLowerCase())) {
return false;
}
// Filter by sport
if (sportFilter !== "all" && p.sport !== sportFilter) {
return false;
}
return true;
});
// Sort
if (sortBy === "ev") {
filtered.sort((a, b) => b.expectedValue - a.expectedValue);
} else {
filtered.sort((a, b) => a.name.localeCompare(b.name));
}
return filtered;
}, [participants, draftedIds, searchQuery, sportFilter, sortBy]);
return (
<div className="flex flex-col h-full overflow-hidden">
{/* Filters */}
<div className="space-y-3 mb-4">
{/* Search */}
<div className="relative">
<Search className="absolute left-3 top-1/2 -translate-y-1/2 h-4 w-4 text-muted-foreground" />
<Input
placeholder="Search players..."
value={searchQuery}
onChange={(e) => setSearchQuery(e.target.value)}
className="pl-9"
/>
</div>
{/* Sport Filter and Sort */}
<div className="flex gap-2">
<Select value={sportFilter} onValueChange={setSportFilter}>
<SelectTrigger className="flex-1">
<SelectValue placeholder="All Sports" />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">All Sports</SelectItem>
{sports.map((sport) => (
<SelectItem key={sport} value={sport}>
{sport}
</SelectItem>
))}
</SelectContent>
</Select>
<Button
variant="outline"
size="sm"
onClick={() => setSortBy(sortBy === "ev" ? "name" : "ev")}
className="flex items-center gap-2"
>
<ArrowUpDown className="h-4 w-4" />
{sortBy === "ev" ? "By EV" : "By Name"}
</Button>
</div>
</div>
{/* Player Count */}
<div className="text-sm text-muted-foreground mb-3">
{filteredParticipants.length} player{filteredParticipants.length !== 1 ? "s" : ""} available
</div>
{/* Player Table */}
<div className="flex-1 overflow-y-auto border rounded-md">
{filteredParticipants.length === 0 ? (
<div className="text-center py-8 text-muted-foreground">
No players found
</div>
) : (
<table className="w-full text-sm border-separate border-spacing-0">
<thead className="sticky top-0 bg-muted border-b">
<tr>
<th className="text-left p-2 font-semibold">Player</th>
<th className="text-left p-2 font-semibold">Sport</th>
<th className="text-right p-2 font-semibold">EV</th>
<th className="text-right p-2 font-semibold">Actions</th>
</tr>
</thead>
<tbody>
{filteredParticipants.map((participant) => (
<tr
key={participant.id}
className="border-b last:border-b-0 hover:bg-accent/50 transition-colors"
>
<td className="p-2 font-medium">{participant.name}</td>
<td className="p-2">
<Badge variant="secondary" className="text-xs">
{participant.sport}
</Badge>
</td>
<td className="p-2 text-right text-muted-foreground">
{participant.expectedValue.toFixed(1)}
</td>
<td className="p-2 text-right">
<div className="flex items-center justify-end gap-1">
{onAddToQueue && (
<Button
variant="outline"
size="sm"
onClick={() => onAddToQueue(participant.id)}
className="h-7 px-2 text-xs"
>
Queue
</Button>
)}
{canDraft && onDraftPlayer && (
<Button
variant="default"
size="sm"
onClick={() => onDraftPlayer(participant.id)}
className="h-7 px-2 text-xs"
>
Draft
</Button>
)}
</div>
</td>
</tr>
))}
</tbody>
</table>
)}
</div>
</div>
);
}