Skip to content

Commit 7115dda

Browse files
feat: Improve selected columns storage mechanism (#5002)
Co-authored-by: Cursor Agent <[email protected]>
1 parent 9919b99 commit 7115dda

File tree

19 files changed

+945
-114
lines changed

19 files changed

+945
-114
lines changed

keep-ui/app/(keep)/alerts/[id]/ui/alert-table-tab-panel-server-side.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ export default function AlertTableTabPanelServerSide({
9393
setDismissedModalAlert={setDismissModalAlert}
9494
isAsyncLoading={isAsyncLoading}
9595
presetName={preset.name}
96+
presetId={preset.id}
9697
presetTabs={presetTabs}
9798
mutateAlerts={mutateAlerts}
9899
setRunWorkflowModalAlert={setRunWorkflowModalAlert}

keep-ui/app/(keep)/alerts/[id]/ui/alerts.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import {
2626

2727
const defaultPresets: Preset[] = [
2828
{
29-
id: "feed",
29+
id: "11111111-1111-1111-1111-111111111111", // FEED_PRESET_ID
3030
name: "feed",
3131
options: [],
3232
is_private: false,

keep-ui/app/(keep)/providers/page.client.tsx

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,11 @@ export const useFetchProviders = () => {
4141
);
4242

4343
useEffect(() => {
44+
// Check if we're in a browser environment before accessing localStorage
45+
if (typeof window === "undefined" || typeof localStorage === "undefined") {
46+
return;
47+
}
48+
4449
const toastShown = localStorage.getItem(toastShownKey);
4550

4651
if (isLocalhost && !toastShown) {

keep-ui/components/ui/useTimeframeState.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,11 @@ export function useTimeframeState({
9999

100100
useEffect(() => {
101101
return () => {
102+
// Check if we're in a browser environment before accessing window
103+
if (typeof window === "undefined") {
104+
return;
105+
}
106+
102107
const newParams = new URLSearchParams(window.location.search);
103108
deleteTimeframeParams(newParams);
104109
const queryString = newParams.toString();
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
11
export const STATIC_PRESETS_NAMES = ["feed"];
22
export const LOCAL_PRESETS_KEY = "presets-order";
33
export const LOCAL_STATIC_PRESETS_KEY = "static-presets-order";
4+
5+
// Static preset IDs used to identify non-backend presets
6+
export const STATIC_PRESET_IDS = [
7+
"11111111-1111-1111-1111-111111111111", // Feed preset
8+
"22222222-2222-2222-2222-222222222222", // Deleted preset
9+
"33333333-3333-3333-3333-333333333333", // Correlated preset
10+
"44444444-4444-4444-4444-444444444444", // Without correlation preset
11+
];

keep-ui/entities/presets/model/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@ export * from "./constants";
33
export { usePresetActions } from "./usePresetActions";
44
export { usePresetPolling } from "./usePresetPolling";
55
export { usePresets } from "./usePresets";
6+
export { usePresetColumnConfig } from "./usePresetColumnConfig";
7+
export { usePresetColumnState } from "./usePresetColumnState";

keep-ui/entities/presets/model/types.ts

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
11
// TODO: move to entities/alerts/models
22

33
interface Option {
4-
readonly label: string;
5-
readonly value: string;
4+
label: string;
5+
value: string;
66
}
77

88
export interface Tag {
9-
id: string;
9+
id?: string;
1010
name: string;
1111
}
1212

13+
export interface ColumnConfiguration {
14+
column_visibility: Record<string, boolean>;
15+
column_order: string[];
16+
column_rename_mapping: Record<string, string>;
17+
column_time_formats: Record<string, string>;
18+
column_list_formats: Record<string, string>;
19+
}
20+
1321
export interface Preset {
1422
id: string;
1523
name: string;
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import useSWR, { SWRConfiguration } from "swr";
2+
import { useApi } from "@/shared/lib/hooks/useApi";
3+
import { useCallback } from "react";
4+
import { showErrorToast, showSuccessToast } from "@/shared/ui";
5+
import { ColumnConfiguration } from "./types";
6+
import { useRevalidateMultiple } from "@/shared/lib/state-utils";
7+
8+
type UsePresetColumnConfigOptions = {
9+
presetId?: string;
10+
enabled?: boolean; // Flag to control whether to fetch
11+
} & SWRConfiguration;
12+
13+
const DEFAULT_COLUMN_CONFIG: ColumnConfiguration = {
14+
column_visibility: {},
15+
column_order: [],
16+
column_rename_mapping: {},
17+
column_time_formats: {},
18+
column_list_formats: {},
19+
};
20+
21+
export const usePresetColumnConfig = ({
22+
presetId,
23+
enabled = true,
24+
...options
25+
}: UsePresetColumnConfigOptions = {}) => {
26+
const api = useApi();
27+
const revalidateMultiple = useRevalidateMultiple();
28+
29+
const {
30+
data: columnConfig = DEFAULT_COLUMN_CONFIG,
31+
isLoading,
32+
error,
33+
mutate,
34+
} = useSWR<ColumnConfiguration>(
35+
// Only make API call if enabled, API is ready AND presetId is provided
36+
enabled && api?.isReady?.() && presetId
37+
? `/preset/${presetId}/column-config`
38+
: null,
39+
async (url) => {
40+
try {
41+
const result = await api.get(url);
42+
return result || DEFAULT_COLUMN_CONFIG;
43+
} catch (error: any) {
44+
// If the column config endpoint fails (e.g., 404), return default config
45+
// This prevents the page from failing to load
46+
console.warn(
47+
`Failed to fetch column config for preset ${presetId}:`,
48+
error
49+
);
50+
// Don't throw the error, just return default config
51+
return DEFAULT_COLUMN_CONFIG;
52+
}
53+
},
54+
{
55+
fallbackData: DEFAULT_COLUMN_CONFIG,
56+
// Disable error retries to prevent blocking the page
57+
shouldRetryOnError: false,
58+
revalidateOnFocus: false,
59+
// Return default config on error
60+
onError: (error) => {
61+
console.warn("Column config fetch error:", error);
62+
},
63+
...options,
64+
}
65+
);
66+
67+
const updateColumnConfig = useCallback(
68+
async (config: Partial<ColumnConfiguration>) => {
69+
if (!presetId) {
70+
showErrorToast("No preset ID provided");
71+
return;
72+
}
73+
74+
if (!api?.isReady?.()) {
75+
console.warn("API not ready, cannot update column config");
76+
return;
77+
}
78+
79+
try {
80+
const response = await api.put(
81+
`/preset/${presetId}/column-config`,
82+
config
83+
);
84+
showSuccessToast("Column configuration saved!");
85+
mutate();
86+
// Also revalidate preset list to update any cached data
87+
revalidateMultiple(["/preset", "/preset?"]);
88+
return response;
89+
} catch (error) {
90+
showErrorToast(error, "Failed to save column configuration");
91+
throw error;
92+
}
93+
},
94+
[api, presetId, mutate, revalidateMultiple]
95+
);
96+
97+
return {
98+
columnConfig,
99+
isLoading,
100+
error,
101+
updateColumnConfig,
102+
mutate,
103+
};
104+
};
105+
106+
export type UsePresetColumnConfigValue = ReturnType<
107+
typeof usePresetColumnConfig
108+
>;

0 commit comments

Comments
 (0)