diff --git a/ui/src/pages/AppsTab/AppDeploymentPage/index.tsx b/ui/src/pages/AppsTab/AppDeploymentPage/index.tsx index 127a4476..e12db860 100644 --- a/ui/src/pages/AppsTab/AppDeploymentPage/index.tsx +++ b/ui/src/pages/AppsTab/AppDeploymentPage/index.tsx @@ -62,7 +62,7 @@ export default function AppDeploymentPage() { const activeDeployment = deployments.find((d) => d.status === 'STARTING') ?? null; const { data: agentConfig = null } = useApplicationConfig(app?.slug, selectedEnv); - const { data: dirtyState } = useDirtyState(selectedEnv, app?.slug); + const { data: dirtyState, isLoading: dirtyLoading } = useDirtyState(selectedEnv, app?.slug); // Mutations const createApp = useCreateApp(); @@ -113,7 +113,11 @@ export default function AppDeploymentPage() { const dirty = useFormDirty(form, serverState, stagedJar); const { dialogOpen: blockerOpen, confirm: blockerConfirm, cancel: blockerCancel } = useUnsavedChangesBlocker(dirty.anyLocalEdit); - const serverDirtyAgainstDeploy = dirtyState?.dirty ?? true; + // Before the first dirty-state fetch resolves, default to "not dirty" so the + // primary button shows `Save (disabled)` — not a stale `Redeploy`. Once loaded, + // fall back to `true` if the endpoint failed entirely (fail-safe for the + // redeploy path). + const serverDirtyAgainstDeploy = app && dirtyLoading ? false : (dirtyState?.dirty ?? true); const deploymentInProgress = !!activeDeployment; const primaryMode = computeMode({ deploymentInProgress,