feat: preferences framework integrated logs & saved logs views

This commit is contained in:
sawhil 2025-05-14 23:50:00 +05:30
parent 5d52731c77
commit 9a7bec31a7
10 changed files with 254 additions and 145 deletions

View File

@ -240,7 +240,6 @@ function ExplorerOptions({
dataSource: sourcepage, dataSource: sourcepage,
aggregateOperator: StringOperators.NOOP, aggregateOperator: StringOperators.NOOP,
}); });
console.log('uncaught options in saved views', options);
const getUpdatedExtraData = ( const getUpdatedExtraData = (
extraData: string | undefined, extraData: string | undefined,
@ -339,12 +338,6 @@ function ExplorerOptions({
backwardCompatibleOptions = omit(options, 'version'); backwardCompatibleOptions = omit(options, 'version');
} }
console.log('uncaught backwardCompatibleOptions', {
backwardCompatibleOptions,
esc: extraData?.selectColumns,
osc: options.selectColumns,
});
if (extraData.selectColumns?.length) { if (extraData.selectColumns?.length) {
handleOptionsChange({ handleOptionsChange({
...backwardCompatibleOptions, ...backwardCompatibleOptions,
@ -426,7 +419,6 @@ function ExplorerOptions({
updatePreservedViewInLocalStorage(option); updatePreservedViewInLocalStorage(option);
console.log('uncaught options in saved views before call', options);
updateOrRestoreSelectColumns( updateOrRestoreSelectColumns(
option.key, option.key,
viewsData?.data?.data, viewsData?.data?.data,

View File

@ -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 { getAggregateKeys } from 'api/queryBuilder/getAttributeKeys';
import { LOCALSTORAGE } from 'constants/localStorage';
import { LogViewMode } from 'container/LogsTable'; import { LogViewMode } from 'container/LogsTable';
import { useGetAggregateKeys } from 'hooks/queryBuilder/useGetAggregateKeys'; import { useGetAggregateKeys } from 'hooks/queryBuilder/useGetAggregateKeys';
import useDebounce from 'hooks/useDebounce'; import useDebounce from 'hooks/useDebounce';
@ -11,6 +8,7 @@ import {
AllTraceFilterKeys, AllTraceFilterKeys,
AllTraceFilterKeyValue, AllTraceFilterKeyValue,
} from 'pages/TracesExplorer/Filter/filterUtils'; } from 'pages/TracesExplorer/Filter/filterUtils';
import { usePreferenceContext } from 'providers/preferences/context/PreferenceContextProvider';
import { useCallback, useEffect, useMemo, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import { useQueries } from 'react-query'; import { useQueries } from 'react-query';
import { ErrorResponse, SuccessResponse } from 'types/api'; import { ErrorResponse, SuccessResponse } from 'types/api';
@ -35,10 +33,10 @@ import {
import { getOptionsFromKeys } from './utils'; import { getOptionsFromKeys } from './utils';
interface UseOptionsMenuProps { interface UseOptionsMenuProps {
storageKey?: string;
dataSource: DataSource; dataSource: DataSource;
aggregateOperator: string; aggregateOperator: string;
initialOptions?: InitialOptions; initialOptions?: InitialOptions;
storageKey: LOCALSTORAGE;
} }
interface UseOptionsMenu { interface UseOptionsMenu {
@ -48,22 +46,21 @@ interface UseOptionsMenu {
} }
const useOptionsMenu = ({ const useOptionsMenu = ({
storageKey,
dataSource, dataSource,
aggregateOperator, aggregateOperator,
initialOptions = {}, initialOptions = {},
}: UseOptionsMenuProps): UseOptionsMenu => { }: UseOptionsMenuProps): UseOptionsMenu => {
const { notifications } = useNotifications(); const { notifications } = useNotifications();
const {
preferences,
updateColumns,
updateFormatting,
} = usePreferenceContext();
const [searchText, setSearchText] = useState<string>(''); const [searchText, setSearchText] = useState<string>('');
const [isFocused, setIsFocused] = useState<boolean>(false); const [isFocused, setIsFocused] = useState<boolean>(false);
const debouncedSearchText = useDebounce(searchText, 300); const debouncedSearchText = useDebounce(searchText, 300);
const localStorageOptionsQuery = useMemo(
() => getFromLocalstorage(storageKey),
[storageKey],
);
const initialQueryParams = useMemo( const initialQueryParams = useMemo(
() => ({ () => ({
searchText: '', searchText: '',
@ -77,7 +74,6 @@ const useOptionsMenu = ({
const { const {
query: optionsQuery, query: optionsQuery,
queryData: optionsQueryData,
redirectWithQuery: redirectWithOptionsData, redirectWithQuery: redirectWithOptionsData,
} = useUrlQueryData<OptionsQuery>(URL_OPTIONS, defaultOptionsQuery); } = useUrlQueryData<OptionsQuery>(URL_OPTIONS, defaultOptionsQuery);
@ -142,14 +138,12 @@ const useOptionsMenu = ({
}) })
.filter(Boolean) as BaseAutocompleteData[]; .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) { if (!initialSelected || !initialSelected?.length) {
initialSelected = defaultTraceSelectedColumns; initialSelected = defaultTraceSelectedColumns;
} }
} }
return initialSelected || []; return initialSelected || [];
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ }, [
isFetchedInitialAttributes, isFetchedInitialAttributes,
initialOptions?.selectColumns, initialOptions?.selectColumns,
@ -171,7 +165,6 @@ const useOptionsMenu = ({
const searchedAttributeKeys = useMemo(() => { const searchedAttributeKeys = useMemo(() => {
if (searchedAttributesData?.payload?.attributeKeys?.length) { if (searchedAttributesData?.payload?.attributeKeys?.length) {
if (dataSource === DataSource.LOGS) { if (dataSource === DataSource.LOGS) {
// add timestamp and body to the list of attributes
return [ return [
...defaultLogsSelectedColumns, ...defaultLogsSelectedColumns,
...searchedAttributesData.payload.attributeKeys.filter( ...searchedAttributesData.payload.attributeKeys.filter(
@ -188,32 +181,31 @@ const useOptionsMenu = ({
return []; return [];
}, [dataSource, searchedAttributesData?.payload?.attributeKeys]); }, [dataSource, searchedAttributesData?.payload?.attributeKeys]);
const initialOptionsQuery: OptionsQuery = useMemo( const initialOptionsQuery: OptionsQuery = useMemo(() => {
() => ({ const defaultColumns =
dataSource === DataSource.TRACES
? defaultTraceSelectedColumns
: defaultOptionsQuery.selectColumns;
return {
...defaultOptionsQuery, ...defaultOptionsQuery,
...initialOptions, ...initialOptions,
// eslint-disable-next-line no-nested-ternary
selectColumns: initialOptions?.selectColumns selectColumns: initialOptions?.selectColumns
? initialSelectedColumns ? initialSelectedColumns
: dataSource === DataSource.TRACES : defaultColumns,
? defaultTraceSelectedColumns };
: defaultOptionsQuery.selectColumns, }, [dataSource, initialOptions, initialSelectedColumns]);
}),
[dataSource, initialOptions, initialSelectedColumns],
);
const selectedColumnKeys = useMemo( const selectedColumnKeys = useMemo(
() => optionsQueryData?.selectColumns?.map(({ id }) => id) || [], () => preferences?.columns?.map(({ id }) => id) || [],
[optionsQueryData], [preferences?.columns],
); );
const optionsFromAttributeKeys = useMemo(() => { const optionsFromAttributeKeys = useMemo(() => {
const filteredAttributeKeys = searchedAttributeKeys.filter((item) => { const filteredAttributeKeys = searchedAttributeKeys.filter((item) => {
// For other data sources, only filter out 'body' if it exists
if (dataSource !== DataSource.LOGS) { if (dataSource !== DataSource.LOGS) {
return item.key !== 'body'; return item.key !== 'body';
} }
// For LOGS, keep all keys
return true; return true;
}); });
@ -223,10 +215,8 @@ const useOptionsMenu = ({
const handleRedirectWithOptionsData = useCallback( const handleRedirectWithOptionsData = useCallback(
(newQueryData: OptionsQuery) => { (newQueryData: OptionsQuery) => {
redirectWithOptionsData(newQueryData); redirectWithOptionsData(newQueryData);
setToLocalstorage(storageKey, JSON.stringify(newQueryData));
}, },
[storageKey, redirectWithOptionsData], [redirectWithOptionsData],
); );
const handleSelectColumns = useCallback( const handleSelectColumns = useCallback(
@ -235,7 +225,7 @@ const useOptionsMenu = ({
const newSelectedColumns = newSelectedColumnKeys.reduce((acc, key) => { const newSelectedColumns = newSelectedColumnKeys.reduce((acc, key) => {
const column = [ const column = [
...searchedAttributeKeys, ...searchedAttributeKeys,
...optionsQueryData.selectColumns, ...(preferences?.columns || []),
].find(({ id }) => id === key); ].find(({ id }) => id === key);
if (!column) return acc; if (!column) return acc;
@ -243,75 +233,122 @@ const useOptionsMenu = ({
}, [] as BaseAutocompleteData[]); }, [] as BaseAutocompleteData[]);
const optionsData: OptionsQuery = { const optionsData: OptionsQuery = {
...optionsQueryData, ...defaultOptionsQuery,
selectColumns: newSelectedColumns, selectColumns: newSelectedColumns,
format: preferences?.formatting?.format || defaultOptionsQuery.format,
maxLines: preferences?.formatting?.maxLines || defaultOptionsQuery.maxLines,
fontSize: preferences?.formatting?.fontSize || defaultOptionsQuery.fontSize,
}; };
updateColumns(newSelectedColumns);
handleRedirectWithOptionsData(optionsData); handleRedirectWithOptionsData(optionsData);
}, },
[ [
searchedAttributeKeys, searchedAttributeKeys,
selectedColumnKeys, selectedColumnKeys,
optionsQueryData, preferences,
handleRedirectWithOptionsData, handleRedirectWithOptionsData,
updateColumns,
], ],
); );
const handleRemoveSelectedColumn = useCallback( const handleRemoveSelectedColumn = useCallback(
(columnKey: string) => { (columnKey: string) => {
const newSelectedColumns = optionsQueryData?.selectColumns?.filter( const newSelectedColumns = preferences?.columns?.filter(
({ id }) => id !== columnKey, ({ id }) => id !== columnKey,
); );
if (!newSelectedColumns.length && dataSource !== DataSource.LOGS) { if (!newSelectedColumns?.length && dataSource !== DataSource.LOGS) {
notifications.error({ notifications.error({
message: 'There must be at least one selected column', message: 'There must be at least one selected column',
}); });
} else { } else {
const optionsData: OptionsQuery = { const optionsData: OptionsQuery = {
...optionsQueryData, ...defaultOptionsQuery,
selectColumns: newSelectedColumns, selectColumns: newSelectedColumns || [],
format: preferences?.formatting?.format || defaultOptionsQuery.format,
maxLines:
preferences?.formatting?.maxLines || defaultOptionsQuery.maxLines,
fontSize:
preferences?.formatting?.fontSize || defaultOptionsQuery.fontSize,
}; };
updateColumns(newSelectedColumns || []);
handleRedirectWithOptionsData(optionsData); handleRedirectWithOptionsData(optionsData);
} }
}, },
[dataSource, notifications, optionsQueryData, handleRedirectWithOptionsData], [
dataSource,
notifications,
preferences,
handleRedirectWithOptionsData,
updateColumns,
],
); );
const handleFormatChange = useCallback( const handleFormatChange = useCallback(
(value: LogViewMode) => { (value: LogViewMode) => {
const optionsData: OptionsQuery = { const optionsData: OptionsQuery = {
...optionsQueryData, ...defaultOptionsQuery,
selectColumns: preferences?.columns || [],
format: value, 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(optionsData);
}, },
[handleRedirectWithOptionsData, optionsQueryData], [handleRedirectWithOptionsData, preferences, updateFormatting],
); );
const handleMaxLinesChange = useCallback( const handleMaxLinesChange = useCallback(
(value: string | number | null) => { (value: string | number | null) => {
const optionsData: OptionsQuery = { const optionsData: OptionsQuery = {
...optionsQueryData, ...defaultOptionsQuery,
selectColumns: preferences?.columns || [],
format: preferences?.formatting?.format || defaultOptionsQuery.format,
maxLines: value as number, 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(optionsData);
}, },
[handleRedirectWithOptionsData, optionsQueryData], [handleRedirectWithOptionsData, preferences, updateFormatting],
); );
const handleFontSizeChange = useCallback( const handleFontSizeChange = useCallback(
(value: FontSize) => { (value: FontSize) => {
const optionsData: OptionsQuery = { const optionsData: OptionsQuery = {
...optionsQueryData, ...defaultOptionsQuery,
selectColumns: preferences?.columns || [],
format: preferences?.formatting?.format || defaultOptionsQuery.format,
maxLines: preferences?.formatting?.maxLines || defaultOptionsQuery.maxLines,
fontSize: value, 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(optionsData);
}, },
[handleRedirectWithOptionsData, optionsQueryData], [handleRedirectWithOptionsData, preferences, updateFormatting],
); );
const handleSearchAttribute = useCallback((value: string) => { const handleSearchAttribute = useCallback((value: string) => {
@ -331,7 +368,7 @@ const useOptionsMenu = ({
() => ({ () => ({
addColumn: { addColumn: {
isFetching: isSearchedAttributesFetching, isFetching: isSearchedAttributesFetching,
value: optionsQueryData?.selectColumns || defaultOptionsQuery.selectColumns, value: preferences?.columns || defaultOptionsQuery.selectColumns,
options: optionsFromAttributeKeys || [], options: optionsFromAttributeKeys || [],
onFocus: handleFocus, onFocus: handleFocus,
onBlur: handleBlur, onBlur: handleBlur,
@ -340,24 +377,21 @@ const useOptionsMenu = ({
onSearch: handleSearchAttribute, onSearch: handleSearchAttribute,
}, },
format: { format: {
value: optionsQueryData.format || defaultOptionsQuery.format, value: preferences?.formatting?.format || defaultOptionsQuery.format,
onChange: handleFormatChange, onChange: handleFormatChange,
}, },
maxLines: { maxLines: {
value: optionsQueryData.maxLines || defaultOptionsQuery.maxLines, value: preferences?.formatting?.maxLines || defaultOptionsQuery.maxLines,
onChange: handleMaxLinesChange, onChange: handleMaxLinesChange,
}, },
fontSize: { fontSize: {
value: optionsQueryData?.fontSize || defaultOptionsQuery.fontSize, value: preferences?.formatting?.fontSize || defaultOptionsQuery.fontSize,
onChange: handleFontSizeChange, onChange: handleFontSizeChange,
}, },
}), }),
[ [
isSearchedAttributesFetching, isSearchedAttributesFetching,
optionsQueryData?.selectColumns, preferences,
optionsQueryData.format,
optionsQueryData.maxLines,
optionsQueryData?.fontSize,
optionsFromAttributeKeys, optionsFromAttributeKeys,
handleSelectColumns, handleSelectColumns,
handleRemoveSelectedColumn, handleRemoveSelectedColumn,
@ -371,21 +405,21 @@ const useOptionsMenu = ({
useEffect(() => { useEffect(() => {
if (optionsQuery || !isFetchedInitialAttributes) return; if (optionsQuery || !isFetchedInitialAttributes) return;
const nextOptionsQuery = localStorageOptionsQuery redirectWithOptionsData(initialOptionsQuery);
? JSON.parse(localStorageOptionsQuery)
: initialOptionsQuery;
redirectWithOptionsData(nextOptionsQuery);
}, [ }, [
isFetchedInitialAttributes, isFetchedInitialAttributes,
optionsQuery, optionsQuery,
initialOptionsQuery, initialOptionsQuery,
localStorageOptionsQuery,
redirectWithOptionsData, redirectWithOptionsData,
]); ]);
return { 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, config: optionsMenuConfig,
handleOptionsChange: handleRedirectWithOptionsData, handleOptionsChange: handleRedirectWithOptionsData,
}; };

View File

@ -36,9 +36,8 @@ function LogsExplorer(): JSX.Element {
const [selectedView, setSelectedView] = useState<SELECTED_VIEWS>( const [selectedView, setSelectedView] = useState<SELECTED_VIEWS>(
SELECTED_VIEWS.SEARCH, SELECTED_VIEWS.SEARCH,
); );
const { preferences, updateFormatting } = usePreferenceContext(); const { preferences } = usePreferenceContext();
console.log('uncaught preferences', preferences);
const [showFilters, setShowFilters] = useState<boolean>(() => { const [showFilters, setShowFilters] = useState<boolean>(() => {
const localStorageValue = getLocalStorageKey( const localStorageValue = getLocalStorageKey(
LOCALSTORAGE.SHOW_LOGS_QUICK_FILTERS, LOCALSTORAGE.SHOW_LOGS_QUICK_FILTERS,
@ -87,7 +86,6 @@ function LogsExplorer(): JSX.Element {
}, [currentQuery.builder.queryData, currentQuery.builder.queryData.length]); }, [currentQuery.builder.queryData, currentQuery.builder.queryData.length]);
const { const {
queryData: optionsQueryData,
redirectWithQuery: redirectWithOptionsData, redirectWithQuery: redirectWithOptionsData,
} = useUrlQueryData<OptionsQuery>(URL_OPTIONS, defaultOptionsQuery); } = useUrlQueryData<OptionsQuery>(URL_OPTIONS, defaultOptionsQuery);
@ -168,12 +166,32 @@ function LogsExplorer(): JSX.Element {
); );
useEffect(() => { 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 // 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); redirectWithOptionsData(migratedQuery);
} }
}, [migrateOptionsQuery, optionsQueryData, redirectWithOptionsData]); }, [migrateOptionsQuery, preferences, redirectWithOptionsData]);
const isMultipleQueries = useMemo( const isMultipleQueries = useMemo(
() => () =>
@ -226,21 +244,6 @@ function LogsExplorer(): JSX.Element {
</section> </section>
)} )}
<section className={cx('log-module-right-section')}> <section className={cx('log-module-right-section')}>
{/* dummy button to test the updateFormatting function */}
<div>
<button
type="button"
onClick={(): void => updateFormatting({ maxLines: 10 })}
>
<h1>Update formatting</h1>
</button>
</div>
{preferences && (
<div>
<h1>Preferences</h1>
<pre>{JSON.stringify(preferences, null, 2)}</pre>
</div>
)}
<Toolbar <Toolbar
showAutoRefresh={false} showAutoRefresh={false}
leftActions={ leftActions={

View File

@ -2,6 +2,7 @@
import getLocalStorageKey from 'api/browser/localstorage/get'; import getLocalStorageKey from 'api/browser/localstorage/get';
import { LOCALSTORAGE } from 'constants/localStorage'; import { LOCALSTORAGE } from 'constants/localStorage';
import { defaultLogsSelectedColumns } from 'container/OptionsMenu/constants'; import { defaultLogsSelectedColumns } from 'container/OptionsMenu/constants';
import { FontSize } from 'container/OptionsMenu/types';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { FormattingOptions } from '../types'; import { FormattingOptions } from '../types';
@ -56,7 +57,7 @@ const logsLoaders = {
formatting: { formatting: {
maxLines: 2, maxLines: 2,
format: 'table', format: 'table',
fontSize: 'small', fontSize: 'small' as FontSize,
version: 1, version: 1,
}, },
}), }),

View File

@ -1,20 +1,39 @@
import setLocalStorageKey from 'api/browser/localstorage/set'; import setLocalStorageKey from 'api/browser/localstorage/set';
import { LOCALSTORAGE } from 'constants/localStorage'; import { LOCALSTORAGE } from 'constants/localStorage';
import { defaultOptionsQuery } from 'container/OptionsMenu/constants';
import { FontSize, OptionsQuery } from 'container/OptionsMenu/types';
import { Dispatch, SetStateAction } from 'react';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { FormattingOptions } from '../types'; import { FormattingOptions, Preferences } from '../types';
// --- LOGS preferences updater config --- // --- LOGS preferences updater config ---
const logsUpdater = { const getLogsUpdaterConfig = (
redirectWithOptionsData: (options: OptionsQuery) => void,
setSavedViewPreferences: Dispatch<SetStateAction<Preferences | null>>,
): {
updateColumns: (newColumns: BaseAutocompleteData[], mode: string) => void;
updateFormatting: (newFormatting: FormattingOptions, mode: string) => void;
} => ({
updateColumns: (newColumns: BaseAutocompleteData[], mode: string): void => { updateColumns: (newColumns: BaseAutocompleteData[], mode: string): void => {
// Always update URL if (mode === 'savedView') {
const url = new URL(window.location.href); setSavedViewPreferences({
const options = JSON.parse(url.searchParams.get('options') || '{}'); columns: newColumns,
options.selectColumns = newColumns; formatting: {
url.searchParams.set('options', JSON.stringify(options)); maxLines: 2,
window.history.replaceState({}, '', url.toString()); format: 'table',
fontSize: 'small' as FontSize,
version: 1,
},
});
}
if (mode === 'direct') { if (mode === 'direct') {
// redirectWithOptionsData({
// ...defaultOptionsQuery,
// selectColumns: newColumns,
// });
// Also update local storage // Also update local storage
const local = JSON.parse( const local = JSON.parse(
localStorage.getItem(LOCALSTORAGE.LOGS_LIST_OPTIONS) || '{}', localStorage.getItem(LOCALSTORAGE.LOGS_LIST_OPTIONS) || '{}',
@ -25,11 +44,10 @@ const logsUpdater = {
}, },
updateFormatting: (newFormatting: FormattingOptions, mode: string): void => { updateFormatting: (newFormatting: FormattingOptions, mode: string): void => {
// Always update URL // Always update URL
const url = new URL(window.location.href); redirectWithOptionsData({
const options = JSON.parse(url.searchParams.get('options') || '{}'); ...defaultOptionsQuery,
Object.assign(options, newFormatting); ...newFormatting,
url.searchParams.set('options', JSON.stringify(options)); });
window.history.replaceState({}, '', url.toString());
if (mode === 'direct') { if (mode === 'direct') {
// Also update local storage // Also update local storage
@ -40,6 +58,6 @@ const logsUpdater = {
setLocalStorageKey(LOCALSTORAGE.LOGS_LIST_OPTIONS, JSON.stringify(local)); setLocalStorageKey(LOCALSTORAGE.LOGS_LIST_OPTIONS, JSON.stringify(local));
} }
}, },
}; });
export default logsUpdater; export default getLogsUpdaterConfig;

View File

@ -23,7 +23,15 @@ export function PreferenceContextProvider({
const location = useLocation(); const location = useLocation();
const params = new URLSearchParams(location.search); 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; let dataSource: DataSource = DataSource.LOGS;
if (location.pathname.includes('traces')) dataSource = DataSource.TRACES; if (location.pathname.includes('traces')) dataSource = DataSource.TRACES;

View File

@ -1,12 +1,11 @@
/* eslint-disable sonarjs/cognitive-complexity */ /* eslint-disable sonarjs/cognitive-complexity */
/* eslint-disable no-empty */ /* eslint-disable no-empty */
import { // import {
defaultLogsSelectedColumns, // defaultLogsSelectedColumns,
defaultTraceSelectedColumns, // defaultTraceSelectedColumns,
} from 'container/OptionsMenu/constants'; // } from 'container/OptionsMenu/constants';
import { useGetAllViews } from 'hooks/saveViews/useGetAllViews'; import { useGetAllViews } from 'hooks/saveViews/useGetAllViews';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { useLocation } from 'react-router-dom';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { DataSource } from 'types/common/queryBuilder'; import { DataSource } from 'types/common/queryBuilder';
@ -71,12 +70,9 @@ export function usePreferenceLoader({
const [preferences, setPreferences] = useState<Preferences | null>(null); const [preferences, setPreferences] = useState<Preferences | null>(null);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null); const [error, setError] = useState<Error | null>(null);
const location = useLocation();
const { data: viewsData } = useGetAllViews(dataSource); const { data: viewsData } = useGetAllViews(dataSource);
console.log('uncaught viewsData', viewsData);
useEffect((): void => { useEffect((): void => {
async function loadPreferences(): Promise<void> { async function loadPreferences(): Promise<void> {
setLoading(true); setLoading(true);
@ -87,25 +83,24 @@ export function usePreferenceLoader({
// we can also switch to the URL options params // we can also switch to the URL options params
// as we are essentially setting the options in the URL // as we are essentially setting the options in the URL
// in ExplorerOptions.tsx#430 (updateOrRestoreSelectColumns) // in ExplorerOptions.tsx#430 (updateOrRestoreSelectColumns)
const extraData = viewsData?.data?.data?.find( // const extraData = viewsData?.data?.data?.find(
(view) => view.id === savedViewId, // (view) => view.id === savedViewId,
)?.extraData; // )?.extraData;
// const parsedExtraData = JSON.parse(extraData || '{}');
const parsedExtraData = JSON.parse(extraData || '{}'); // let columns: BaseAutocompleteData[] = [];
let columns: BaseAutocompleteData[] = []; // let formatting: FormattingOptions | undefined;
let formatting: FormattingOptions | undefined; // if (dataSource === DataSource.LOGS) {
if (dataSource === DataSource.LOGS) { // columns = parsedExtraData?.selectColumns || defaultLogsSelectedColumns;
columns = parsedExtraData?.selectColumns || defaultLogsSelectedColumns; // formatting = {
formatting = { // maxLines: parsedExtraData?.maxLines ?? 2,
maxLines: parsedExtraData?.maxLines ?? 2, // format: parsedExtraData?.format ?? 'table',
format: parsedExtraData?.format ?? 'table', // fontSize: parsedExtraData?.fontSize ?? 'small',
fontSize: parsedExtraData?.fontSize ?? 'small', // version: parsedExtraData?.version ?? 1,
version: parsedExtraData?.version ?? 1, // };
}; // } else if (dataSource === DataSource.TRACES) {
} else if (dataSource === DataSource.TRACES) { // columns = parsedExtraData?.selectColumns || defaultTraceSelectedColumns;
columns = parsedExtraData?.selectColumns || defaultTraceSelectedColumns; // }
} // setPreferences(savedViewPreferences);
setPreferences({ columns, formatting });
} else { } else {
if (dataSource === DataSource.LOGS) { if (dataSource === DataSource.LOGS) {
const { columns, formatting } = await logsPreferencesLoader(); const { columns, formatting } = await logsPreferencesLoader();
@ -124,7 +119,7 @@ export function usePreferenceLoader({
} }
} }
loadPreferences(); loadPreferences();
}, [mode, savedViewId, dataSource, location, reSync, viewsData]); }, [mode, savedViewId, dataSource, reSync, viewsData]);
return { preferences, loading, error }; return { preferences, loading, error };
} }

View File

@ -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 { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { DataSource } from 'types/common/queryBuilder'; import { DataSource } from 'types/common/queryBuilder';
@ -21,6 +23,32 @@ export function usePreferenceSync({
updateColumns: (newColumns: BaseAutocompleteData[]) => void; updateColumns: (newColumns: BaseAutocompleteData[]) => void;
updateFormatting: (newFormatting: FormattingOptions) => void; updateFormatting: (newFormatting: FormattingOptions) => void;
} { } {
const { data: viewsData } = useGetAllViews(dataSource);
const [
savedViewPreferences,
setSavedViewPreferences,
] = useState<Preferences | null>(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 // 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 // and we want to make sure we are always using the latest preferences
const [reSync, setReSync] = useState(0); const [reSync, setReSync] = useState(0);
@ -35,7 +63,15 @@ export function usePreferenceSync({
dataSource, dataSource,
mode, mode,
setReSync, setReSync,
setSavedViewPreferences,
}); });
return { preferences, loading, error, updateColumns, updateFormatting }; return {
preferences:
mode === 'savedView' && savedViewId ? savedViewPreferences : preferences,
loading,
error,
updateColumns,
updateFormatting,
};
} }

View File

@ -1,3 +1,5 @@
import { LogViewMode } from 'container/LogsTable';
import { FontSize } from 'container/OptionsMenu/types';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { DataSource } from 'types/common/queryBuilder'; import { DataSource } from 'types/common/queryBuilder';
@ -16,8 +18,8 @@ export interface PreferenceContextValue {
export interface FormattingOptions { export interface FormattingOptions {
maxLines?: number; maxLines?: number;
format?: 'raw' | 'table'; format?: LogViewMode;
fontSize?: 'small' | 'medium' | 'large'; fontSize?: FontSize;
version?: number; version?: number;
} }

View File

@ -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 { Dispatch, SetStateAction } from 'react';
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
import { DataSource } from 'types/common/queryBuilder'; import { DataSource } from 'types/common/queryBuilder';
import logsUpdater from '../configs/logsUpdaterConfig'; import getLogsUpdaterConfig from '../configs/logsUpdaterConfig';
import tracesUpdater from '../configs/tracesUpdaterConfig'; import tracesUpdater from '../configs/tracesUpdaterConfig';
import { FormattingOptions } from '../types'; import { FormattingOptions, Preferences } from '../types';
const metricsUpdater = { const metricsUpdater = {
updateColumns: (): void => {}, // no-op for metrics updateColumns: (): void => {}, // no-op for metrics
updateFormatting: (): void => {}, // no-op for metrics updateFormatting: (): void => {}, // no-op for metrics
}; };
const updaterConfig: Record< const getUpdaterConfig = (
redirectWithOptionsData: (options: OptionsQuery) => void,
setSavedViewPreferences: Dispatch<SetStateAction<Preferences | null>>,
): Record<
DataSource, DataSource,
{ {
updateColumns: (newColumns: BaseAutocompleteData[], mode: string) => void; updateColumns: (newColumns: BaseAutocompleteData[], mode: string) => void;
updateFormatting: (newFormatting: FormattingOptions, mode: string) => void; updateFormatting: (newFormatting: FormattingOptions, mode: string) => void;
} }
> = { > => ({
[DataSource.LOGS]: logsUpdater, [DataSource.LOGS]: getLogsUpdaterConfig(
redirectWithOptionsData,
setSavedViewPreferences,
),
[DataSource.TRACES]: tracesUpdater, [DataSource.TRACES]: tracesUpdater,
[DataSource.METRICS]: metricsUpdater, [DataSource.METRICS]: metricsUpdater,
}; });
export function usePreferenceUpdater({ export function usePreferenceUpdater({
dataSource, dataSource,
mode, mode,
setReSync, setReSync,
setSavedViewPreferences,
}: { }: {
dataSource: DataSource; dataSource: DataSource;
mode: string; mode: string;
setReSync: Dispatch<SetStateAction<number>>; setReSync: Dispatch<SetStateAction<number>>;
setSavedViewPreferences: Dispatch<SetStateAction<Preferences | null>>;
}): { }): {
updateColumns: (newColumns: BaseAutocompleteData[]) => void; updateColumns: (newColumns: BaseAutocompleteData[]) => void;
updateFormatting: (newFormatting: FormattingOptions) => void; updateFormatting: (newFormatting: FormattingOptions) => void;
} { } {
const updater = updaterConfig[dataSource]; const {
redirectWithQuery: redirectWithOptionsData,
} = useUrlQueryData<OptionsQuery>(URL_OPTIONS, defaultOptionsQuery);
const updater = getUpdaterConfig(
redirectWithOptionsData,
setSavedViewPreferences,
)[dataSource];
return { return {
updateColumns: (newColumns: BaseAutocompleteData[]): void => { updateColumns: (newColumns: BaseAutocompleteData[]): void => {