2026-02-27 23:46:09 -08:00
|
|
|
import { useState, useEffect, useMemo } from "react";
|
|
|
|
|
import { Button } from "~/components/ui/button";
|
|
|
|
|
import { Badge } from "~/components/ui/badge";
|
2026-05-04 21:54:14 -07:00
|
|
|
import type { DraftIneligibilityReason } from "~/lib/draft-eligibility";
|
2026-02-27 23:46:09 -08:00
|
|
|
import {
|
|
|
|
|
Dialog,
|
|
|
|
|
DialogContent,
|
|
|
|
|
DialogDescription,
|
|
|
|
|
DialogFooter,
|
|
|
|
|
DialogHeader,
|
|
|
|
|
DialogTitle,
|
|
|
|
|
} from "~/components/ui/dialog";
|
|
|
|
|
|
|
|
|
|
interface ParticipantSelectionDialogProps {
|
|
|
|
|
open: boolean;
|
|
|
|
|
onOpenChange: (open: boolean) => void;
|
|
|
|
|
title: string;
|
|
|
|
|
description: string;
|
|
|
|
|
participants: Array<{
|
|
|
|
|
id: string;
|
|
|
|
|
name: string;
|
|
|
|
|
sport: { id: string; name: string };
|
|
|
|
|
}>;
|
|
|
|
|
draftedParticipantIds: Set<string>;
|
|
|
|
|
/** Pass the old participant's ID to keep them in the list (replace-pick dialog) */
|
|
|
|
|
allowParticipantId?: string;
|
|
|
|
|
eligibility: {
|
|
|
|
|
eligibleSportIds: Set<string>;
|
2026-05-04 21:54:14 -07:00
|
|
|
ineligibleReasons: Record<string, DraftIneligibilityReason>;
|
2026-02-27 23:46:09 -08:00
|
|
|
} | null;
|
|
|
|
|
/** Marks a row with "Current" badge and "Keep" button label */
|
|
|
|
|
currentParticipantId?: string;
|
|
|
|
|
uniqueSports: string[];
|
|
|
|
|
onSelect: (participantId: string) => void;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function ParticipantSelectionDialog({
|
|
|
|
|
open,
|
|
|
|
|
onOpenChange,
|
|
|
|
|
title,
|
|
|
|
|
description,
|
|
|
|
|
participants,
|
|
|
|
|
draftedParticipantIds,
|
|
|
|
|
allowParticipantId,
|
|
|
|
|
eligibility,
|
|
|
|
|
currentParticipantId,
|
|
|
|
|
uniqueSports,
|
|
|
|
|
onSelect,
|
|
|
|
|
}: ParticipantSelectionDialogProps) {
|
|
|
|
|
const [searchQuery, setSearchQuery] = useState("");
|
|
|
|
|
const [sportFilter, setSportFilter] = useState("all");
|
|
|
|
|
|
|
|
|
|
// Reset filters each time the dialog opens
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
if (open) {
|
|
|
|
|
setSearchQuery("");
|
|
|
|
|
setSportFilter("all");
|
|
|
|
|
}
|
|
|
|
|
}, [open]);
|
|
|
|
|
|
|
|
|
|
const filtered = useMemo(
|
|
|
|
|
() =>
|
|
|
|
|
participants.filter((p) => {
|
|
|
|
|
if (p.id !== allowParticipantId && draftedParticipantIds.has(p.id))
|
|
|
|
|
return false;
|
|
|
|
|
if (
|
|
|
|
|
searchQuery &&
|
|
|
|
|
!p.name.toLowerCase().includes(searchQuery.toLowerCase())
|
|
|
|
|
)
|
|
|
|
|
return false;
|
|
|
|
|
if (sportFilter !== "all" && p.sport.name !== sportFilter) return false;
|
|
|
|
|
return true;
|
|
|
|
|
}),
|
|
|
|
|
[participants, draftedParticipantIds, allowParticipantId, searchQuery, sportFilter]
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Dialog open={open} onOpenChange={onOpenChange}>
|
|
|
|
|
<DialogContent className="max-w-2xl max-h-[80vh] flex flex-col gap-0 p-0">
|
|
|
|
|
<DialogHeader className="p-6 pb-4">
|
|
|
|
|
<DialogTitle>{title}</DialogTitle>
|
|
|
|
|
<DialogDescription>{description}</DialogDescription>
|
|
|
|
|
</DialogHeader>
|
|
|
|
|
|
|
|
|
|
<div className="px-6 pb-4">
|
|
|
|
|
<div className="flex gap-2 mb-4">
|
fix: resolve all 48 WCAG 2.2 AA accessibility issues (#439)
* fix: resolve all 48 WCAG 2.2 AA accessibility issues
Critical fixes:
- Add aria-label to all unlabeled inputs/selects in draft dialogs (ParticipantSelectionDialog, TimeBankAdjustmentDialog, AvailableParticipantsSection)
- Add role="dialog" + aria-modal + focus trap to ConnectionOverlay and AuthRecoveryOverlay
- Add aria-live region and connection status announcement to ConnectionOverlay
Serious fixes:
- Add skip-to-content link in root.tsx with id="main-content" on <main>
- Add aria-label to UserMenu trigger button
- Add aria-describedby + role="alert" to all auth form error messages (login, register, onboarding, forgot-password, reset-password)
- Replace emoji column headers in StandingsTable with aria-label + aria-hidden spans
- Add aria-live="assertive" to "It's your turn" desktop and mobile on-clock indicators
- Add aria-live="polite" to draft room countdown timer
- Add pause button to SportTicker (WCAG 2.2.2); add aria-hidden to ticker content
- Fix Footer text contrast (changed from 28% to text-muted-foreground)
- Fix OvernightPauseSettings: add htmlFor/id pairs and role="radiogroup"+aria-checked to mode buttons
- Fix DraftSetupSection: replace broken htmlFor with aria-label on date picker button
- Add aria-label to PeopleSection owner and commissioner selects
- Add labels to ScoringPresetPicker score inputs; add role="radiogroup"+aria-checked to preset buttons
- Add role="radiogroup"+aria-checked to AutodraftSettings option buttons
- Add accessible names, aria-current="step", and <ol> list semantics to WizardStepper
Moderate fixes:
- Add aria-controls to RecentPicksFeed toggle button; wrap picks list in aria-live region
- Add role="tab"+aria-selected+aria-controls to mobile board sub-tabs + role="tabpanel"
- Add role="radiogroup"+aria-checked to TimerModeSelector
- Add aria-current="page" + aria-label to SettingsDesktopNav
- Add aria-label="Admin navigation" to admin sidebar nav
- Add scope="col" + <caption> to StandingsTable and ScoringTables
- Add ARIA table roles (role="table/rowgroup/row/columnheader/rowheader/cell") to DraftSummaryView CSS grid
Minor fixes:
- Add aria-hidden="true" to decorative trend icons in StandingsTable
- Add aria-hidden="true" to desktop column header labels row in AvailableParticipantsSection
- Replace title with aria-label on all icon-only buttons (watchlist, queue) in AvailableParticipantsSection
- Add aria-label to NotificationSettings switchOnly Switch
- Add prefers-reduced-motion check to SlotMachineHeadline JS animation
- Bump --muted-foreground from 55% to 62% opacity for improved contrast margin
https://claude.ai/code/session_01JXajpFxhqLf8aPCncP81k3
* Fix code review findings from WCAG compliance pass
- Add Arrow key navigation + roving tabindex to all role=radiogroup
components (AutodraftSettings x2, TimerModeSelector,
OvernightPauseSettings, ScoringPresetPicker) per ARIA radio pattern
- Extract shared focus-trap logic into useFocusTrap hook; update
ConnectionOverlay and AuthRecoveryOverlay to use it
- Add tabIndex={-1} to ConnectionOverlay Card so focus can land in
spinner-only state (no interactive children)
- Replace aria-live on loading dots container with sr-only span so
status changes are announced by text content, not aria-label
- Remove contradictory aria-hidden+role=columnheader from
AvailableParticipantsSection visual-only header row
- Remove invalid scope="col" from div[role=columnheader] in
DraftSummaryView (scope is only valid on <th>)
- Remove redundant aria-label from ParticipantSelectionDialog sport
select (htmlFor label is sufficient)
- Change WizardStepper connector <li> to role=presentation
- Revert muted-foreground from 62% to 55% (original already passes
contrast; footer was fixed separately via text-muted-foreground)
https://claude.ai/code/session_01JXajpFxhqLf8aPCncP81k3
* Fix lint error and update tests for WCAG role changes
- Replace el! non-null assertion with optional chaining in useFocusTrap
- Update AutodraftSettings tests to query role="radio" instead of
role="button" (buttons have an explicit radio role since the WCAG pass)
- Update AvailableParticipantsSection watchlist tests to use
getByRole/getAllByRole instead of getByTitle/getAllByTitle (watchlist
buttons now use aria-label instead of title)
https://claude.ai/code/session_01JXajpFxhqLf8aPCncP81k3
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-05-17 20:11:38 -07:00
|
|
|
<label htmlFor="participant-dialog-search" className="sr-only">Search participants</label>
|
2026-02-27 23:46:09 -08:00
|
|
|
<input
|
fix: resolve all 48 WCAG 2.2 AA accessibility issues (#439)
* fix: resolve all 48 WCAG 2.2 AA accessibility issues
Critical fixes:
- Add aria-label to all unlabeled inputs/selects in draft dialogs (ParticipantSelectionDialog, TimeBankAdjustmentDialog, AvailableParticipantsSection)
- Add role="dialog" + aria-modal + focus trap to ConnectionOverlay and AuthRecoveryOverlay
- Add aria-live region and connection status announcement to ConnectionOverlay
Serious fixes:
- Add skip-to-content link in root.tsx with id="main-content" on <main>
- Add aria-label to UserMenu trigger button
- Add aria-describedby + role="alert" to all auth form error messages (login, register, onboarding, forgot-password, reset-password)
- Replace emoji column headers in StandingsTable with aria-label + aria-hidden spans
- Add aria-live="assertive" to "It's your turn" desktop and mobile on-clock indicators
- Add aria-live="polite" to draft room countdown timer
- Add pause button to SportTicker (WCAG 2.2.2); add aria-hidden to ticker content
- Fix Footer text contrast (changed from 28% to text-muted-foreground)
- Fix OvernightPauseSettings: add htmlFor/id pairs and role="radiogroup"+aria-checked to mode buttons
- Fix DraftSetupSection: replace broken htmlFor with aria-label on date picker button
- Add aria-label to PeopleSection owner and commissioner selects
- Add labels to ScoringPresetPicker score inputs; add role="radiogroup"+aria-checked to preset buttons
- Add role="radiogroup"+aria-checked to AutodraftSettings option buttons
- Add accessible names, aria-current="step", and <ol> list semantics to WizardStepper
Moderate fixes:
- Add aria-controls to RecentPicksFeed toggle button; wrap picks list in aria-live region
- Add role="tab"+aria-selected+aria-controls to mobile board sub-tabs + role="tabpanel"
- Add role="radiogroup"+aria-checked to TimerModeSelector
- Add aria-current="page" + aria-label to SettingsDesktopNav
- Add aria-label="Admin navigation" to admin sidebar nav
- Add scope="col" + <caption> to StandingsTable and ScoringTables
- Add ARIA table roles (role="table/rowgroup/row/columnheader/rowheader/cell") to DraftSummaryView CSS grid
Minor fixes:
- Add aria-hidden="true" to decorative trend icons in StandingsTable
- Add aria-hidden="true" to desktop column header labels row in AvailableParticipantsSection
- Replace title with aria-label on all icon-only buttons (watchlist, queue) in AvailableParticipantsSection
- Add aria-label to NotificationSettings switchOnly Switch
- Add prefers-reduced-motion check to SlotMachineHeadline JS animation
- Bump --muted-foreground from 55% to 62% opacity for improved contrast margin
https://claude.ai/code/session_01JXajpFxhqLf8aPCncP81k3
* Fix code review findings from WCAG compliance pass
- Add Arrow key navigation + roving tabindex to all role=radiogroup
components (AutodraftSettings x2, TimerModeSelector,
OvernightPauseSettings, ScoringPresetPicker) per ARIA radio pattern
- Extract shared focus-trap logic into useFocusTrap hook; update
ConnectionOverlay and AuthRecoveryOverlay to use it
- Add tabIndex={-1} to ConnectionOverlay Card so focus can land in
spinner-only state (no interactive children)
- Replace aria-live on loading dots container with sr-only span so
status changes are announced by text content, not aria-label
- Remove contradictory aria-hidden+role=columnheader from
AvailableParticipantsSection visual-only header row
- Remove invalid scope="col" from div[role=columnheader] in
DraftSummaryView (scope is only valid on <th>)
- Remove redundant aria-label from ParticipantSelectionDialog sport
select (htmlFor label is sufficient)
- Change WizardStepper connector <li> to role=presentation
- Revert muted-foreground from 62% to 55% (original already passes
contrast; footer was fixed separately via text-muted-foreground)
https://claude.ai/code/session_01JXajpFxhqLf8aPCncP81k3
* Fix lint error and update tests for WCAG role changes
- Replace el! non-null assertion with optional chaining in useFocusTrap
- Update AutodraftSettings tests to query role="radio" instead of
role="button" (buttons have an explicit radio role since the WCAG pass)
- Update AvailableParticipantsSection watchlist tests to use
getByRole/getAllByRole instead of getByTitle/getAllByTitle (watchlist
buttons now use aria-label instead of title)
https://claude.ai/code/session_01JXajpFxhqLf8aPCncP81k3
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-05-17 20:11:38 -07:00
|
|
|
id="participant-dialog-search"
|
2026-02-27 23:46:09 -08:00
|
|
|
type="text"
|
|
|
|
|
placeholder="Search participants..."
|
|
|
|
|
className="flex-1 px-3 py-2 border rounded-md text-base md:text-sm bg-background text-foreground"
|
|
|
|
|
value={searchQuery}
|
|
|
|
|
onChange={(e) => setSearchQuery(e.target.value)}
|
|
|
|
|
/>
|
fix: resolve all 48 WCAG 2.2 AA accessibility issues (#439)
* fix: resolve all 48 WCAG 2.2 AA accessibility issues
Critical fixes:
- Add aria-label to all unlabeled inputs/selects in draft dialogs (ParticipantSelectionDialog, TimeBankAdjustmentDialog, AvailableParticipantsSection)
- Add role="dialog" + aria-modal + focus trap to ConnectionOverlay and AuthRecoveryOverlay
- Add aria-live region and connection status announcement to ConnectionOverlay
Serious fixes:
- Add skip-to-content link in root.tsx with id="main-content" on <main>
- Add aria-label to UserMenu trigger button
- Add aria-describedby + role="alert" to all auth form error messages (login, register, onboarding, forgot-password, reset-password)
- Replace emoji column headers in StandingsTable with aria-label + aria-hidden spans
- Add aria-live="assertive" to "It's your turn" desktop and mobile on-clock indicators
- Add aria-live="polite" to draft room countdown timer
- Add pause button to SportTicker (WCAG 2.2.2); add aria-hidden to ticker content
- Fix Footer text contrast (changed from 28% to text-muted-foreground)
- Fix OvernightPauseSettings: add htmlFor/id pairs and role="radiogroup"+aria-checked to mode buttons
- Fix DraftSetupSection: replace broken htmlFor with aria-label on date picker button
- Add aria-label to PeopleSection owner and commissioner selects
- Add labels to ScoringPresetPicker score inputs; add role="radiogroup"+aria-checked to preset buttons
- Add role="radiogroup"+aria-checked to AutodraftSettings option buttons
- Add accessible names, aria-current="step", and <ol> list semantics to WizardStepper
Moderate fixes:
- Add aria-controls to RecentPicksFeed toggle button; wrap picks list in aria-live region
- Add role="tab"+aria-selected+aria-controls to mobile board sub-tabs + role="tabpanel"
- Add role="radiogroup"+aria-checked to TimerModeSelector
- Add aria-current="page" + aria-label to SettingsDesktopNav
- Add aria-label="Admin navigation" to admin sidebar nav
- Add scope="col" + <caption> to StandingsTable and ScoringTables
- Add ARIA table roles (role="table/rowgroup/row/columnheader/rowheader/cell") to DraftSummaryView CSS grid
Minor fixes:
- Add aria-hidden="true" to decorative trend icons in StandingsTable
- Add aria-hidden="true" to desktop column header labels row in AvailableParticipantsSection
- Replace title with aria-label on all icon-only buttons (watchlist, queue) in AvailableParticipantsSection
- Add aria-label to NotificationSettings switchOnly Switch
- Add prefers-reduced-motion check to SlotMachineHeadline JS animation
- Bump --muted-foreground from 55% to 62% opacity for improved contrast margin
https://claude.ai/code/session_01JXajpFxhqLf8aPCncP81k3
* Fix code review findings from WCAG compliance pass
- Add Arrow key navigation + roving tabindex to all role=radiogroup
components (AutodraftSettings x2, TimerModeSelector,
OvernightPauseSettings, ScoringPresetPicker) per ARIA radio pattern
- Extract shared focus-trap logic into useFocusTrap hook; update
ConnectionOverlay and AuthRecoveryOverlay to use it
- Add tabIndex={-1} to ConnectionOverlay Card so focus can land in
spinner-only state (no interactive children)
- Replace aria-live on loading dots container with sr-only span so
status changes are announced by text content, not aria-label
- Remove contradictory aria-hidden+role=columnheader from
AvailableParticipantsSection visual-only header row
- Remove invalid scope="col" from div[role=columnheader] in
DraftSummaryView (scope is only valid on <th>)
- Remove redundant aria-label from ParticipantSelectionDialog sport
select (htmlFor label is sufficient)
- Change WizardStepper connector <li> to role=presentation
- Revert muted-foreground from 62% to 55% (original already passes
contrast; footer was fixed separately via text-muted-foreground)
https://claude.ai/code/session_01JXajpFxhqLf8aPCncP81k3
* Fix lint error and update tests for WCAG role changes
- Replace el! non-null assertion with optional chaining in useFocusTrap
- Update AutodraftSettings tests to query role="radio" instead of
role="button" (buttons have an explicit radio role since the WCAG pass)
- Update AvailableParticipantsSection watchlist tests to use
getByRole/getAllByRole instead of getByTitle/getAllByTitle (watchlist
buttons now use aria-label instead of title)
https://claude.ai/code/session_01JXajpFxhqLf8aPCncP81k3
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-05-17 20:11:38 -07:00
|
|
|
<label htmlFor="participant-dialog-sport" className="sr-only">Filter by sport</label>
|
2026-02-27 23:46:09 -08:00
|
|
|
<select
|
fix: resolve all 48 WCAG 2.2 AA accessibility issues (#439)
* fix: resolve all 48 WCAG 2.2 AA accessibility issues
Critical fixes:
- Add aria-label to all unlabeled inputs/selects in draft dialogs (ParticipantSelectionDialog, TimeBankAdjustmentDialog, AvailableParticipantsSection)
- Add role="dialog" + aria-modal + focus trap to ConnectionOverlay and AuthRecoveryOverlay
- Add aria-live region and connection status announcement to ConnectionOverlay
Serious fixes:
- Add skip-to-content link in root.tsx with id="main-content" on <main>
- Add aria-label to UserMenu trigger button
- Add aria-describedby + role="alert" to all auth form error messages (login, register, onboarding, forgot-password, reset-password)
- Replace emoji column headers in StandingsTable with aria-label + aria-hidden spans
- Add aria-live="assertive" to "It's your turn" desktop and mobile on-clock indicators
- Add aria-live="polite" to draft room countdown timer
- Add pause button to SportTicker (WCAG 2.2.2); add aria-hidden to ticker content
- Fix Footer text contrast (changed from 28% to text-muted-foreground)
- Fix OvernightPauseSettings: add htmlFor/id pairs and role="radiogroup"+aria-checked to mode buttons
- Fix DraftSetupSection: replace broken htmlFor with aria-label on date picker button
- Add aria-label to PeopleSection owner and commissioner selects
- Add labels to ScoringPresetPicker score inputs; add role="radiogroup"+aria-checked to preset buttons
- Add role="radiogroup"+aria-checked to AutodraftSettings option buttons
- Add accessible names, aria-current="step", and <ol> list semantics to WizardStepper
Moderate fixes:
- Add aria-controls to RecentPicksFeed toggle button; wrap picks list in aria-live region
- Add role="tab"+aria-selected+aria-controls to mobile board sub-tabs + role="tabpanel"
- Add role="radiogroup"+aria-checked to TimerModeSelector
- Add aria-current="page" + aria-label to SettingsDesktopNav
- Add aria-label="Admin navigation" to admin sidebar nav
- Add scope="col" + <caption> to StandingsTable and ScoringTables
- Add ARIA table roles (role="table/rowgroup/row/columnheader/rowheader/cell") to DraftSummaryView CSS grid
Minor fixes:
- Add aria-hidden="true" to decorative trend icons in StandingsTable
- Add aria-hidden="true" to desktop column header labels row in AvailableParticipantsSection
- Replace title with aria-label on all icon-only buttons (watchlist, queue) in AvailableParticipantsSection
- Add aria-label to NotificationSettings switchOnly Switch
- Add prefers-reduced-motion check to SlotMachineHeadline JS animation
- Bump --muted-foreground from 55% to 62% opacity for improved contrast margin
https://claude.ai/code/session_01JXajpFxhqLf8aPCncP81k3
* Fix code review findings from WCAG compliance pass
- Add Arrow key navigation + roving tabindex to all role=radiogroup
components (AutodraftSettings x2, TimerModeSelector,
OvernightPauseSettings, ScoringPresetPicker) per ARIA radio pattern
- Extract shared focus-trap logic into useFocusTrap hook; update
ConnectionOverlay and AuthRecoveryOverlay to use it
- Add tabIndex={-1} to ConnectionOverlay Card so focus can land in
spinner-only state (no interactive children)
- Replace aria-live on loading dots container with sr-only span so
status changes are announced by text content, not aria-label
- Remove contradictory aria-hidden+role=columnheader from
AvailableParticipantsSection visual-only header row
- Remove invalid scope="col" from div[role=columnheader] in
DraftSummaryView (scope is only valid on <th>)
- Remove redundant aria-label from ParticipantSelectionDialog sport
select (htmlFor label is sufficient)
- Change WizardStepper connector <li> to role=presentation
- Revert muted-foreground from 62% to 55% (original already passes
contrast; footer was fixed separately via text-muted-foreground)
https://claude.ai/code/session_01JXajpFxhqLf8aPCncP81k3
* Fix lint error and update tests for WCAG role changes
- Replace el! non-null assertion with optional chaining in useFocusTrap
- Update AutodraftSettings tests to query role="radio" instead of
role="button" (buttons have an explicit radio role since the WCAG pass)
- Update AvailableParticipantsSection watchlist tests to use
getByRole/getAllByRole instead of getByTitle/getAllByTitle (watchlist
buttons now use aria-label instead of title)
https://claude.ai/code/session_01JXajpFxhqLf8aPCncP81k3
---------
Co-authored-by: Claude <noreply@anthropic.com>
2026-05-17 20:11:38 -07:00
|
|
|
id="participant-dialog-sport"
|
2026-02-27 23:46:09 -08:00
|
|
|
className="px-3 py-2 border rounded-md text-base md:text-sm bg-background text-foreground"
|
|
|
|
|
value={sportFilter}
|
|
|
|
|
onChange={(e) => setSportFilter(e.target.value)}
|
|
|
|
|
>
|
|
|
|
|
<option value="all">All Sports</option>
|
|
|
|
|
{uniqueSports.map((sport) => (
|
|
|
|
|
<option key={sport} value={sport}>
|
|
|
|
|
{sport}
|
|
|
|
|
</option>
|
|
|
|
|
))}
|
|
|
|
|
</select>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="max-h-96 overflow-y-auto border rounded-lg">
|
|
|
|
|
<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">Action</th>
|
|
|
|
|
</tr>
|
|
|
|
|
</thead>
|
|
|
|
|
<tbody>
|
|
|
|
|
{filtered.map((participant) => {
|
|
|
|
|
const isCurrentPick =
|
|
|
|
|
participant.id === currentParticipantId;
|
|
|
|
|
const isEligible = eligibility
|
|
|
|
|
? eligibility.eligibleSportIds.has(participant.sport.id)
|
|
|
|
|
: true;
|
|
|
|
|
const ineligibleReason =
|
2026-05-04 21:54:14 -07:00
|
|
|
eligibility?.ineligibleReasons[participant.sport.id] ?? null;
|
2026-02-27 23:46:09 -08:00
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<tr
|
|
|
|
|
key={participant.id}
|
|
|
|
|
className={`border-t ${
|
|
|
|
|
isEligible
|
|
|
|
|
? "hover:bg-muted/50"
|
|
|
|
|
: "bg-destructive/10 opacity-60"
|
|
|
|
|
}`}
|
2026-05-04 21:54:14 -07:00
|
|
|
title={!isEligible ? ineligibleReason?.message : undefined}
|
2026-02-27 23:46:09 -08:00
|
|
|
>
|
|
|
|
|
<td className="p-3">
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
<span
|
|
|
|
|
className={`font-medium ${!isEligible ? "text-muted-foreground" : ""}`}
|
|
|
|
|
>
|
|
|
|
|
{participant.name}
|
|
|
|
|
</span>
|
|
|
|
|
{isCurrentPick && (
|
|
|
|
|
<Badge variant="outline" className="text-xs">
|
|
|
|
|
Current
|
|
|
|
|
</Badge>
|
|
|
|
|
)}
|
|
|
|
|
{!isEligible && (
|
|
|
|
|
<Badge
|
|
|
|
|
variant="destructive"
|
|
|
|
|
className="text-xs"
|
2026-05-04 21:54:14 -07:00
|
|
|
title={ineligibleReason?.message}
|
2026-02-27 23:46:09 -08:00
|
|
|
>
|
|
|
|
|
Ineligible
|
|
|
|
|
</Badge>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
2026-05-04 21:54:14 -07:00
|
|
|
{!isEligible && ineligibleReason && (
|
|
|
|
|
<p className="mt-1 text-xs text-destructive/90">
|
|
|
|
|
{ineligibleReason.message}
|
|
|
|
|
</p>
|
|
|
|
|
)}
|
2026-02-27 23:46:09 -08:00
|
|
|
</td>
|
|
|
|
|
<td className="p-3 text-muted-foreground">
|
|
|
|
|
{participant.sport.name}
|
|
|
|
|
</td>
|
|
|
|
|
<td className="p-3 text-right">
|
|
|
|
|
<Button
|
|
|
|
|
size="sm"
|
|
|
|
|
onClick={() => onSelect(participant.id)}
|
|
|
|
|
disabled={!isEligible}
|
2026-05-04 21:54:14 -07:00
|
|
|
title={!isEligible ? ineligibleReason?.message : undefined}
|
2026-02-27 23:46:09 -08:00
|
|
|
>
|
|
|
|
|
{isCurrentPick ? "Keep" : "Draft"}
|
|
|
|
|
</Button>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</tbody>
|
|
|
|
|
</table>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<DialogFooter className="px-6 pb-6">
|
|
|
|
|
<Button variant="outline" onClick={() => onOpenChange(false)}>
|
|
|
|
|
Cancel
|
|
|
|
|
</Button>
|
|
|
|
|
</DialogFooter>
|
|
|
|
|
</DialogContent>
|
|
|
|
|
</Dialog>
|
|
|
|
|
);
|
|
|
|
|
}
|