From 3838cd6ee08c16556abdf62d0bbf4c73542a6d9c Mon Sep 17 00:00:00 2001 From: Diego Imbert <70353967+diegoimbert@users.noreply.github.com> Date: Sun, 27 Sep 2026 19:50:02 +0200 Subject: [PATCH] fix: stop the schedule enabled toggle from showing unsaved changes (#11390) * fix: keep schedule enabled toggle from reading as unsaved changes Co-Authored-By: Claude Opus 5.5 (1M context) * fix: only fold the enabled toggle into the baseline when it is deployed Co-Authored-By: Claude Opus 5.5 (1M context) * fix: skip the enabled revert once the drawer moved to another schedule Co-Authored-By: Claude Opus 5.5 (1M context) --------- Co-authored-by: Claude Opus 5.5 (1M context) --- .../schedules/ScheduleEditorInner.svelte | 39 +++++++++++++------ .../triggers/useTriggerDraftSync.svelte.ts | 18 ++++++++- 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/frontend/src/lib/components/triggers/schedules/ScheduleEditorInner.svelte b/frontend/src/lib/components/triggers/schedules/ScheduleEditorInner.svelte index 5a8771195b..7e95de4f40 100644 --- a/frontend/src/lib/components/triggers/schedules/ScheduleEditorInner.svelte +++ b/frontend/src/lib/components/triggers/schedules/ScheduleEditorInner.svelte @@ -737,19 +737,34 @@ async function handleToggleEnabled(nEnabled: boolean) { const previousEnabled = enabled - enabled = nEnabled - if (!trigger?.draftConfig) { - const ok = await withForkConflictRetry( - (force) => - ScheduleService.setScheduleEnabled({ - path: initialPath, - workspace: wsId ?? '', - requestBody: { enabled: nEnabled, force } - }), - 'schedule' - ) + const writesBackend = !trigger?.draftConfig + const togglePath = initialPath + const setEnabled = (v: boolean) => { + // The drawer is reused: a revert landing after it moved to another + // schedule would fold this one's value into that one's baseline. + if (initialPath !== togglePath) return + enabled = v + if (writesBackend) draftSync.patchBaseline({ enabled: v }) + } + setEnabled(nEnabled) + if (writesBackend) { + let ok: boolean + try { + ok = await withForkConflictRetry( + (force) => + ScheduleService.setScheduleEnabled({ + path: initialPath, + workspace: wsId ?? '', + requestBody: { enabled: nEnabled, force } + }), + 'schedule' + ) + } catch (err) { + setEnabled(previousEnabled) + throw err + } if (!ok) { - enabled = previousEnabled + setEnabled(previousEnabled) return } sendUserToast(`${nEnabled ? 'enabled' : 'disabled'} schedule ${initialPath}`) diff --git a/frontend/src/lib/components/triggers/useTriggerDraftSync.svelte.ts b/frontend/src/lib/components/triggers/useTriggerDraftSync.svelte.ts index 98d38ac25f..abe2e287bd 100644 --- a/frontend/src/lib/components/triggers/useTriggerDraftSync.svelte.ts +++ b/frontend/src/lib/components/triggers/useTriggerDraftSync.svelte.ts @@ -88,6 +88,13 @@ export interface TriggerDraftSync { * the form back to the now-stale draft. */ discard(path: string, fallback: Cfg | undefined): void + /** + * Fold fields the editor wrote to the backend on its own (the enabled/mode + * toggle) into the baseline, so they don't read as unsaved changes. Call it + * in the same tick as the form write: a later call leaves the persist-effect + * time to save the toggle as a draft. + */ + patchBaseline(patch: Cfg): void } /** @@ -301,6 +308,15 @@ export function useTriggerDraftSync(opts: TriggerDraftSyncOptions): TriggerDraft openedOnDraft = false await opts.applyCfg(deployedCfg) }, - discard + discard, + patchBaseline(patch: Cfg) { + const deployed = opts.deployed() + if (deployed == null) return + // The deployed object too, so `resetToDeployed` restores the new value; + // `settledBaseline` because it shadows the deployed object and is the + // reactive side (an editor's `deployed` may be a plain `let`). + Object.assign(deployed, patch) + settledBaseline = { ...snapshotCfg(settledBaseline ?? deployed), ...patch } + } } }