Commissioners can now opt in to having a draft start automatically at its scheduled draftDateTime rather than requiring a manual "Start Draft" click. The 1-second timer loop checks for eligible seasons each tick and calls the new shared startDraft() service, which is also used by the existing commissioner HTTP route. - Add autoStartDraft boolean to seasons table (migration 0108) - Extract core start logic into app/services/draft-autostart.ts so both the API route and the timer can call it without AsyncLocalStorage - Add checkAndAutoStartDrafts() to the timer tick; disables autoStartDraft and stops retrying if no draft order is set at fire time - Guard against null draftDateTime in both the timer query (isNotNull) and server actions (autoStartDraft forced false when datetime is null) - Add "Auto-start at scheduled time" checkbox to league creation wizard (step 3) and league settings, gated on date+time being set - Show countdown + "Start Now" button in draft room when auto-start is scheduled; reuses the existing nowForPauseCheck 1-second ticker - Show orange warning banner on league homepage to commissioners when auto-start is within 1 hour and no draft order has been configured Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
70 lines
2 KiB
TypeScript
70 lines
2 KiB
TypeScript
import { Button } from "~/components/ui/button";
|
|
|
|
interface CommissionerDraftControlsProps {
|
|
seasonStatus: string;
|
|
isDraftComplete: boolean;
|
|
isPaused: boolean;
|
|
onStart: () => void;
|
|
onPause: () => void;
|
|
onResume: () => void;
|
|
buttonClassName?: string;
|
|
autoStartDraft?: boolean;
|
|
secondsUntilAutoStart?: number | null;
|
|
}
|
|
|
|
function formatCountdown(seconds: number): string {
|
|
const h = Math.floor(seconds / 3600);
|
|
const m = Math.floor((seconds % 3600) / 60);
|
|
const s = seconds % 60;
|
|
if (h > 0) return `${h}:${String(m).padStart(2, "0")}:${String(s).padStart(2, "0")}`;
|
|
return `${m}:${String(s).padStart(2, "0")}`;
|
|
}
|
|
|
|
export function CommissionerDraftControls({
|
|
seasonStatus,
|
|
isDraftComplete,
|
|
isPaused,
|
|
onStart,
|
|
onPause,
|
|
onResume,
|
|
buttonClassName,
|
|
autoStartDraft,
|
|
secondsUntilAutoStart,
|
|
}: CommissionerDraftControlsProps) {
|
|
if (seasonStatus === "pre_draft") {
|
|
if (autoStartDraft && secondsUntilAutoStart !== null && secondsUntilAutoStart !== undefined && secondsUntilAutoStart > 0) {
|
|
return (
|
|
<div className="flex items-center gap-2">
|
|
<span className="text-sm text-muted-foreground font-mono tabular-nums">
|
|
Auto-starts in {formatCountdown(secondsUntilAutoStart)}
|
|
</span>
|
|
<Button variant="ghost" size="sm" className={buttonClassName} onClick={onStart}>
|
|
Start Now
|
|
</Button>
|
|
</div>
|
|
);
|
|
}
|
|
if (autoStartDraft && secondsUntilAutoStart === 0) {
|
|
return <span className="text-sm text-muted-foreground">Starting…</span>;
|
|
}
|
|
return (
|
|
<Button className={buttonClassName} onClick={onStart}>
|
|
Start Draft
|
|
</Button>
|
|
);
|
|
}
|
|
|
|
if (seasonStatus === "draft" && !isDraftComplete) {
|
|
return isPaused ? (
|
|
<Button className={buttonClassName} onClick={onResume}>
|
|
Resume Draft
|
|
</Button>
|
|
) : (
|
|
<Button className={buttonClassName} variant="outline" onClick={onPause}>
|
|
Pause Draft
|
|
</Button>
|
|
);
|
|
}
|
|
|
|
return null;
|
|
}
|