diff --git a/client/src/pages/platform/workflow-editor/components/WorkflowCodeEditorSheet.tsx b/client/src/pages/platform/workflow-editor/components/WorkflowCodeEditorSheet.tsx
index c4257204c59..601123f5554 100644
--- a/client/src/pages/platform/workflow-editor/components/WorkflowCodeEditorSheet.tsx
+++ b/client/src/pages/platform/workflow-editor/components/WorkflowCodeEditorSheet.tsx
@@ -159,10 +159,10 @@ const WorkflowCodeEditorSheet = ({
}
onClick={() => handleOpenChange(false)}
- size="iconXs"
+ size="icon"
variant="ghost"
/>
diff --git a/client/src/pages/platform/workflow-editor/components/properties/components/property-input/PropertyInput.test.tsx b/client/src/pages/platform/workflow-editor/components/properties/components/property-input/PropertyInput.test.tsx
index 49674c0f4bf..29986a16b85 100644
--- a/client/src/pages/platform/workflow-editor/components/properties/components/property-input/PropertyInput.test.tsx
+++ b/client/src/pages/platform/workflow-editor/components/properties/components/property-input/PropertyInput.test.tsx
@@ -131,4 +131,20 @@ describe('PropertyInput', async () => {
expect(input).toHaveValue('=someValue');
});
+
+ it('uses minute precision (step=60) for time inputs so the field is easily clearable', () => {
+ const {container} = render();
+
+ const input = container.querySelector('input[type="time"]');
+
+ expect(input).toHaveAttribute('step', '60');
+ });
+
+ it('keeps step=1 for non-time inputs', () => {
+ const {container} = render();
+
+ const input = container.querySelector('input[type="date"]');
+
+ expect(input).toHaveAttribute('step', '1');
+ });
});
diff --git a/client/src/pages/platform/workflow-editor/components/properties/components/property-input/PropertyInput.tsx b/client/src/pages/platform/workflow-editor/components/properties/components/property-input/PropertyInput.tsx
index bc98cef4e97..65ed10aeb23 100644
--- a/client/src/pages/platform/workflow-editor/components/properties/components/property-input/PropertyInput.tsx
+++ b/client/src/pages/platform/workflow-editor/components/properties/components/property-input/PropertyInput.tsx
@@ -168,7 +168,7 @@ const PropertyInput = forwardRef(
placeholder={placeholder}
ref={ref}
required={required}
- step={1}
+ step={type === 'time' ? 60 : 1}
type={type}
value={localValue}
{...props}
diff --git a/client/src/pages/platform/workflow-editor/components/properties/hooks/tests/saveInputValueResolvedValue.test.ts b/client/src/pages/platform/workflow-editor/components/properties/hooks/tests/saveInputValueResolvedValue.test.ts
new file mode 100644
index 00000000000..4366ed80eb4
--- /dev/null
+++ b/client/src/pages/platform/workflow-editor/components/properties/hooks/tests/saveInputValueResolvedValue.test.ts
@@ -0,0 +1,63 @@
+import {describe, expect, it} from 'vitest';
+
+/**
+ * Replicates the resolvedValue branch of saveInputValue in useProperty.ts.
+ *
+ * Why: issue #4768 — clearing a Date/Datetime/Time field previously persisted an
+ * empty string. The backend expects null to represent "unset" for date/time
+ * fields, so the save path coerces empty string to null for those control types
+ * only (other string fields legitimately store "").
+ */
+const resolveSaveValue = ({
+ controlType,
+ isNumericalInput,
+ valueToSave,
+}: {
+ controlType?: string;
+ isNumericalInput: boolean;
+ valueToSave: unknown;
+}): unknown => {
+ const isDateOrTimeControlType = controlType === 'DATE' || controlType === 'DATE_TIME' || controlType === 'TIME';
+
+ if (isNumericalInput) {
+ return parseFloat(valueToSave as string);
+ }
+
+ if (valueToSave === '' && isDateOrTimeControlType) {
+ return null;
+ }
+
+ return valueToSave;
+};
+
+describe('saveInputValue resolved value', () => {
+ it('returns null when a DATE field is cleared', () => {
+ expect(resolveSaveValue({controlType: 'DATE', isNumericalInput: false, valueToSave: ''})).toBeNull();
+ });
+
+ it('returns null when a DATE_TIME field is cleared', () => {
+ expect(resolveSaveValue({controlType: 'DATE_TIME', isNumericalInput: false, valueToSave: ''})).toBeNull();
+ });
+
+ it('returns null when a TIME field is cleared', () => {
+ expect(resolveSaveValue({controlType: 'TIME', isNumericalInput: false, valueToSave: ''})).toBeNull();
+ });
+
+ it('preserves non-empty date values', () => {
+ expect(resolveSaveValue({controlType: 'DATE', isNumericalInput: false, valueToSave: '2026-04-15'})).toBe(
+ '2026-04-15'
+ );
+ });
+
+ it('preserves empty string for regular text fields', () => {
+ expect(resolveSaveValue({controlType: 'TEXT', isNumericalInput: false, valueToSave: ''})).toBe('');
+ });
+
+ it('preserves empty string when controlType is undefined', () => {
+ expect(resolveSaveValue({isNumericalInput: false, valueToSave: ''})).toBe('');
+ });
+
+ it('parses numerical input to float regardless of control type', () => {
+ expect(resolveSaveValue({controlType: 'INTEGER', isNumericalInput: true, valueToSave: '42'})).toBe(42);
+ });
+});
diff --git a/client/src/pages/platform/workflow-editor/components/properties/hooks/useProperty.ts b/client/src/pages/platform/workflow-editor/components/properties/hooks/useProperty.ts
index 2aa09d1cb32..d2ff78dbc27 100644
--- a/client/src/pages/platform/workflow-editor/components/properties/hooks/useProperty.ts
+++ b/client/src/pages/platform/workflow-editor/components/properties/hooks/useProperty.ts
@@ -565,6 +565,18 @@ export const useProperty = ({
isSavingRef.current = true;
+ const isDateOrTimeControlType = controlType === 'DATE' || controlType === 'DATE_TIME' || controlType === 'TIME';
+
+ let resolvedValue: unknown;
+
+ if (isNumericalInput) {
+ resolvedValue = parseFloat(valueToSave as string);
+ } else if (valueToSave === '' && isDateOrTimeControlType) {
+ resolvedValue = null;
+ } else {
+ resolvedValue = valueToSave;
+ }
+
saveProperty({
includeInMetadata: custom,
path,
@@ -572,7 +584,7 @@ export const useProperty = ({
type,
updateClusterElementParameterMutation,
updateWorkflowNodeParameterMutation,
- value: isNumericalInput ? parseFloat(valueToSave as string) : valueToSave,
+ value: resolvedValue,
workflowId: workflow.id!,
});
}, 600);