diff --git a/frontend/src/lib/components/LocalDraftBanner.svelte b/frontend/src/lib/components/LocalDraftBanner.svelte index a379679419..77501ccd6e 100644 --- a/frontend/src/lib/components/LocalDraftBanner.svelte +++ b/frontend/src/lib/components/LocalDraftBanner.svelte @@ -19,6 +19,12 @@ onDiscard: () => void | Promise /** When true (e.g. no write access), hide the Discard action. */ disabled?: boolean + /** + * A command is in flight on the item. What a discard would revert to is whatever that + * command leaves behind, which is not what the user is looking at, so it waits, greyed + * rather than hidden, like Save. + */ + busy?: boolean /** Diff drawer title. */ title?: string /** @@ -35,6 +41,7 @@ getCurrent, onDiscard, disabled = false, + busy = false, title = 'Deployed <> Local changes', reserveSpace }: Props = $props() @@ -85,23 +92,24 @@ const original = $state.snapshot(deployed) as Value const current = $state.snapshot(getCurrent()) as Value diffDrawer?.openDrawer() - // Mirror the inline Discard's `disabled` gate inside the diff drawer — - // otherwise a read-only user could still trigger onDiscard via the - // drawer button even though we hid the banner's inline action. + // Mirror the inline Discard's gates inside the diff drawer — otherwise a read-only user + // could still trigger onDiscard via the drawer button even though we hid the banner's + // inline action. diffDrawer?.setDiff({ mode: 'simple', original, current, title, - button: disabled - ? undefined - : { - text: 'Discard changes', - onClick: async () => { - await onDiscard() - diffDrawer?.closeDrawer() + button: + disabled || busy + ? undefined + : { + text: 'Discard changes', + onClick: async () => { + await onDiscard() + diffDrawer?.closeDrawer() + } } - } }) } @@ -142,6 +150,7 @@ diff --git a/frontend/src/lib/components/ResourceEditor.svelte b/frontend/src/lib/components/ResourceEditor.svelte index e1f9fdd48c..9bfbffad0d 100644 --- a/frontend/src/lib/components/ResourceEditor.svelte +++ b/frontend/src/lib/components/ResourceEditor.svelte @@ -52,6 +52,9 @@ * so it can hide the banner's Discard button in read-only mode (matches * the trigger editors' `disabled={!can_write}` wiring). */ onCanWriteChange?: (canWrite: boolean) => void + /** Notifies the parent drawer that a command is in flight on one of the resource's + * items, so it can grey the banner's Discard the way Save is greyed. */ + onBusyChange?: (busy: boolean) => void } let { @@ -65,7 +68,8 @@ selected: selectedProp = $bindable(), viewJsonSchema = $bindable(), onDraftStateChange, - onCanWriteChange + onCanWriteChange, + onBusyChange }: Props = $props() type ResourceState = { @@ -349,6 +353,9 @@ $effect(() => { onCanWriteChange?.(can_write === true) }) + $effect(() => { + onBusyChange?.(Object.values(items).some((it) => it.busy)) + }) export function localDraftDeployed(): ResourceState | undefined { return selectedItem?.deployed diff --git a/frontend/src/lib/components/ResourceEditorDrawer.svelte b/frontend/src/lib/components/ResourceEditorDrawer.svelte index a269215390..96b36ec10b 100644 --- a/frontend/src/lib/components/ResourceEditorDrawer.svelte +++ b/frontend/src/lib/components/ResourceEditorDrawer.svelte @@ -58,6 +58,7 @@ let session = $state(0) let hasLocalDraft = $state(false) let canWriteSelected = $state(true) + let editorBusy = $state(false) let path: string | undefined = $state(undefined) let selected: string | undefined = $state(undefined) @@ -169,6 +170,7 @@ bind:viewJsonSchema onDraftStateChange={(v) => (hasLocalDraft = v)} onCanWriteChange={(v) => (canWriteSelected = v)} + onBusyChange={(v) => (editorBusy = v)} /> {/key} {/await} @@ -191,6 +193,7 @@ else dispatch('refresh', removedPath) }} disabled={!canWriteSelected} + busy={editorBusy} /> {/snippet} {#snippet actions()} diff --git a/frontend/src/lib/components/VariableEditor.svelte b/frontend/src/lib/components/VariableEditor.svelte index 417558a6a1..02c5fcec64 100644 --- a/frontend/src/lib/components/VariableEditor.svelte +++ b/frontend/src/lib/components/VariableEditor.svelte @@ -354,6 +354,7 @@ if ((await shown?.discard())?.removed && shown !== selectedItem) dispatch('create') }} disabled={!can_write} + busy={anyBusy} /> {/snippet}
diff --git a/frontend/src/lib/components/triggers/schedules/ScheduleEditorInner.svelte b/frontend/src/lib/components/triggers/schedules/ScheduleEditorInner.svelte index 6fa9f0e50e..e81dd554de 100644 --- a/frontend/src/lib/components/triggers/schedules/ScheduleEditorInner.svelte +++ b/frontend/src/lib/components/triggers/schedules/ScheduleEditorInner.svelte @@ -1346,6 +1346,7 @@ if ((await shown?.discard())?.removed && shown !== item.current) onUpdate?.(target) }} disabled={!can_write} + busy={item.busy} /> {/snippet} {@render content()}