Fix custom chess-clock timer detection and save settings blocker

- Extract getInitialDraftSpeed() helper so both useState init and
  resetSettingsFormState use the same logic; unrecognized presets
  now produce a "custom:{bank}:{incr}" string instead of falling
  back to "standard", so the custom section auto-expands correctly
  (e.g. 8 hr bank + 30 min increment is no longer shown as Standard)
- resetSettingsFormState was not resetting draftSpeed at all; fixed
- Clear hasUnsavedSettingsChanges in the Form's onSubmit so the
  useBlocker check is false at the moment the save navigation starts,
  preventing the "Leave without saving?" dialog from firing on save
- Replace the cleared-on-success useEffect with one that re-marks
  dirty when the action returns an error, so the warning reappears
  after a failed save

https://claude.ai/code/session_01DgHNkvJXGizx41CE2tkVS1
This commit is contained in:
Claude 2026-05-15 20:19:12 +00:00
parent 469cc82e15
commit 5e668fc914
No known key found for this signature in database

View file

@ -48,6 +48,19 @@ type LoaderSportsSeason = Route.ComponentProps["loaderData"]["allSportsSeasons"]
fantasySeasonId?: string | null;
};
function getInitialDraftSpeed(season: LoaderSeason): string {
const mode = season?.draftTimerMode ?? "chess_clock";
if (mode === "standard") return season?.draftIncrementTime?.toString() ?? "90";
if (season?.draftInitialTime === 60 && season?.draftIncrementTime === 10) return "fast";
if (season?.draftInitialTime === 120 && season?.draftIncrementTime === 15) return "standard";
if (season?.draftInitialTime === 28800 && season?.draftIncrementTime === 3600) return "slow";
if (season?.draftInitialTime === 43200 && season?.draftIncrementTime === 3600) return "very-slow";
if (season?.draftInitialTime != null && season?.draftIncrementTime != null) {
return `custom:${season.draftInitialTime}:${season.draftIncrementTime}`;
}
return "standard";
}
function getInitialScoringPreset(season: LoaderSeason) {
const rules: ScoringRules = {
pointsFor1st: season?.pointsFor1st ?? DEFAULT_SCORING_RULES.pointsFor1st,
@ -148,15 +161,7 @@ export default function LeagueSettings({ loaderData, actionData }: Route.Compone
pointsFor7th: season?.pointsFor7th ?? DEFAULT_SCORING_RULES.pointsFor7th,
pointsFor8th: season?.pointsFor8th ?? DEFAULT_SCORING_RULES.pointsFor8th,
});
const initTimerMode = season?.draftTimerMode ?? "chess_clock";
const [draftSpeed, setDraftSpeed] = useState(() => {
if (initTimerMode === "standard") return season?.draftIncrementTime?.toString() ?? "90";
if (season?.draftInitialTime === 60 && season?.draftIncrementTime === 10) return "fast";
if (season?.draftInitialTime === 120 && season?.draftIncrementTime === 15) return "standard";
if (season?.draftInitialTime === 28800 && season?.draftIncrementTime === 3600) return "slow";
if (season?.draftInitialTime === 43200 && season?.draftIncrementTime === 3600) return "very-slow";
return "standard";
});
const [draftSpeed, setDraftSpeed] = useState(() => getInitialDraftSpeed(season));
const [selectedSports, setSelectedSports] = useState<Set<string>>(
() => getInitialSelectedSports({ season, allSportsSeasons })
);
@ -184,13 +189,13 @@ export default function LeagueSettings({ loaderData, actionData }: Route.Compone
setHasDraftOrderChanges(false);
}, [draftSlots, teams]);
// Clear settings dirty flag after a successful (non-error) submission
// Re-mark dirty if the save returned an error (success path redirects away, so no cleanup needed there)
useEffect(() => {
if (!hasUnsavedSettingsChanges || navigation.state !== "idle") return;
if (actionData && !("error" in actionData)) {
setHasUnsavedSettingsChanges(false);
if (navigation.state !== "idle") return;
if (actionData && "error" in actionData) {
setHasUnsavedSettingsChanges(true);
}
}, [actionData, hasUnsavedSettingsChanges, navigation.state]);
}, [actionData, navigation.state]);
const canEditSports = season?.status === "pre_draft";
const canEditTeamCount = season?.status === "pre_draft";
@ -235,6 +240,7 @@ export default function LeagueSettings({ loaderData, actionData }: Route.Compone
setDiscordWebhookUrl(league.discordWebhookUrl ?? "");
setTeamCountValue(teamCount);
setTimerMode(season?.draftTimerMode ?? "chess_clock");
setDraftSpeed(getInitialDraftSpeed(season));
setOvernightMode(season?.overnightPauseMode ?? "none");
setOvernightStart(season?.overnightPauseStart ?? "23:00");
setOvernightEnd(season?.overnightPauseEnd ?? "07:00");
@ -345,7 +351,7 @@ export default function LeagueSettings({ loaderData, actionData }: Route.Compone
<main className="space-y-6">
{showUpdateForm && (
<Form method="post" noValidate className="space-y-6">
<Form method="post" noValidate className="space-y-6" onSubmit={() => setHasUnsavedSettingsChanges(false)}>
<input type="hidden" name="intent" value="update" />
<LeagueBasicsSection