2026-03-02 13:18:47 -08:00
|
|
|
import { memo, useMemo, useState } from "react";
|
2025-10-25 03:23:41 -07:00
|
|
|
import {
|
|
|
|
|
ContextMenu,
|
|
|
|
|
ContextMenuContent,
|
|
|
|
|
ContextMenuItem,
|
|
|
|
|
ContextMenuTrigger,
|
|
|
|
|
} from "~/components/ui/context-menu";
|
2026-02-22 22:36:12 -08:00
|
|
|
import {
|
|
|
|
|
Sheet,
|
|
|
|
|
SheetContent,
|
|
|
|
|
SheetHeader,
|
|
|
|
|
SheetTitle,
|
|
|
|
|
} from "~/components/ui/sheet";
|
|
|
|
|
import { Button } from "~/components/ui/button";
|
|
|
|
|
import { MoreVertical } from "lucide-react";
|
Add draft clock UI, Fischer increment timer logic, and security fixes (#19)
- Add prominent clock badge to tab bar (lights up on your turn) with
correct Fischer increment chess-clock model: bank starts at initialTime,
+= incrementTime after each pick, other teams' banks untouched
- Extract pure timer helpers to app/lib/draft-timer.ts and add 29 unit
tests covering formatClockTime, calculateTimeAfterPick, getTimerColorClass,
and full snake-draft lifecycle regression scenarios
- Fix make-pick.ts: add status !== 'draft' and draftPaused server guards
- Fix draft-utils.ts: replace (global as any).__socketIO with getSocketIO(),
fix timeUsed to store actual timeRemaining at pick moment (not always 120),
add null timer warning, move timer fetch before pick insert
- Fix draft.start.ts: batch timer inserts, guard against empty draftSlots
- Fix DraftGridSection: memoize currentTeamId, widen teamTimers type to
Record<string, number | undefined>
- Fix duplicate animate-pulse (getTimerColorClass already includes it)
- Clamp negative seconds in formatClockTime to guard against timer drift
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-21 16:51:12 -08:00
|
|
|
import { formatClockTime, getTimerColorClass } from "~/lib/draft-timer";
|
2025-10-25 03:23:41 -07:00
|
|
|
|
2026-02-22 22:36:12 -08:00
|
|
|
type MobileSheetData =
|
|
|
|
|
| { type: "team"; teamId: string }
|
|
|
|
|
| { type: "current-cell"; pickNumber: number; teamId: string }
|
|
|
|
|
| { type: "picked-cell"; pickNumber: number; teamId: string };
|
|
|
|
|
|
2025-10-25 03:23:41 -07:00
|
|
|
interface DraftGridSectionProps {
|
|
|
|
|
draftSlots: Array<{
|
|
|
|
|
id: string;
|
|
|
|
|
draftOrder: number;
|
|
|
|
|
team: {
|
|
|
|
|
id: string;
|
|
|
|
|
name: string;
|
|
|
|
|
};
|
|
|
|
|
}>;
|
|
|
|
|
draftGrid: Array<
|
|
|
|
|
Array<{
|
|
|
|
|
pickNumber: number;
|
|
|
|
|
round: number;
|
|
|
|
|
pickInRound: number;
|
|
|
|
|
teamId: string;
|
|
|
|
|
pick?: {
|
|
|
|
|
participant: {
|
|
|
|
|
name: string;
|
|
|
|
|
};
|
|
|
|
|
sport: {
|
|
|
|
|
name: string;
|
|
|
|
|
};
|
|
|
|
|
};
|
|
|
|
|
}>
|
|
|
|
|
>;
|
|
|
|
|
currentPick: number;
|
Add draft clock UI, Fischer increment timer logic, and security fixes (#19)
- Add prominent clock badge to tab bar (lights up on your turn) with
correct Fischer increment chess-clock model: bank starts at initialTime,
+= incrementTime after each pick, other teams' banks untouched
- Extract pure timer helpers to app/lib/draft-timer.ts and add 29 unit
tests covering formatClockTime, calculateTimeAfterPick, getTimerColorClass,
and full snake-draft lifecycle regression scenarios
- Fix make-pick.ts: add status !== 'draft' and draftPaused server guards
- Fix draft-utils.ts: replace (global as any).__socketIO with getSocketIO(),
fix timeUsed to store actual timeRemaining at pick moment (not always 120),
add null timer warning, move timer fetch before pick insert
- Fix draft.start.ts: batch timer inserts, guard against empty draftSlots
- Fix DraftGridSection: memoize currentTeamId, widen teamTimers type to
Record<string, number | undefined>
- Fix duplicate animate-pulse (getTimerColorClass already includes it)
- Clamp negative seconds in formatClockTime to guard against timer drift
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-21 16:51:12 -08:00
|
|
|
teamTimers: Record<string, number | undefined>;
|
2026-03-03 20:14:38 -08:00
|
|
|
autodraftStatus: Record<string, { isEnabled: boolean; mode: "next_pick" | "while_on"; queueOnly: boolean }>;
|
2025-10-25 03:23:41 -07:00
|
|
|
connectedTeams: Set<string>;
|
|
|
|
|
isCommissioner: boolean;
|
2026-02-22 16:16:51 -08:00
|
|
|
onAdjustTimeBankOpen?: (teamId: string) => void;
|
2026-03-03 20:14:38 -08:00
|
|
|
onSetAutodraftOpen?: (teamId: string) => void;
|
2025-10-25 03:23:41 -07:00
|
|
|
onForceAutopick: (pickNumber: number, teamId: string) => void;
|
|
|
|
|
onForceManualPickOpen: (pickNumber: number, teamId: string) => void;
|
2026-02-20 22:47:29 -08:00
|
|
|
onReplacePick?: (pickNumber: number, teamId: string) => void;
|
|
|
|
|
onRollbackToPick?: (pickNumber: number) => void;
|
2026-02-22 16:56:07 -08:00
|
|
|
ownerMap?: Record<string, string>;
|
2025-10-25 03:23:41 -07:00
|
|
|
}
|
|
|
|
|
|
2026-03-02 13:18:47 -08:00
|
|
|
export const DraftGridSection = memo(function DraftGridSection({
|
2025-10-25 03:23:41 -07:00
|
|
|
draftSlots,
|
|
|
|
|
draftGrid,
|
|
|
|
|
currentPick,
|
|
|
|
|
teamTimers,
|
|
|
|
|
autodraftStatus,
|
|
|
|
|
connectedTeams,
|
|
|
|
|
isCommissioner,
|
2026-02-22 16:16:51 -08:00
|
|
|
onAdjustTimeBankOpen,
|
2026-03-03 20:14:38 -08:00
|
|
|
onSetAutodraftOpen,
|
2025-10-25 03:23:41 -07:00
|
|
|
onForceAutopick,
|
|
|
|
|
onForceManualPickOpen,
|
2026-02-20 22:47:29 -08:00
|
|
|
onReplacePick,
|
|
|
|
|
onRollbackToPick,
|
2026-02-22 16:56:07 -08:00
|
|
|
ownerMap = {},
|
2025-10-25 03:23:41 -07:00
|
|
|
}: DraftGridSectionProps) {
|
2026-02-22 22:36:12 -08:00
|
|
|
const [mobileSheet, setMobileSheet] = useState<MobileSheetData | null>(null);
|
|
|
|
|
|
Add draft clock UI, Fischer increment timer logic, and security fixes (#19)
- Add prominent clock badge to tab bar (lights up on your turn) with
correct Fischer increment chess-clock model: bank starts at initialTime,
+= incrementTime after each pick, other teams' banks untouched
- Extract pure timer helpers to app/lib/draft-timer.ts and add 29 unit
tests covering formatClockTime, calculateTimeAfterPick, getTimerColorClass,
and full snake-draft lifecycle regression scenarios
- Fix make-pick.ts: add status !== 'draft' and draftPaused server guards
- Fix draft-utils.ts: replace (global as any).__socketIO with getSocketIO(),
fix timeUsed to store actual timeRemaining at pick moment (not always 120),
add null timer warning, move timer fetch before pick insert
- Fix draft.start.ts: batch timer inserts, guard against empty draftSlots
- Fix DraftGridSection: memoize currentTeamId, widen teamTimers type to
Record<string, number | undefined>
- Fix duplicate animate-pulse (getTimerColorClass already includes it)
- Clamp negative seconds in formatClockTime to guard against timer drift
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-21 16:51:12 -08:00
|
|
|
const currentTeamId = useMemo(
|
|
|
|
|
() => draftGrid.flat().find((c) => c.pickNumber === currentPick)?.teamId ?? null,
|
|
|
|
|
[draftGrid, currentPick]
|
|
|
|
|
);
|
2025-10-25 03:23:41 -07:00
|
|
|
|
|
|
|
|
return (
|
2026-02-24 10:01:27 -08:00
|
|
|
<div className="h-full flex flex-col p-4">
|
|
|
|
|
<h2 className="text-xl font-semibold mb-4 flex-shrink-0">Draft Grid</h2>
|
|
|
|
|
<div className="flex-1 overflow-auto">
|
|
|
|
|
<div className="inline-block min-w-full min-h-full">
|
2025-10-25 03:23:41 -07:00
|
|
|
{/* Team Headers */}
|
2026-02-22 22:36:12 -08:00
|
|
|
<div className="flex gap-2 mb-2 sticky top-0 z-10 bg-background/95 backdrop-blur-sm py-1">
|
|
|
|
|
{/* Spacer for round column — sticky so it covers the corner when scrolling both axes */}
|
|
|
|
|
<div className="w-8 flex-shrink-0 sticky left-0 z-[6] bg-background/95" />
|
2025-10-25 03:23:41 -07:00
|
|
|
{draftSlots.map((slot) => {
|
|
|
|
|
const teamTime = teamTimers[slot.team.id];
|
2026-03-03 20:14:38 -08:00
|
|
|
const isAutodraft = autodraftStatus[slot.team.id]?.isEnabled || false;
|
2025-10-25 03:23:41 -07:00
|
|
|
const isConnected = connectedTeams.has(slot.team.id);
|
|
|
|
|
|
2026-02-22 16:16:51 -08:00
|
|
|
const headerInner = (
|
2026-02-22 22:36:12 -08:00
|
|
|
<div className="relative">
|
2025-10-25 03:23:41 -07:00
|
|
|
<div
|
Add draft clock UI, Fischer increment timer logic, and security fixes (#19)
- Add prominent clock badge to tab bar (lights up on your turn) with
correct Fischer increment chess-clock model: bank starts at initialTime,
+= incrementTime after each pick, other teams' banks untouched
- Extract pure timer helpers to app/lib/draft-timer.ts and add 29 unit
tests covering formatClockTime, calculateTimeAfterPick, getTimerColorClass,
and full snake-draft lifecycle regression scenarios
- Fix make-pick.ts: add status !== 'draft' and draftPaused server guards
- Fix draft-utils.ts: replace (global as any).__socketIO with getSocketIO(),
fix timeUsed to store actual timeRemaining at pick moment (not always 120),
add null timer warning, move timer fetch before pick insert
- Fix draft.start.ts: batch timer inserts, guard against empty draftSlots
- Fix DraftGridSection: memoize currentTeamId, widen teamTimers type to
Record<string, number | undefined>
- Fix duplicate animate-pulse (getTimerColorClass already includes it)
- Clamp negative seconds in formatClockTime to guard against timer drift
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-21 16:51:12 -08:00
|
|
|
className={`font-semibold text-sm truncate px-2 ${
|
|
|
|
|
slot.team.id === currentTeamId
|
|
|
|
|
? "text-electric font-bold"
|
|
|
|
|
: !isConnected
|
|
|
|
|
? "italic text-muted-foreground"
|
|
|
|
|
: ""
|
|
|
|
|
}`}
|
2025-10-25 03:23:41 -07:00
|
|
|
>
|
2026-02-22 16:56:07 -08:00
|
|
|
{ownerMap[slot.team.id] || slot.team.name}
|
2025-10-25 03:23:41 -07:00
|
|
|
</div>
|
|
|
|
|
<div
|
2026-02-22 22:36:12 -08:00
|
|
|
className={`text-xs font-mono px-2 ${getTimerColorClass(teamTime)}`}
|
2025-10-25 03:23:41 -07:00
|
|
|
>
|
Add draft clock UI, Fischer increment timer logic, and security fixes (#19)
- Add prominent clock badge to tab bar (lights up on your turn) with
correct Fischer increment chess-clock model: bank starts at initialTime,
+= incrementTime after each pick, other teams' banks untouched
- Extract pure timer helpers to app/lib/draft-timer.ts and add 29 unit
tests covering formatClockTime, calculateTimeAfterPick, getTimerColorClass,
and full snake-draft lifecycle regression scenarios
- Fix make-pick.ts: add status !== 'draft' and draftPaused server guards
- Fix draft-utils.ts: replace (global as any).__socketIO with getSocketIO(),
fix timeUsed to store actual timeRemaining at pick moment (not always 120),
add null timer warning, move timer fetch before pick insert
- Fix draft.start.ts: batch timer inserts, guard against empty draftSlots
- Fix DraftGridSection: memoize currentTeamId, widen teamTimers type to
Record<string, number | undefined>
- Fix duplicate animate-pulse (getTimerColorClass already includes it)
- Clamp negative seconds in formatClockTime to guard against timer drift
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-21 16:51:12 -08:00
|
|
|
{formatClockTime(teamTime)}
|
2025-10-25 03:23:41 -07:00
|
|
|
{isAutodraft && (
|
|
|
|
|
<span className="ml-1 text-muted-foreground">
|
|
|
|
|
(auto)
|
|
|
|
|
</span>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
2026-03-03 20:14:38 -08:00
|
|
|
{isCommissioner && (onAdjustTimeBankOpen || onSetAutodraftOpen) && (
|
2026-02-22 22:36:12 -08:00
|
|
|
<button
|
|
|
|
|
className="absolute top-0 right-0 md:hidden p-1 min-w-[32px] min-h-[32px] flex items-center justify-center rounded hover:bg-muted"
|
|
|
|
|
onClick={() => setMobileSheet({ type: "team", teamId: slot.team.id })}
|
|
|
|
|
>
|
|
|
|
|
<MoreVertical className="h-3.5 w-3.5" />
|
|
|
|
|
</button>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
2026-02-22 16:16:51 -08:00
|
|
|
);
|
|
|
|
|
|
2026-03-03 20:14:38 -08:00
|
|
|
if (isCommissioner && (onAdjustTimeBankOpen || onSetAutodraftOpen)) {
|
2026-02-22 16:16:51 -08:00
|
|
|
return (
|
|
|
|
|
<ContextMenu key={slot.id}>
|
|
|
|
|
<ContextMenuTrigger asChild>
|
|
|
|
|
<div className="flex-1 min-w-32 text-center cursor-context-menu">
|
|
|
|
|
{headerInner}
|
|
|
|
|
</div>
|
|
|
|
|
</ContextMenuTrigger>
|
|
|
|
|
<ContextMenuContent>
|
2026-03-03 20:14:38 -08:00
|
|
|
{onAdjustTimeBankOpen && (
|
|
|
|
|
<ContextMenuItem onClick={() => onAdjustTimeBankOpen(slot.team.id)}>
|
|
|
|
|
Adjust Time Bank...
|
|
|
|
|
</ContextMenuItem>
|
|
|
|
|
)}
|
|
|
|
|
{onSetAutodraftOpen && (
|
|
|
|
|
<ContextMenuItem onClick={() => onSetAutodraftOpen(slot.team.id)}>
|
|
|
|
|
Set Autodraft...
|
|
|
|
|
</ContextMenuItem>
|
|
|
|
|
)}
|
2026-02-22 16:16:51 -08:00
|
|
|
</ContextMenuContent>
|
|
|
|
|
</ContextMenu>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div key={slot.id} className="flex-1 min-w-32 text-center">
|
|
|
|
|
{headerInner}
|
2025-10-25 03:23:41 -07:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Draft Grid Rows */}
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
{draftGrid.map((roundPicks, roundIndex) => {
|
|
|
|
|
const round = roundIndex + 1;
|
|
|
|
|
const isEvenRound = round % 2 === 0;
|
|
|
|
|
const displayPicks = isEvenRound
|
2026-03-21 09:44:05 -07:00
|
|
|
? [...roundPicks].toReversed()
|
2025-10-25 03:23:41 -07:00
|
|
|
: roundPicks;
|
|
|
|
|
|
|
|
|
|
return (
|
2026-03-21 09:44:05 -07:00
|
|
|
<div key={round} className="flex gap-2 items-stretch">
|
2026-02-22 22:36:12 -08:00
|
|
|
{/* Round label */}
|
|
|
|
|
<div className="w-8 flex-shrink-0 sticky left-0 z-[5] bg-background/95 flex items-center justify-center">
|
|
|
|
|
<span className="text-xs font-mono text-muted-foreground">R{round}</span>
|
|
|
|
|
</div>
|
2025-10-25 03:23:41 -07:00
|
|
|
{displayPicks.map((cell) => {
|
|
|
|
|
const isCurrent = cell.pickNumber === currentPick;
|
|
|
|
|
const isPicked = !!cell.pick;
|
|
|
|
|
|
2026-02-22 22:36:12 -08:00
|
|
|
const cellClassName = `relative flex-1 min-w-32 h-20 border-2 rounded-lg p-2 transition-all ${
|
2026-02-20 22:47:29 -08:00
|
|
|
isCurrent
|
Add draft clock UI, Fischer increment timer logic, and security fixes (#19)
- Add prominent clock badge to tab bar (lights up on your turn) with
correct Fischer increment chess-clock model: bank starts at initialTime,
+= incrementTime after each pick, other teams' banks untouched
- Extract pure timer helpers to app/lib/draft-timer.ts and add 29 unit
tests covering formatClockTime, calculateTimeAfterPick, getTimerColorClass,
and full snake-draft lifecycle regression scenarios
- Fix make-pick.ts: add status !== 'draft' and draftPaused server guards
- Fix draft-utils.ts: replace (global as any).__socketIO with getSocketIO(),
fix timeUsed to store actual timeRemaining at pick moment (not always 120),
add null timer warning, move timer fetch before pick insert
- Fix draft.start.ts: batch timer inserts, guard against empty draftSlots
- Fix DraftGridSection: memoize currentTeamId, widen teamTimers type to
Record<string, number | undefined>
- Fix duplicate animate-pulse (getTimerColorClass already includes it)
- Clamp negative seconds in formatClockTime to guard against timer drift
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-21 16:51:12 -08:00
|
|
|
? "border-electric bg-electric/20 shadow-lg shadow-electric/25 ring-1 ring-electric/40"
|
2026-02-20 22:47:29 -08:00
|
|
|
: isPicked
|
|
|
|
|
? "bg-emerald-500/10 border-emerald-500/30"
|
|
|
|
|
: "border-border bg-card"
|
|
|
|
|
}`;
|
|
|
|
|
|
|
|
|
|
const cellInner = (
|
|
|
|
|
<>
|
2025-10-25 03:23:41 -07:00
|
|
|
<div className="text-xs font-mono text-muted-foreground mb-1">
|
|
|
|
|
{cell.round}.
|
|
|
|
|
{String(cell.pickInRound).padStart(2, "0")}
|
|
|
|
|
</div>
|
|
|
|
|
{isPicked ? (
|
|
|
|
|
<div className="text-xs">
|
|
|
|
|
<div className="font-semibold truncate">
|
|
|
|
|
{cell.pick?.participant.name}
|
|
|
|
|
</div>
|
|
|
|
|
<div className="text-muted-foreground truncate">
|
|
|
|
|
{cell.pick?.sport.name}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
) : isCurrent ? (
|
Add draft clock UI, Fischer increment timer logic, and security fixes (#19)
- Add prominent clock badge to tab bar (lights up on your turn) with
correct Fischer increment chess-clock model: bank starts at initialTime,
+= incrementTime after each pick, other teams' banks untouched
- Extract pure timer helpers to app/lib/draft-timer.ts and add 29 unit
tests covering formatClockTime, calculateTimeAfterPick, getTimerColorClass,
and full snake-draft lifecycle regression scenarios
- Fix make-pick.ts: add status !== 'draft' and draftPaused server guards
- Fix draft-utils.ts: replace (global as any).__socketIO with getSocketIO(),
fix timeUsed to store actual timeRemaining at pick moment (not always 120),
add null timer warning, move timer fetch before pick insert
- Fix draft.start.ts: batch timer inserts, guard against empty draftSlots
- Fix DraftGridSection: memoize currentTeamId, widen teamTimers type to
Record<string, number | undefined>
- Fix duplicate animate-pulse (getTimerColorClass already includes it)
- Clamp negative seconds in formatClockTime to guard against timer drift
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-21 16:51:12 -08:00
|
|
|
<div className="text-sm font-bold text-electric animate-pulse">
|
2025-10-25 03:23:41 -07:00
|
|
|
On Clock
|
|
|
|
|
</div>
|
|
|
|
|
) : null}
|
2026-02-22 22:36:12 -08:00
|
|
|
{/* Mobile commissioner button */}
|
|
|
|
|
{isCommissioner && !isPicked && isCurrent && (
|
|
|
|
|
<button
|
|
|
|
|
className="absolute top-1 right-1 md:hidden p-1 min-w-[32px] min-h-[32px] flex items-center justify-center rounded hover:bg-muted"
|
|
|
|
|
onClick={() => setMobileSheet({ type: "current-cell", pickNumber: cell.pickNumber, teamId: cell.teamId })}
|
|
|
|
|
>
|
|
|
|
|
<MoreVertical className="h-3.5 w-3.5" />
|
|
|
|
|
</button>
|
|
|
|
|
)}
|
|
|
|
|
{isCommissioner && isPicked && (onReplacePick || onRollbackToPick) && (
|
|
|
|
|
<button
|
|
|
|
|
className="absolute top-1 right-1 md:hidden p-1 min-w-[32px] min-h-[32px] flex items-center justify-center rounded hover:bg-muted"
|
|
|
|
|
onClick={() => setMobileSheet({ type: "picked-cell", pickNumber: cell.pickNumber, teamId: cell.teamId })}
|
|
|
|
|
>
|
|
|
|
|
<MoreVertical className="h-3.5 w-3.5" />
|
|
|
|
|
</button>
|
|
|
|
|
)}
|
2026-02-20 22:47:29 -08:00
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
// Commissioner context menu on the current unpicked cell
|
|
|
|
|
if (isCommissioner && !isPicked && isCurrent) {
|
|
|
|
|
return (
|
|
|
|
|
<ContextMenu key={cell.pickNumber}>
|
|
|
|
|
<ContextMenuTrigger asChild>
|
|
|
|
|
<div
|
|
|
|
|
className={cellClassName}
|
|
|
|
|
title={`Overall Pick #${cell.pickNumber}`}
|
|
|
|
|
>
|
|
|
|
|
{cellInner}
|
|
|
|
|
</div>
|
|
|
|
|
</ContextMenuTrigger>
|
|
|
|
|
<ContextMenuContent>
|
|
|
|
|
<ContextMenuItem
|
|
|
|
|
onClick={() =>
|
|
|
|
|
onForceAutopick(cell.pickNumber, cell.teamId)
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
Force Auto Pick
|
|
|
|
|
</ContextMenuItem>
|
|
|
|
|
<ContextMenuItem
|
|
|
|
|
onClick={() =>
|
|
|
|
|
onForceManualPickOpen(
|
|
|
|
|
cell.pickNumber,
|
|
|
|
|
cell.teamId
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
Force Manual Pick
|
|
|
|
|
</ContextMenuItem>
|
|
|
|
|
</ContextMenuContent>
|
|
|
|
|
</ContextMenu>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Commissioner context menu on already-picked cells
|
|
|
|
|
if (isCommissioner && isPicked && (onReplacePick || onRollbackToPick)) {
|
|
|
|
|
return (
|
|
|
|
|
<ContextMenu key={cell.pickNumber}>
|
|
|
|
|
<ContextMenuTrigger asChild>
|
|
|
|
|
<div
|
|
|
|
|
className={cellClassName}
|
|
|
|
|
title={`Overall Pick #${cell.pickNumber}`}
|
|
|
|
|
>
|
|
|
|
|
{cellInner}
|
|
|
|
|
</div>
|
|
|
|
|
</ContextMenuTrigger>
|
|
|
|
|
<ContextMenuContent>
|
|
|
|
|
{onReplacePick && (
|
|
|
|
|
<ContextMenuItem
|
|
|
|
|
onClick={() =>
|
|
|
|
|
onReplacePick(cell.pickNumber, cell.teamId)
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
Replace Pick
|
|
|
|
|
</ContextMenuItem>
|
|
|
|
|
)}
|
|
|
|
|
{onRollbackToPick && (
|
|
|
|
|
<ContextMenuItem
|
|
|
|
|
onClick={() => onRollbackToPick(cell.pickNumber)}
|
|
|
|
|
className="text-destructive focus:text-destructive"
|
|
|
|
|
>
|
|
|
|
|
Roll Back to This Pick
|
|
|
|
|
</ContextMenuItem>
|
|
|
|
|
)}
|
|
|
|
|
</ContextMenuContent>
|
|
|
|
|
</ContextMenu>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
key={cell.pickNumber}
|
|
|
|
|
className={cellClassName}
|
|
|
|
|
title={`Overall Pick #${cell.pickNumber}`}
|
|
|
|
|
>
|
|
|
|
|
{cellInner}
|
2025-10-25 03:23:41 -07:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-02-22 22:36:12 -08:00
|
|
|
|
|
|
|
|
{/* Mobile Commissioner Sheet */}
|
|
|
|
|
<Sheet open={!!mobileSheet} onOpenChange={(open) => !open && setMobileSheet(null)}>
|
|
|
|
|
<SheetContent side="bottom" className="pb-8">
|
|
|
|
|
<SheetHeader>
|
|
|
|
|
<SheetTitle>Commissioner Actions</SheetTitle>
|
|
|
|
|
</SheetHeader>
|
|
|
|
|
<div className="flex flex-col gap-3 p-4">
|
2026-03-03 20:14:38 -08:00
|
|
|
{mobileSheet?.type === "team" && (
|
|
|
|
|
<>
|
|
|
|
|
{onAdjustTimeBankOpen && (
|
|
|
|
|
<Button
|
|
|
|
|
onClick={() => {
|
|
|
|
|
onAdjustTimeBankOpen(mobileSheet.teamId);
|
|
|
|
|
setMobileSheet(null);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Adjust Time Bank...
|
|
|
|
|
</Button>
|
|
|
|
|
)}
|
|
|
|
|
{onSetAutodraftOpen && (
|
|
|
|
|
<Button
|
|
|
|
|
variant="outline"
|
|
|
|
|
onClick={() => {
|
|
|
|
|
onSetAutodraftOpen(mobileSheet.teamId);
|
|
|
|
|
setMobileSheet(null);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Set Autodraft...
|
|
|
|
|
</Button>
|
|
|
|
|
)}
|
|
|
|
|
</>
|
2026-02-22 22:36:12 -08:00
|
|
|
)}
|
|
|
|
|
{mobileSheet?.type === "current-cell" && (
|
|
|
|
|
<>
|
|
|
|
|
<Button
|
|
|
|
|
onClick={() => {
|
|
|
|
|
onForceAutopick(mobileSheet.pickNumber, mobileSheet.teamId);
|
|
|
|
|
setMobileSheet(null);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Force Auto Pick
|
|
|
|
|
</Button>
|
|
|
|
|
<Button
|
|
|
|
|
variant="outline"
|
|
|
|
|
onClick={() => {
|
|
|
|
|
onForceManualPickOpen(mobileSheet.pickNumber, mobileSheet.teamId);
|
|
|
|
|
setMobileSheet(null);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Force Manual Pick
|
|
|
|
|
</Button>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
{mobileSheet?.type === "picked-cell" && (
|
|
|
|
|
<>
|
|
|
|
|
{onReplacePick && (
|
|
|
|
|
<Button
|
|
|
|
|
onClick={() => {
|
|
|
|
|
onReplacePick(mobileSheet.pickNumber, mobileSheet.teamId);
|
|
|
|
|
setMobileSheet(null);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Replace Pick
|
|
|
|
|
</Button>
|
|
|
|
|
)}
|
|
|
|
|
{onRollbackToPick && (
|
|
|
|
|
<Button
|
|
|
|
|
variant="destructive"
|
|
|
|
|
onClick={() => {
|
|
|
|
|
onRollbackToPick(mobileSheet.pickNumber);
|
|
|
|
|
setMobileSheet(null);
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
Roll Back to This Pick
|
|
|
|
|
</Button>
|
|
|
|
|
)}
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</SheetContent>
|
|
|
|
|
</Sheet>
|
2025-10-25 03:23:41 -07:00
|
|
|
</div>
|
|
|
|
|
);
|
2026-03-02 13:18:47 -08:00
|
|
|
});
|