From 349bd6439e6d66a2f586dc5867ff88e72332912c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ausi=C3=A0s=20Armesto?= Date: Wed, 27 May 2026 11:22:04 +0200 Subject: [PATCH] fix: prevent custom columns from resetting to defaults when saving preset or renaming column --- .../presets/model/usePresetActions.ts | 11 +++++- .../presets/model/usePresetColumnConfig.ts | 7 +--- .../presets/model/usePresetColumnState.ts | 37 ++++++++++--------- 3 files changed, 30 insertions(+), 25 deletions(-) diff --git a/keep-ui/entities/presets/model/usePresetActions.ts b/keep-ui/entities/presets/model/usePresetActions.ts index 16a65a1f62..6e84f215d6 100644 --- a/keep-ui/entities/presets/model/usePresetActions.ts +++ b/keep-ui/entities/presets/model/usePresetActions.ts @@ -39,7 +39,14 @@ export function usePresetActions() { ); const revalidateMultiple = useRevalidateMultiple(); const mutatePresetsList = useCallback( - () => revalidateMultiple(["/preset", "/preset?"]), + () => { + // Use exact match for /preset (list without filters) and prefix match only + // for /preset? (filtered queries like /preset?filters=...). + // Do NOT use a broad /preset prefix — that would accidentally match + // /preset/{id}/column-config and wipe the user's column configuration. + revalidateMultiple(["/preset"], { isExact: true }); + revalidateMultiple(["/preset?"]); + }, [revalidateMultiple] ); const mutateTags = useCallback( @@ -88,7 +95,7 @@ export function usePresetActions() { return; } try { - const response = await api.delete(`/preset/${presetId}`); + await api.delete(`/preset/${presetId}`); showSuccessToast(`Preset ${presetName} deleted!`); mutatePresetsList(); setLocalDynamicPresets((oldOrder) => diff --git a/keep-ui/entities/presets/model/usePresetColumnConfig.ts b/keep-ui/entities/presets/model/usePresetColumnConfig.ts index 183541bddc..3a9cd08c69 100644 --- a/keep-ui/entities/presets/model/usePresetColumnConfig.ts +++ b/keep-ui/entities/presets/model/usePresetColumnConfig.ts @@ -3,7 +3,6 @@ import { useApi } from "@/shared/lib/hooks/useApi"; import { useCallback } from "react"; import { showErrorToast, showSuccessToast } from "@/shared/ui"; import { ColumnConfiguration } from "./types"; -import { useRevalidateMultiple } from "@/shared/lib/state-utils"; type UsePresetColumnConfigOptions = { presetId?: string; @@ -24,7 +23,6 @@ export const usePresetColumnConfig = ({ ...options }: UsePresetColumnConfigOptions = {}) => { const api = useApi(); - const revalidateMultiple = useRevalidateMultiple(); const { data: columnConfig = DEFAULT_COLUMN_CONFIG, @@ -82,16 +80,15 @@ export const usePresetColumnConfig = ({ config ); showSuccessToast("Column configuration saved!"); + // mutate() already revalidates /preset/${presetId}/column-config mutate(); - // Also revalidate preset list to update any cached data - revalidateMultiple(["/preset", "/preset?"]); return response; } catch (error) { showErrorToast(error, "Failed to save column configuration"); throw error; } }, - [api, presetId, mutate, revalidateMultiple] + [api, presetId, mutate] ); return { diff --git a/keep-ui/entities/presets/model/usePresetColumnState.ts b/keep-ui/entities/presets/model/usePresetColumnState.ts index d80e12b1a4..8184b62984 100644 --- a/keep-ui/entities/presets/model/usePresetColumnState.ts +++ b/keep-ui/entities/presets/model/usePresetColumnState.ts @@ -151,24 +151,19 @@ export const usePresetColumnState = ({ columnListFormats?: Record; }) => { if (shouldUseBackend && !error) { - // Batch all updates into a single API call - const batchedUpdate: Partial = {}; - - if (updates.columnVisibility !== undefined) { - batchedUpdate.column_visibility = updates.columnVisibility; - } - if (updates.columnOrder !== undefined) { - batchedUpdate.column_order = updates.columnOrder; - } - if (updates.columnRenameMapping !== undefined) { - batchedUpdate.column_rename_mapping = updates.columnRenameMapping; - } - if (updates.columnTimeFormats !== undefined) { - batchedUpdate.column_time_formats = updates.columnTimeFormats; - } - if (updates.columnListFormats !== undefined) { - batchedUpdate.column_list_formats = updates.columnListFormats; - } + // Always send the FULL current config to the backend. + // The backend PUT replaces the entire config object, so sending only + // a partial update (e.g. just column_rename_mapping) would wipe out + // all other fields (column_visibility, column_order, etc.) causing + // custom columns to disappear after a rename or format change. + const batchedUpdate: ColumnConfiguration = { + column_visibility: updates.columnVisibility ?? columnVisibility, + column_order: updates.columnOrder ?? columnOrder, + column_rename_mapping: + updates.columnRenameMapping ?? columnRenameMapping, + column_time_formats: updates.columnTimeFormats ?? columnTimeFormats, + column_list_formats: updates.columnListFormats ?? columnListFormats, + }; try { return await updateColumnConfig(batchedUpdate); @@ -209,6 +204,12 @@ export const usePresetColumnState = ({ setLocalColumnTimeFormats, setLocalColumnListFormats, error, + // Current state values needed as baseline for full-config PUT + columnVisibility, + columnOrder, + columnRenameMapping, + columnTimeFormats, + columnListFormats, ] );