brackt/app/components/draft/AvailableParticipantsSection.tsx

276 lines
11 KiB
TypeScript
Raw Normal View History

import { Button } from "~/components/ui/button";
import { Badge } from "~/components/ui/badge";
interface AvailableParticipantsSectionProps {
participants: Array<{
id: string;
name: string;
expectedValue: number;
sport: {
id: string;
name: string;
};
}>;
searchQuery: string;
sportFilter: string;
hideDrafted: boolean;
uniqueSports: string[];
draftedParticipantIds: Set<string>;
queue: Array<{ id: string; participantId: string }>;
eligibility: {
eligibleSportIds: Set<string>;
ineligibleReasons: Record<string, string>;
} | null;
canPick: boolean;
hasTeam: boolean;
onSearchChange: (query: string) => void;
onSportFilterChange: (sport: string) => void;
onHideDraftedChange: (hide: boolean) => void;
onMakePick: (participantId: string) => void;
onAddToQueue: (participantId: string) => void;
onRemoveFromQueue: (queueId: string) => void;
}
export function AvailableParticipantsSection({
participants,
searchQuery,
sportFilter,
hideDrafted,
uniqueSports,
draftedParticipantIds,
queue,
eligibility,
canPick,
hasTeam,
onSearchChange,
onSportFilterChange,
onHideDraftedChange,
onMakePick,
onAddToQueue,
onRemoveFromQueue,
}: AvailableParticipantsSectionProps) {
return (
<div className="flex flex-col h-full">
<div className="px-4 pt-4 pb-2 flex-shrink-0">
<div className="space-y-2">
{/* Search Input */}
<input
type="text"
placeholder="Search participants..."
value={searchQuery}
onChange={(e) => onSearchChange(e.target.value)}
className="w-full px-3 py-2 border rounded-md text-sm"
/>
<div className="flex gap-3">
{/* Sport Filter Dropdown */}
<select
value={sportFilter}
onChange={(e) => onSportFilterChange(e.target.value)}
className="flex-1 px-3 py-2 border rounded-md text-sm"
>
<option value="all">All Sports</option>
{uniqueSports.map((sport) => (
<option key={sport} value={sport}>
{sport}
</option>
))}
</select>
{/* Show/Hide Drafted Toggle */}
<label className="flex items-center gap-2 text-sm cursor-pointer whitespace-nowrap px-3 py-2 border rounded-md">
<input
type="checkbox"
checked={!hideDrafted}
onChange={(e) => onHideDraftedChange(!e.target.checked)}
className="rounded"
/>
<span>Show Drafted</span>
</label>
</div>
</div>
</div>
{/* Table - This will scroll independently */}
<div className="flex-1 overflow-hidden">
<div className="h-full overflow-y-auto">
<table className="w-full text-sm">
<thead className="bg-muted sticky top-0">
<tr>
<th className="text-left p-3 font-semibold">Participant</th>
<th className="text-left p-3 font-semibold">Sport</th>
<th className="text-right p-3 font-semibold">EV</th>
{hasTeam && (
<th className="text-right p-3 font-semibold">Queue</th>
)}
</tr>
</thead>
<tbody>
{participants.length === 0 ? (
<tr>
<td
colSpan={hasTeam ? 4 : 3}
className="text-center py-8 text-muted-foreground"
>
No participants found
</td>
</tr>
) : (
participants.map((participant) => {
const isDrafted = draftedParticipantIds.has(participant.id);
const isInQueue = queue.some(
(item) => item.participantId === participant.id
);
// Check eligibility
const isEligible = eligibility
? eligibility.eligibleSportIds.has(participant.sport.id)
: true;
const ineligibleReason =
eligibility?.ineligibleReasons[participant.sport.id];
return (
<tr
key={participant.id}
className={`border-t transition-colors ${
isDrafted
? "bg-muted/50 opacity-60"
: !isEligible
? "bg-red-50/30 dark:bg-red-950/20 opacity-75"
: "hover:bg-muted/50"
}`}
title={
!isEligible && !isDrafted ? ineligibleReason : undefined
}
>
<td className="p-3">
<div className="flex items-center gap-2">
<span
className={`font-medium ${!isEligible && !isDrafted ? "text-muted-foreground" : ""}`}
>
{participant.name}
</span>
{isDrafted && (
<Badge variant="secondary" className="text-xs">
Drafted
</Badge>
)}
{!isDrafted && !isEligible && (
<Badge
variant="destructive"
className="text-xs"
title={ineligibleReason}
>
Ineligible
</Badge>
)}
{isInQueue && !isDrafted && isEligible && (
<Badge variant="default" className="text-xs">
Queued
</Badge>
)}
</div>
</td>
<td className="p-3 text-muted-foreground">
{participant.sport.name}
</td>
<td className="p-3 text-right font-mono font-semibold">
{participant.expectedValue}
</td>
{hasTeam && (
<td className="p-3 text-right">
<div className="flex gap-2 justify-end items-center">
{/* Pick button - only show if it's your turn and participant not drafted */}
{canPick && !isDrafted && (
<>
<Button
variant="default"
size="sm"
onClick={() => onMakePick(participant.id)}
disabled={!isEligible}
title={
!isEligible ? ineligibleReason : undefined
}
>
Pick
</Button>
{/* Queue icon button next to Pick */}
{!isInQueue ? (
<Button
variant="ghost"
size="sm"
onClick={() => onAddToQueue(participant.id)}
title={
!isEligible
? ineligibleReason
: "Add to queue"
}
disabled={!isEligible}
>
<span className="text-lg">+</span>
</Button>
) : (
<Button
variant="ghost"
size="sm"
onClick={() => {
const queueItem = queue.find(
(item) =>
item.participantId === participant.id
);
if (queueItem) {
onRemoveFromQueue(queueItem.id);
}
}}
title="Remove from queue"
>
<span className="text-lg"></span>
</Button>
)}
</>
)}
{/* Queue buttons - only show if not your turn */}
{!canPick && !isDrafted && !isInQueue && (
<Button
variant="outline"
size="sm"
onClick={() => onAddToQueue(participant.id)}
disabled={!isEligible}
title={!isEligible ? ineligibleReason : undefined}
>
Add to Queue
</Button>
)}
{!canPick && !isDrafted && isInQueue && (
<Button
variant="ghost"
size="sm"
onClick={() => {
const queueItem = queue.find(
(item) =>
item.participantId === participant.id
);
if (queueItem) {
onRemoveFromQueue(queueItem.id);
}
}}
>
Remove
</Button>
)}
</div>
</td>
)}
</tr>
);
})
)}
</tbody>
</table>
</div>
</div>
</div>
);
}