From 8a7f9cb3709a9cee0295f1e52302f0306a954f8c Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Thu, 23 Apr 2026 00:41:33 +0200 Subject: [PATCH] fix(deploy): compare samplingRate as number in dirty detection MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Drop the Number.isInteger normalization hack in useDeploymentPageState that mapped 1.0 → "1.0" but broke for values like 1.10 (which round-trip to 1.1). Instead, useFormDirty now parseFloats samplingRate on both sides before comparing, so "1", "1.0", and "1.00" all compare equal regardless of how the backend serializes the number. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../hooks/useDeploymentPageState.ts | 2 +- .../AppsTab/AppDeploymentPage/hooks/useFormDirty.ts | 13 +++++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/ui/src/pages/AppsTab/AppDeploymentPage/hooks/useDeploymentPageState.ts b/ui/src/pages/AppsTab/AppDeploymentPage/hooks/useDeploymentPageState.ts index a570de1a..2a4eb6a4 100644 --- a/ui/src/pages/AppsTab/AppDeploymentPage/hooks/useDeploymentPageState.ts +++ b/ui/src/pages/AppsTab/AppDeploymentPage/hooks/useDeploymentPageState.ts @@ -97,7 +97,7 @@ export function useDeploymentPageState( metricsEnabled: agentConfig?.metricsEnabled ?? defaultForm.monitoring.metricsEnabled, metricsInterval: defaultForm.monitoring.metricsInterval, samplingRate: agentConfig?.samplingRate !== undefined - ? (Number.isInteger(agentConfig.samplingRate) ? `${agentConfig.samplingRate}.0` : String(agentConfig.samplingRate)) + ? String(agentConfig.samplingRate) : defaultForm.monitoring.samplingRate, compressSuccess: agentConfig?.compressSuccess ?? defaultForm.monitoring.compressSuccess, replayEnabled: defaultForm.monitoring.replayEnabled, diff --git a/ui/src/pages/AppsTab/AppDeploymentPage/hooks/useFormDirty.ts b/ui/src/pages/AppsTab/AppDeploymentPage/hooks/useFormDirty.ts index 0e8fbb77..ce512fa5 100644 --- a/ui/src/pages/AppsTab/AppDeploymentPage/hooks/useFormDirty.ts +++ b/ui/src/pages/AppsTab/AppDeploymentPage/hooks/useFormDirty.ts @@ -1,5 +1,5 @@ import { useMemo } from 'react'; -import type { DeploymentPageFormState } from './useDeploymentPageState'; +import type { DeploymentPageFormState, MonitoringFormState } from './useDeploymentPageState'; export interface PerTabDirty { monitoring: boolean; @@ -9,13 +9,22 @@ export interface PerTabDirty { anyLocalEdit: boolean; } +// Normalize free-text numeric fields (user types "1.0" / "1" / "1.00" — all equal). +// NaN compares as NaN through JSON, which is harmless since both sides coerce the same. +function normalizeMonitoring(m: MonitoringFormState): Omit & { samplingRate: number } { + const { samplingRate, ...rest } = m; + return { ...rest, samplingRate: parseFloat(samplingRate) }; +} + export function useFormDirty( form: DeploymentPageFormState, serverState: DeploymentPageFormState, stagedJar: File | null, ): PerTabDirty { return useMemo(() => { - const monitoring = JSON.stringify(form.monitoring) !== JSON.stringify(serverState.monitoring); + const monitoring = + JSON.stringify(normalizeMonitoring(form.monitoring)) !== + JSON.stringify(normalizeMonitoring(serverState.monitoring)); const resources = JSON.stringify(form.resources) !== JSON.stringify(serverState.resources); const variables = JSON.stringify(form.variables) !== JSON.stringify(serverState.variables); const sensitiveKeys = JSON.stringify(form.sensitiveKeys) !== JSON.stringify(serverState.sensitiveKeys);