From 9a7bec31a74233ef619d2c6170f91a9b02a213aa Mon Sep 17 00:00:00 2001 From: sawhil Date: Wed, 14 May 2025 23:50:00 +0530 Subject: [PATCH] feat: preferences framework integrated logs & saved logs views --- .../ExplorerOptions/ExplorerOptions.tsx | 8 - .../container/OptionsMenu/useOptionsMenu.ts | 154 +++++++++++------- frontend/src/pages/LogsExplorer/index.tsx | 45 ++--- .../preferences/configs/logsLoaderConfig.ts | 3 +- .../preferences/configs/logsUpdaterConfig.ts | 48 ++++-- .../context/PreferenceContextProvider.tsx | 10 +- .../preferences/loader/usePreferenceLoader.ts | 51 +++--- .../preferences/sync/usePreferenceSync.ts | 40 ++++- .../src/providers/preferences/types/index.ts | 6 +- .../updater/usePreferenceUpdater.ts | 34 +++- 10 files changed, 254 insertions(+), 145 deletions(-) diff --git a/frontend/src/container/ExplorerOptions/ExplorerOptions.tsx b/frontend/src/container/ExplorerOptions/ExplorerOptions.tsx index ac442789b9..3090babe1d 100644 --- a/frontend/src/container/ExplorerOptions/ExplorerOptions.tsx +++ b/frontend/src/container/ExplorerOptions/ExplorerOptions.tsx @@ -240,7 +240,6 @@ function ExplorerOptions({ dataSource: sourcepage, aggregateOperator: StringOperators.NOOP, }); - console.log('uncaught options in saved views', options); const getUpdatedExtraData = ( extraData: string | undefined, @@ -339,12 +338,6 @@ function ExplorerOptions({ backwardCompatibleOptions = omit(options, 'version'); } - console.log('uncaught backwardCompatibleOptions', { - backwardCompatibleOptions, - esc: extraData?.selectColumns, - osc: options.selectColumns, - }); - if (extraData.selectColumns?.length) { handleOptionsChange({ ...backwardCompatibleOptions, @@ -426,7 +419,6 @@ function ExplorerOptions({ updatePreservedViewInLocalStorage(option); - console.log('uncaught options in saved views before call', options); updateOrRestoreSelectColumns( option.key, viewsData?.data?.data, diff --git a/frontend/src/container/OptionsMenu/useOptionsMenu.ts b/frontend/src/container/OptionsMenu/useOptionsMenu.ts index 93f99348fb..aca8be7686 100644 --- a/frontend/src/container/OptionsMenu/useOptionsMenu.ts +++ b/frontend/src/container/OptionsMenu/useOptionsMenu.ts @@ -1,7 +1,4 @@ -import getFromLocalstorage from 'api/browser/localstorage/get'; -import setToLocalstorage from 'api/browser/localstorage/set'; import { getAggregateKeys } from 'api/queryBuilder/getAttributeKeys'; -import { LOCALSTORAGE } from 'constants/localStorage'; import { LogViewMode } from 'container/LogsTable'; import { useGetAggregateKeys } from 'hooks/queryBuilder/useGetAggregateKeys'; import useDebounce from 'hooks/useDebounce'; @@ -11,6 +8,7 @@ import { AllTraceFilterKeys, AllTraceFilterKeyValue, } from 'pages/TracesExplorer/Filter/filterUtils'; +import { usePreferenceContext } from 'providers/preferences/context/PreferenceContextProvider'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useQueries } from 'react-query'; import { ErrorResponse, SuccessResponse } from 'types/api'; @@ -35,10 +33,10 @@ import { import { getOptionsFromKeys } from './utils'; interface UseOptionsMenuProps { + storageKey?: string; dataSource: DataSource; aggregateOperator: string; initialOptions?: InitialOptions; - storageKey: LOCALSTORAGE; } interface UseOptionsMenu { @@ -48,22 +46,21 @@ interface UseOptionsMenu { } const useOptionsMenu = ({ - storageKey, dataSource, aggregateOperator, initialOptions = {}, }: UseOptionsMenuProps): UseOptionsMenu => { const { notifications } = useNotifications(); + const { + preferences, + updateColumns, + updateFormatting, + } = usePreferenceContext(); const [searchText, setSearchText] = useState(''); const [isFocused, setIsFocused] = useState(false); const debouncedSearchText = useDebounce(searchText, 300); - const localStorageOptionsQuery = useMemo( - () => getFromLocalstorage(storageKey), - [storageKey], - ); - const initialQueryParams = useMemo( () => ({ searchText: '', @@ -77,7 +74,6 @@ const useOptionsMenu = ({ const { query: optionsQuery, - queryData: optionsQueryData, redirectWithQuery: redirectWithOptionsData, } = useUrlQueryData(URL_OPTIONS, defaultOptionsQuery); @@ -142,14 +138,12 @@ const useOptionsMenu = ({ }) .filter(Boolean) as BaseAutocompleteData[]; - // this is the last point where we can set the default columns and if uptil now also we have an empty array then we will set the default columns if (!initialSelected || !initialSelected?.length) { initialSelected = defaultTraceSelectedColumns; } } return initialSelected || []; - // eslint-disable-next-line react-hooks/exhaustive-deps }, [ isFetchedInitialAttributes, initialOptions?.selectColumns, @@ -171,7 +165,6 @@ const useOptionsMenu = ({ const searchedAttributeKeys = useMemo(() => { if (searchedAttributesData?.payload?.attributeKeys?.length) { if (dataSource === DataSource.LOGS) { - // add timestamp and body to the list of attributes return [ ...defaultLogsSelectedColumns, ...searchedAttributesData.payload.attributeKeys.filter( @@ -188,32 +181,31 @@ const useOptionsMenu = ({ return []; }, [dataSource, searchedAttributesData?.payload?.attributeKeys]); - const initialOptionsQuery: OptionsQuery = useMemo( - () => ({ + const initialOptionsQuery: OptionsQuery = useMemo(() => { + const defaultColumns = + dataSource === DataSource.TRACES + ? defaultTraceSelectedColumns + : defaultOptionsQuery.selectColumns; + + return { ...defaultOptionsQuery, ...initialOptions, - // eslint-disable-next-line no-nested-ternary selectColumns: initialOptions?.selectColumns ? initialSelectedColumns - : dataSource === DataSource.TRACES - ? defaultTraceSelectedColumns - : defaultOptionsQuery.selectColumns, - }), - [dataSource, initialOptions, initialSelectedColumns], - ); + : defaultColumns, + }; + }, [dataSource, initialOptions, initialSelectedColumns]); const selectedColumnKeys = useMemo( - () => optionsQueryData?.selectColumns?.map(({ id }) => id) || [], - [optionsQueryData], + () => preferences?.columns?.map(({ id }) => id) || [], + [preferences?.columns], ); const optionsFromAttributeKeys = useMemo(() => { const filteredAttributeKeys = searchedAttributeKeys.filter((item) => { - // For other data sources, only filter out 'body' if it exists if (dataSource !== DataSource.LOGS) { return item.key !== 'body'; } - // For LOGS, keep all keys return true; }); @@ -223,10 +215,8 @@ const useOptionsMenu = ({ const handleRedirectWithOptionsData = useCallback( (newQueryData: OptionsQuery) => { redirectWithOptionsData(newQueryData); - - setToLocalstorage(storageKey, JSON.stringify(newQueryData)); }, - [storageKey, redirectWithOptionsData], + [redirectWithOptionsData], ); const handleSelectColumns = useCallback( @@ -235,7 +225,7 @@ const useOptionsMenu = ({ const newSelectedColumns = newSelectedColumnKeys.reduce((acc, key) => { const column = [ ...searchedAttributeKeys, - ...optionsQueryData.selectColumns, + ...(preferences?.columns || []), ].find(({ id }) => id === key); if (!column) return acc; @@ -243,75 +233,122 @@ const useOptionsMenu = ({ }, [] as BaseAutocompleteData[]); const optionsData: OptionsQuery = { - ...optionsQueryData, + ...defaultOptionsQuery, selectColumns: newSelectedColumns, + format: preferences?.formatting?.format || defaultOptionsQuery.format, + maxLines: preferences?.formatting?.maxLines || defaultOptionsQuery.maxLines, + fontSize: preferences?.formatting?.fontSize || defaultOptionsQuery.fontSize, }; + updateColumns(newSelectedColumns); handleRedirectWithOptionsData(optionsData); }, [ searchedAttributeKeys, selectedColumnKeys, - optionsQueryData, + preferences, handleRedirectWithOptionsData, + updateColumns, ], ); const handleRemoveSelectedColumn = useCallback( (columnKey: string) => { - const newSelectedColumns = optionsQueryData?.selectColumns?.filter( + const newSelectedColumns = preferences?.columns?.filter( ({ id }) => id !== columnKey, ); - if (!newSelectedColumns.length && dataSource !== DataSource.LOGS) { + if (!newSelectedColumns?.length && dataSource !== DataSource.LOGS) { notifications.error({ message: 'There must be at least one selected column', }); } else { const optionsData: OptionsQuery = { - ...optionsQueryData, - selectColumns: newSelectedColumns, + ...defaultOptionsQuery, + selectColumns: newSelectedColumns || [], + format: preferences?.formatting?.format || defaultOptionsQuery.format, + maxLines: + preferences?.formatting?.maxLines || defaultOptionsQuery.maxLines, + fontSize: + preferences?.formatting?.fontSize || defaultOptionsQuery.fontSize, }; - + updateColumns(newSelectedColumns || []); handleRedirectWithOptionsData(optionsData); } }, - [dataSource, notifications, optionsQueryData, handleRedirectWithOptionsData], + [ + dataSource, + notifications, + preferences, + handleRedirectWithOptionsData, + updateColumns, + ], ); const handleFormatChange = useCallback( (value: LogViewMode) => { const optionsData: OptionsQuery = { - ...optionsQueryData, + ...defaultOptionsQuery, + selectColumns: preferences?.columns || [], format: value, + maxLines: preferences?.formatting?.maxLines || defaultOptionsQuery.maxLines, + fontSize: preferences?.formatting?.fontSize || defaultOptionsQuery.fontSize, }; + updateFormatting({ + maxLines: preferences?.formatting?.maxLines || defaultOptionsQuery.maxLines, + format: value === 'list' ? 'table' : value, + fontSize: preferences?.formatting?.fontSize || defaultOptionsQuery.fontSize, + }); handleRedirectWithOptionsData(optionsData); }, - [handleRedirectWithOptionsData, optionsQueryData], + [handleRedirectWithOptionsData, preferences, updateFormatting], ); const handleMaxLinesChange = useCallback( (value: string | number | null) => { const optionsData: OptionsQuery = { - ...optionsQueryData, + ...defaultOptionsQuery, + selectColumns: preferences?.columns || [], + format: preferences?.formatting?.format || defaultOptionsQuery.format, maxLines: value as number, + fontSize: preferences?.formatting?.fontSize || defaultOptionsQuery.fontSize, }; + updateFormatting({ + maxLines: value as number, + format: + preferences?.formatting?.format === 'list' + ? 'table' + : preferences?.formatting?.format || defaultOptionsQuery.format, + fontSize: preferences?.formatting?.fontSize || defaultOptionsQuery.fontSize, + }); handleRedirectWithOptionsData(optionsData); }, - [handleRedirectWithOptionsData, optionsQueryData], + [handleRedirectWithOptionsData, preferences, updateFormatting], ); + const handleFontSizeChange = useCallback( (value: FontSize) => { const optionsData: OptionsQuery = { - ...optionsQueryData, + ...defaultOptionsQuery, + selectColumns: preferences?.columns || [], + format: preferences?.formatting?.format || defaultOptionsQuery.format, + maxLines: preferences?.formatting?.maxLines || defaultOptionsQuery.maxLines, fontSize: value, }; + updateFormatting({ + maxLines: preferences?.formatting?.maxLines || defaultOptionsQuery.maxLines, + format: + preferences?.formatting?.format === 'list' + ? 'table' + : preferences?.formatting?.format || defaultOptionsQuery.format, + fontSize: value, + }); handleRedirectWithOptionsData(optionsData); }, - [handleRedirectWithOptionsData, optionsQueryData], + [handleRedirectWithOptionsData, preferences, updateFormatting], ); const handleSearchAttribute = useCallback((value: string) => { @@ -331,7 +368,7 @@ const useOptionsMenu = ({ () => ({ addColumn: { isFetching: isSearchedAttributesFetching, - value: optionsQueryData?.selectColumns || defaultOptionsQuery.selectColumns, + value: preferences?.columns || defaultOptionsQuery.selectColumns, options: optionsFromAttributeKeys || [], onFocus: handleFocus, onBlur: handleBlur, @@ -340,24 +377,21 @@ const useOptionsMenu = ({ onSearch: handleSearchAttribute, }, format: { - value: optionsQueryData.format || defaultOptionsQuery.format, + value: preferences?.formatting?.format || defaultOptionsQuery.format, onChange: handleFormatChange, }, maxLines: { - value: optionsQueryData.maxLines || defaultOptionsQuery.maxLines, + value: preferences?.formatting?.maxLines || defaultOptionsQuery.maxLines, onChange: handleMaxLinesChange, }, fontSize: { - value: optionsQueryData?.fontSize || defaultOptionsQuery.fontSize, + value: preferences?.formatting?.fontSize || defaultOptionsQuery.fontSize, onChange: handleFontSizeChange, }, }), [ isSearchedAttributesFetching, - optionsQueryData?.selectColumns, - optionsQueryData.format, - optionsQueryData.maxLines, - optionsQueryData?.fontSize, + preferences, optionsFromAttributeKeys, handleSelectColumns, handleRemoveSelectedColumn, @@ -371,21 +405,21 @@ const useOptionsMenu = ({ useEffect(() => { if (optionsQuery || !isFetchedInitialAttributes) return; - const nextOptionsQuery = localStorageOptionsQuery - ? JSON.parse(localStorageOptionsQuery) - : initialOptionsQuery; - - redirectWithOptionsData(nextOptionsQuery); + redirectWithOptionsData(initialOptionsQuery); }, [ isFetchedInitialAttributes, optionsQuery, initialOptionsQuery, - localStorageOptionsQuery, redirectWithOptionsData, ]); return { - options: optionsQueryData, + options: { + selectColumns: preferences?.columns || [], + format: preferences?.formatting?.format || defaultOptionsQuery.format, + maxLines: preferences?.formatting?.maxLines || defaultOptionsQuery.maxLines, + fontSize: preferences?.formatting?.fontSize || defaultOptionsQuery.fontSize, + }, config: optionsMenuConfig, handleOptionsChange: handleRedirectWithOptionsData, }; diff --git a/frontend/src/pages/LogsExplorer/index.tsx b/frontend/src/pages/LogsExplorer/index.tsx index df7bc2cd29..1bc53c5728 100644 --- a/frontend/src/pages/LogsExplorer/index.tsx +++ b/frontend/src/pages/LogsExplorer/index.tsx @@ -36,9 +36,8 @@ function LogsExplorer(): JSX.Element { const [selectedView, setSelectedView] = useState( SELECTED_VIEWS.SEARCH, ); - const { preferences, updateFormatting } = usePreferenceContext(); + const { preferences } = usePreferenceContext(); - console.log('uncaught preferences', preferences); const [showFilters, setShowFilters] = useState(() => { const localStorageValue = getLocalStorageKey( LOCALSTORAGE.SHOW_LOGS_QUICK_FILTERS, @@ -87,7 +86,6 @@ function LogsExplorer(): JSX.Element { }, [currentQuery.builder.queryData, currentQuery.builder.queryData.length]); const { - queryData: optionsQueryData, redirectWithQuery: redirectWithOptionsData, } = useUrlQueryData(URL_OPTIONS, defaultOptionsQuery); @@ -168,12 +166,32 @@ function LogsExplorer(): JSX.Element { ); useEffect(() => { - const migratedQuery = migrateOptionsQuery(optionsQueryData); + const migratedQuery = migrateOptionsQuery( + preferences + ? { + selectColumns: preferences.columns || [], + maxLines: + preferences.formatting?.maxLines || defaultOptionsQuery.maxLines, + format: preferences.formatting?.format || defaultOptionsQuery.format, + fontSize: + preferences.formatting?.fontSize || defaultOptionsQuery.fontSize, + version: preferences.formatting?.version, + } + : defaultOptionsQuery, + ); // Only redirect if the query was actually modified - if (!isEqual(migratedQuery, optionsQueryData)) { + if ( + !isEqual(migratedQuery, { + selectColumns: preferences?.columns || [], + maxLines: preferences?.formatting?.maxLines || defaultOptionsQuery.maxLines, + format: preferences?.formatting?.format || defaultOptionsQuery.format, + fontSize: preferences?.formatting?.fontSize || defaultOptionsQuery.fontSize, + version: preferences?.formatting?.version, + }) + ) { redirectWithOptionsData(migratedQuery); } - }, [migrateOptionsQuery, optionsQueryData, redirectWithOptionsData]); + }, [migrateOptionsQuery, preferences, redirectWithOptionsData]); const isMultipleQueries = useMemo( () => @@ -226,21 +244,6 @@ function LogsExplorer(): JSX.Element { )}
- {/* dummy button to test the updateFormatting function */} -
- -
- {preferences && ( -
-

Preferences

-
{JSON.stringify(preferences, null, 2)}
-
- )} void, + setSavedViewPreferences: Dispatch>, +): { + updateColumns: (newColumns: BaseAutocompleteData[], mode: string) => void; + updateFormatting: (newFormatting: FormattingOptions, mode: string) => void; +} => ({ updateColumns: (newColumns: BaseAutocompleteData[], mode: string): void => { - // Always update URL - const url = new URL(window.location.href); - const options = JSON.parse(url.searchParams.get('options') || '{}'); - options.selectColumns = newColumns; - url.searchParams.set('options', JSON.stringify(options)); - window.history.replaceState({}, '', url.toString()); + if (mode === 'savedView') { + setSavedViewPreferences({ + columns: newColumns, + formatting: { + maxLines: 2, + format: 'table', + fontSize: 'small' as FontSize, + version: 1, + }, + }); + } if (mode === 'direct') { + // redirectWithOptionsData({ + // ...defaultOptionsQuery, + // selectColumns: newColumns, + // }); + // Also update local storage const local = JSON.parse( localStorage.getItem(LOCALSTORAGE.LOGS_LIST_OPTIONS) || '{}', @@ -25,11 +44,10 @@ const logsUpdater = { }, updateFormatting: (newFormatting: FormattingOptions, mode: string): void => { // Always update URL - const url = new URL(window.location.href); - const options = JSON.parse(url.searchParams.get('options') || '{}'); - Object.assign(options, newFormatting); - url.searchParams.set('options', JSON.stringify(options)); - window.history.replaceState({}, '', url.toString()); + redirectWithOptionsData({ + ...defaultOptionsQuery, + ...newFormatting, + }); if (mode === 'direct') { // Also update local storage @@ -40,6 +58,6 @@ const logsUpdater = { setLocalStorageKey(LOCALSTORAGE.LOGS_LIST_OPTIONS, JSON.stringify(local)); } }, -}; +}); -export default logsUpdater; +export default getLogsUpdaterConfig; diff --git a/frontend/src/providers/preferences/context/PreferenceContextProvider.tsx b/frontend/src/providers/preferences/context/PreferenceContextProvider.tsx index 68c1f93dbb..07d8a5456c 100644 --- a/frontend/src/providers/preferences/context/PreferenceContextProvider.tsx +++ b/frontend/src/providers/preferences/context/PreferenceContextProvider.tsx @@ -23,7 +23,15 @@ export function PreferenceContextProvider({ const location = useLocation(); const params = new URLSearchParams(location.search); - const savedViewId = params.get('view'); + let savedViewId = ''; + const viewKeyParam = params.get('viewKey'); + if (viewKeyParam) { + try { + savedViewId = JSON.parse(viewKeyParam); + } catch (e) { + console.error(e); + } + } let dataSource: DataSource = DataSource.LOGS; if (location.pathname.includes('traces')) dataSource = DataSource.TRACES; diff --git a/frontend/src/providers/preferences/loader/usePreferenceLoader.ts b/frontend/src/providers/preferences/loader/usePreferenceLoader.ts index 31b8ccb9ff..9d4a021992 100644 --- a/frontend/src/providers/preferences/loader/usePreferenceLoader.ts +++ b/frontend/src/providers/preferences/loader/usePreferenceLoader.ts @@ -1,12 +1,11 @@ /* eslint-disable sonarjs/cognitive-complexity */ /* eslint-disable no-empty */ -import { - defaultLogsSelectedColumns, - defaultTraceSelectedColumns, -} from 'container/OptionsMenu/constants'; +// import { +// defaultLogsSelectedColumns, +// defaultTraceSelectedColumns, +// } from 'container/OptionsMenu/constants'; import { useGetAllViews } from 'hooks/saveViews/useGetAllViews'; import { useEffect, useState } from 'react'; -import { useLocation } from 'react-router-dom'; import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { DataSource } from 'types/common/queryBuilder'; @@ -71,12 +70,9 @@ export function usePreferenceLoader({ const [preferences, setPreferences] = useState(null); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); - const location = useLocation(); const { data: viewsData } = useGetAllViews(dataSource); - console.log('uncaught viewsData', viewsData); - useEffect((): void => { async function loadPreferences(): Promise { setLoading(true); @@ -87,25 +83,24 @@ export function usePreferenceLoader({ // we can also switch to the URL options params // as we are essentially setting the options in the URL // in ExplorerOptions.tsx#430 (updateOrRestoreSelectColumns) - const extraData = viewsData?.data?.data?.find( - (view) => view.id === savedViewId, - )?.extraData; - - const parsedExtraData = JSON.parse(extraData || '{}'); - let columns: BaseAutocompleteData[] = []; - let formatting: FormattingOptions | undefined; - if (dataSource === DataSource.LOGS) { - columns = parsedExtraData?.selectColumns || defaultLogsSelectedColumns; - formatting = { - maxLines: parsedExtraData?.maxLines ?? 2, - format: parsedExtraData?.format ?? 'table', - fontSize: parsedExtraData?.fontSize ?? 'small', - version: parsedExtraData?.version ?? 1, - }; - } else if (dataSource === DataSource.TRACES) { - columns = parsedExtraData?.selectColumns || defaultTraceSelectedColumns; - } - setPreferences({ columns, formatting }); + // const extraData = viewsData?.data?.data?.find( + // (view) => view.id === savedViewId, + // )?.extraData; + // const parsedExtraData = JSON.parse(extraData || '{}'); + // let columns: BaseAutocompleteData[] = []; + // let formatting: FormattingOptions | undefined; + // if (dataSource === DataSource.LOGS) { + // columns = parsedExtraData?.selectColumns || defaultLogsSelectedColumns; + // formatting = { + // maxLines: parsedExtraData?.maxLines ?? 2, + // format: parsedExtraData?.format ?? 'table', + // fontSize: parsedExtraData?.fontSize ?? 'small', + // version: parsedExtraData?.version ?? 1, + // }; + // } else if (dataSource === DataSource.TRACES) { + // columns = parsedExtraData?.selectColumns || defaultTraceSelectedColumns; + // } + // setPreferences(savedViewPreferences); } else { if (dataSource === DataSource.LOGS) { const { columns, formatting } = await logsPreferencesLoader(); @@ -124,7 +119,7 @@ export function usePreferenceLoader({ } } loadPreferences(); - }, [mode, savedViewId, dataSource, location, reSync, viewsData]); + }, [mode, savedViewId, dataSource, reSync, viewsData]); return { preferences, loading, error }; } diff --git a/frontend/src/providers/preferences/sync/usePreferenceSync.ts b/frontend/src/providers/preferences/sync/usePreferenceSync.ts index de98c522ec..216af220c7 100644 --- a/frontend/src/providers/preferences/sync/usePreferenceSync.ts +++ b/frontend/src/providers/preferences/sync/usePreferenceSync.ts @@ -1,4 +1,6 @@ -import { useState } from 'react'; +import { defaultLogsSelectedColumns } from 'container/OptionsMenu/constants'; +import { useGetAllViews } from 'hooks/saveViews/useGetAllViews'; +import { useEffect, useState } from 'react'; import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { DataSource } from 'types/common/queryBuilder'; @@ -21,6 +23,32 @@ export function usePreferenceSync({ updateColumns: (newColumns: BaseAutocompleteData[]) => void; updateFormatting: (newFormatting: FormattingOptions) => void; } { + const { data: viewsData } = useGetAllViews(dataSource); + const [ + savedViewPreferences, + setSavedViewPreferences, + ] = useState(null); + + useEffect(() => { + const extraData = viewsData?.data?.data?.find( + (view) => view.id === savedViewId, + )?.extraData; + + const parsedExtraData = JSON.parse(extraData || '{}'); + let columns: BaseAutocompleteData[] = []; + let formatting: FormattingOptions | undefined; + if (dataSource === DataSource.LOGS) { + columns = parsedExtraData?.selectColumns || defaultLogsSelectedColumns; + formatting = { + maxLines: parsedExtraData?.maxLines ?? 2, + format: parsedExtraData?.format ?? 'table', + fontSize: parsedExtraData?.fontSize ?? 'small', + version: parsedExtraData?.version ?? 1, + }; + } + setSavedViewPreferences({ columns, formatting }); + }, [viewsData, dataSource, savedViewId, mode]); + // We are using a reSync state because we have URL updates as well as local storage updates // and we want to make sure we are always using the latest preferences const [reSync, setReSync] = useState(0); @@ -35,7 +63,15 @@ export function usePreferenceSync({ dataSource, mode, setReSync, + setSavedViewPreferences, }); - return { preferences, loading, error, updateColumns, updateFormatting }; + return { + preferences: + mode === 'savedView' && savedViewId ? savedViewPreferences : preferences, + loading, + error, + updateColumns, + updateFormatting, + }; } diff --git a/frontend/src/providers/preferences/types/index.ts b/frontend/src/providers/preferences/types/index.ts index c163c711c8..65823c0bfa 100644 --- a/frontend/src/providers/preferences/types/index.ts +++ b/frontend/src/providers/preferences/types/index.ts @@ -1,3 +1,5 @@ +import { LogViewMode } from 'container/LogsTable'; +import { FontSize } from 'container/OptionsMenu/types'; import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { DataSource } from 'types/common/queryBuilder'; @@ -16,8 +18,8 @@ export interface PreferenceContextValue { export interface FormattingOptions { maxLines?: number; - format?: 'raw' | 'table'; - fontSize?: 'small' | 'medium' | 'large'; + format?: LogViewMode; + fontSize?: FontSize; version?: number; } diff --git a/frontend/src/providers/preferences/updater/usePreferenceUpdater.ts b/frontend/src/providers/preferences/updater/usePreferenceUpdater.ts index faf11a4cf1..0e412afeaa 100644 --- a/frontend/src/providers/preferences/updater/usePreferenceUpdater.ts +++ b/frontend/src/providers/preferences/updater/usePreferenceUpdater.ts @@ -1,41 +1,61 @@ +import { + defaultOptionsQuery, + URL_OPTIONS, +} from 'container/OptionsMenu/constants'; +import { OptionsQuery } from 'container/OptionsMenu/types'; +import useUrlQueryData from 'hooks/useUrlQueryData'; import { Dispatch, SetStateAction } from 'react'; import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { DataSource } from 'types/common/queryBuilder'; -import logsUpdater from '../configs/logsUpdaterConfig'; +import getLogsUpdaterConfig from '../configs/logsUpdaterConfig'; import tracesUpdater from '../configs/tracesUpdaterConfig'; -import { FormattingOptions } from '../types'; +import { FormattingOptions, Preferences } from '../types'; const metricsUpdater = { updateColumns: (): void => {}, // no-op for metrics updateFormatting: (): void => {}, // no-op for metrics }; -const updaterConfig: Record< +const getUpdaterConfig = ( + redirectWithOptionsData: (options: OptionsQuery) => void, + setSavedViewPreferences: Dispatch>, +): Record< DataSource, { updateColumns: (newColumns: BaseAutocompleteData[], mode: string) => void; updateFormatting: (newFormatting: FormattingOptions, mode: string) => void; } -> = { - [DataSource.LOGS]: logsUpdater, +> => ({ + [DataSource.LOGS]: getLogsUpdaterConfig( + redirectWithOptionsData, + setSavedViewPreferences, + ), [DataSource.TRACES]: tracesUpdater, [DataSource.METRICS]: metricsUpdater, -}; +}); export function usePreferenceUpdater({ dataSource, mode, setReSync, + setSavedViewPreferences, }: { dataSource: DataSource; mode: string; setReSync: Dispatch>; + setSavedViewPreferences: Dispatch>; }): { updateColumns: (newColumns: BaseAutocompleteData[]) => void; updateFormatting: (newFormatting: FormattingOptions) => void; } { - const updater = updaterConfig[dataSource]; + const { + redirectWithQuery: redirectWithOptionsData, + } = useUrlQueryData(URL_OPTIONS, defaultOptionsQuery); + const updater = getUpdaterConfig( + redirectWithOptionsData, + setSavedViewPreferences, + )[dataSource]; return { updateColumns: (newColumns: BaseAutocompleteData[]): void => {