mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-19 18:19:09 +08:00
feat: preferences framework generalised scaffolded
This commit is contained in:
parent
c7db85f44c
commit
5d52731c77
@ -240,6 +240,7 @@ 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,
|
||||||
@ -338,6 +339,12 @@ 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,
|
||||||
@ -419,6 +426,7 @@ 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,
|
||||||
|
@ -23,6 +23,7 @@ import { useQueryBuilder } from 'hooks/queryBuilder/useQueryBuilder';
|
|||||||
import useUrlQueryData from 'hooks/useUrlQueryData';
|
import useUrlQueryData from 'hooks/useUrlQueryData';
|
||||||
import { isEqual, isNull } from 'lodash-es';
|
import { isEqual, isNull } from 'lodash-es';
|
||||||
import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback';
|
import ErrorBoundaryFallback from 'pages/ErrorBoundaryFallback/ErrorBoundaryFallback';
|
||||||
|
import { usePreferenceContext } from 'providers/preferences/context/PreferenceContextProvider';
|
||||||
import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
|
import { useCallback, useEffect, useMemo, useRef, 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';
|
||||||
@ -35,6 +36,9 @@ 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();
|
||||||
|
|
||||||
|
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,
|
||||||
@ -222,6 +226,21 @@ 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={
|
||||||
|
@ -4,9 +4,14 @@ import { Compass, TowerControl, Workflow } from 'lucide-react';
|
|||||||
import LogsExplorer from 'pages/LogsExplorer';
|
import LogsExplorer from 'pages/LogsExplorer';
|
||||||
import Pipelines from 'pages/Pipelines';
|
import Pipelines from 'pages/Pipelines';
|
||||||
import SaveView from 'pages/SaveView';
|
import SaveView from 'pages/SaveView';
|
||||||
|
import { PreferenceContextProvider } from 'providers/preferences/context/PreferenceContextProvider';
|
||||||
|
|
||||||
export const logsExplorer: TabRoutes = {
|
export const logsExplorer: TabRoutes = {
|
||||||
Component: LogsExplorer,
|
Component: (): JSX.Element => (
|
||||||
|
<PreferenceContextProvider>
|
||||||
|
<LogsExplorer />
|
||||||
|
</PreferenceContextProvider>
|
||||||
|
),
|
||||||
name: (
|
name: (
|
||||||
<div className="tab-item">
|
<div className="tab-item">
|
||||||
<Compass size={16} /> Explorer
|
<Compass size={16} /> Explorer
|
||||||
|
@ -0,0 +1,66 @@
|
|||||||
|
/* eslint-disable no-empty */
|
||||||
|
import getLocalStorageKey from 'api/browser/localstorage/get';
|
||||||
|
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||||
|
import { defaultLogsSelectedColumns } from 'container/OptionsMenu/constants';
|
||||||
|
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
|
|
||||||
|
import { FormattingOptions } from '../types';
|
||||||
|
|
||||||
|
// --- LOGS preferences loader config ---
|
||||||
|
const logsLoaders = {
|
||||||
|
local: async (): Promise<{
|
||||||
|
columns: BaseAutocompleteData[];
|
||||||
|
formatting: FormattingOptions;
|
||||||
|
}> => {
|
||||||
|
const local = getLocalStorageKey(LOCALSTORAGE.LOGS_LIST_OPTIONS);
|
||||||
|
if (local) {
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(local);
|
||||||
|
return {
|
||||||
|
columns: parsed.selectColumns || [],
|
||||||
|
formatting: {
|
||||||
|
maxLines: parsed.maxLines ?? 2,
|
||||||
|
format: parsed.format ?? 'table',
|
||||||
|
fontSize: parsed.fontSize ?? 'small',
|
||||||
|
version: parsed.version ?? 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
return { columns: [], formatting: undefined } as any;
|
||||||
|
},
|
||||||
|
url: async (): Promise<{
|
||||||
|
columns: BaseAutocompleteData[];
|
||||||
|
formatting: FormattingOptions;
|
||||||
|
}> => {
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
try {
|
||||||
|
const options = JSON.parse(urlParams.get('options') || '{}');
|
||||||
|
return {
|
||||||
|
columns: options.selectColumns || [],
|
||||||
|
formatting: {
|
||||||
|
maxLines: options.maxLines ?? 2,
|
||||||
|
format: options.format ?? 'table',
|
||||||
|
fontSize: options.fontSize ?? 'small',
|
||||||
|
version: options.version ?? 1,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
} catch {}
|
||||||
|
return { columns: [], formatting: undefined } as any;
|
||||||
|
},
|
||||||
|
default: async (): Promise<{
|
||||||
|
columns: BaseAutocompleteData[];
|
||||||
|
formatting: FormattingOptions;
|
||||||
|
}> => ({
|
||||||
|
columns: defaultLogsSelectedColumns as BaseAutocompleteData[],
|
||||||
|
formatting: {
|
||||||
|
maxLines: 2,
|
||||||
|
format: 'table',
|
||||||
|
fontSize: 'small',
|
||||||
|
version: 1,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
priority: ['local', 'url', 'default'] as const,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default logsLoaders;
|
@ -0,0 +1,45 @@
|
|||||||
|
import setLocalStorageKey from 'api/browser/localstorage/set';
|
||||||
|
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||||
|
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
|
|
||||||
|
import { FormattingOptions } from '../types';
|
||||||
|
|
||||||
|
// --- LOGS preferences updater config ---
|
||||||
|
const logsUpdater = {
|
||||||
|
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 === 'direct') {
|
||||||
|
// Also update local storage
|
||||||
|
const local = JSON.parse(
|
||||||
|
localStorage.getItem(LOCALSTORAGE.LOGS_LIST_OPTIONS) || '{}',
|
||||||
|
);
|
||||||
|
local.selectColumns = newColumns;
|
||||||
|
setLocalStorageKey(LOCALSTORAGE.LOGS_LIST_OPTIONS, JSON.stringify(local));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
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());
|
||||||
|
|
||||||
|
if (mode === 'direct') {
|
||||||
|
// Also update local storage
|
||||||
|
const local = JSON.parse(
|
||||||
|
localStorage.getItem(LOCALSTORAGE.LOGS_LIST_OPTIONS) || '{}',
|
||||||
|
);
|
||||||
|
Object.assign(local, newFormatting);
|
||||||
|
setLocalStorageKey(LOCALSTORAGE.LOGS_LIST_OPTIONS, JSON.stringify(local));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default logsUpdater;
|
@ -0,0 +1,43 @@
|
|||||||
|
/* eslint-disable no-empty */
|
||||||
|
import getLocalStorageKey from 'api/browser/localstorage/get';
|
||||||
|
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||||
|
import { defaultTraceSelectedColumns } from 'container/OptionsMenu/constants';
|
||||||
|
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
|
|
||||||
|
// --- TRACES preferences loader config ---
|
||||||
|
const tracesLoaders = {
|
||||||
|
local: async (): Promise<{
|
||||||
|
columns: BaseAutocompleteData[];
|
||||||
|
}> => {
|
||||||
|
const local = getLocalStorageKey(LOCALSTORAGE.TRACES_LIST_OPTIONS);
|
||||||
|
if (local) {
|
||||||
|
try {
|
||||||
|
const parsed = JSON.parse(local);
|
||||||
|
return {
|
||||||
|
columns: parsed.selectColumns || [],
|
||||||
|
};
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
return { columns: [] };
|
||||||
|
},
|
||||||
|
url: async (): Promise<{
|
||||||
|
columns: BaseAutocompleteData[];
|
||||||
|
}> => {
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
try {
|
||||||
|
const options = JSON.parse(urlParams.get('options') || '{}');
|
||||||
|
return {
|
||||||
|
columns: options.selectColumns || [],
|
||||||
|
};
|
||||||
|
} catch {}
|
||||||
|
return { columns: [] };
|
||||||
|
},
|
||||||
|
default: async (): Promise<{
|
||||||
|
columns: BaseAutocompleteData[];
|
||||||
|
}> => ({
|
||||||
|
columns: defaultTraceSelectedColumns as BaseAutocompleteData[],
|
||||||
|
}),
|
||||||
|
priority: ['local', 'url', 'default'] as const,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default tracesLoaders;
|
@ -0,0 +1,25 @@
|
|||||||
|
import setLocalStorageKey from 'api/browser/localstorage/set';
|
||||||
|
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||||
|
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
|
|
||||||
|
// --- TRACES preferences updater config ---
|
||||||
|
const tracesUpdater = {
|
||||||
|
updateColumns: (newColumns: BaseAutocompleteData[], mode: string): void => {
|
||||||
|
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 === 'direct') {
|
||||||
|
const local = JSON.parse(
|
||||||
|
localStorage.getItem(LOCALSTORAGE.TRACES_LIST_OPTIONS) || '{}',
|
||||||
|
);
|
||||||
|
local.selectColumns = newColumns;
|
||||||
|
setLocalStorageKey(LOCALSTORAGE.TRACES_LIST_OPTIONS, JSON.stringify(local));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
updateFormatting: (): void => {}, // no-op for traces
|
||||||
|
};
|
||||||
|
|
||||||
|
export default tracesUpdater;
|
@ -0,0 +1,78 @@
|
|||||||
|
import { PreferenceContextValue } from 'providers/preferences/types';
|
||||||
|
import React, { createContext, useContext, useMemo } from 'react';
|
||||||
|
import { useLocation } from 'react-router-dom';
|
||||||
|
import { DataSource } from 'types/common/queryBuilder';
|
||||||
|
|
||||||
|
import { usePreferenceSync } from '../sync/usePreferenceSync';
|
||||||
|
|
||||||
|
// This will help in identifying the mode of the preference context
|
||||||
|
// savedView - when the preference is loaded from a saved view
|
||||||
|
// direct - when the preference is loaded from a direct query
|
||||||
|
|
||||||
|
export type PreferenceMode = 'savedView' | 'direct';
|
||||||
|
|
||||||
|
const PreferenceContext = createContext<PreferenceContextValue | undefined>(
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
|
|
||||||
|
export function PreferenceContextProvider({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode;
|
||||||
|
}): JSX.Element {
|
||||||
|
const location = useLocation();
|
||||||
|
const params = new URLSearchParams(location.search);
|
||||||
|
|
||||||
|
const savedViewId = params.get('view');
|
||||||
|
let dataSource: DataSource = DataSource.LOGS;
|
||||||
|
if (location.pathname.includes('traces')) dataSource = DataSource.TRACES;
|
||||||
|
|
||||||
|
const {
|
||||||
|
preferences,
|
||||||
|
loading,
|
||||||
|
error,
|
||||||
|
updateColumns,
|
||||||
|
updateFormatting,
|
||||||
|
} = usePreferenceSync({
|
||||||
|
mode: savedViewId ? 'savedView' : 'direct',
|
||||||
|
savedViewId: savedViewId || undefined,
|
||||||
|
dataSource,
|
||||||
|
});
|
||||||
|
|
||||||
|
const value = useMemo<PreferenceContextValue>(
|
||||||
|
() => ({
|
||||||
|
preferences,
|
||||||
|
loading,
|
||||||
|
error,
|
||||||
|
mode: savedViewId ? 'savedView' : 'direct',
|
||||||
|
savedViewId: savedViewId || undefined,
|
||||||
|
dataSource,
|
||||||
|
updateColumns,
|
||||||
|
updateFormatting,
|
||||||
|
}),
|
||||||
|
[
|
||||||
|
savedViewId,
|
||||||
|
dataSource,
|
||||||
|
preferences,
|
||||||
|
loading,
|
||||||
|
error,
|
||||||
|
updateColumns,
|
||||||
|
updateFormatting,
|
||||||
|
],
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PreferenceContext.Provider value={value}>
|
||||||
|
{children}
|
||||||
|
</PreferenceContext.Provider>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function usePreferenceContext(): PreferenceContextValue {
|
||||||
|
const ctx = useContext(PreferenceContext);
|
||||||
|
if (!ctx)
|
||||||
|
throw new Error(
|
||||||
|
'usePreferenceContext must be used within PreferenceContextProvider',
|
||||||
|
);
|
||||||
|
return ctx;
|
||||||
|
}
|
130
frontend/src/providers/preferences/loader/usePreferenceLoader.ts
Normal file
130
frontend/src/providers/preferences/loader/usePreferenceLoader.ts
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
/* eslint-disable sonarjs/cognitive-complexity */
|
||||||
|
/* eslint-disable no-empty */
|
||||||
|
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';
|
||||||
|
|
||||||
|
import logsLoaderConfig from '../configs/logsLoaderConfig';
|
||||||
|
import tracesLoaderConfig from '../configs/tracesLoaderConfig';
|
||||||
|
import { FormattingOptions, PreferenceMode, Preferences } from '../types';
|
||||||
|
|
||||||
|
// Generic preferences loader that works with any config
|
||||||
|
async function preferencesLoader<T>(config: {
|
||||||
|
priority: readonly string[];
|
||||||
|
[key: string]: any;
|
||||||
|
}): Promise<T> {
|
||||||
|
const findValidLoader = async (): Promise<T> => {
|
||||||
|
// Try each loader in priority order
|
||||||
|
const results = await Promise.all(
|
||||||
|
config.priority.map(async (source) => ({
|
||||||
|
source,
|
||||||
|
result: await config[source](),
|
||||||
|
})),
|
||||||
|
);
|
||||||
|
// Find the first result with columns
|
||||||
|
const validResult = results.find(({ result }) => result.columns.length);
|
||||||
|
if (validResult) {
|
||||||
|
return validResult.result;
|
||||||
|
}
|
||||||
|
// fallback to default
|
||||||
|
return config.default();
|
||||||
|
};
|
||||||
|
|
||||||
|
return findValidLoader();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the generic loader with specific configs
|
||||||
|
async function logsPreferencesLoader(): Promise<{
|
||||||
|
columns: BaseAutocompleteData[];
|
||||||
|
formatting: FormattingOptions;
|
||||||
|
}> {
|
||||||
|
return preferencesLoader(logsLoaderConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
async function tracesPreferencesLoader(): Promise<{
|
||||||
|
columns: BaseAutocompleteData[];
|
||||||
|
}> {
|
||||||
|
return preferencesLoader(tracesLoaderConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function usePreferenceLoader({
|
||||||
|
mode,
|
||||||
|
savedViewId,
|
||||||
|
dataSource,
|
||||||
|
reSync,
|
||||||
|
}: {
|
||||||
|
mode: PreferenceMode;
|
||||||
|
savedViewId: string;
|
||||||
|
dataSource: DataSource;
|
||||||
|
reSync: number;
|
||||||
|
}): {
|
||||||
|
preferences: Preferences | null;
|
||||||
|
loading: boolean;
|
||||||
|
error: Error | null;
|
||||||
|
} {
|
||||||
|
const [preferences, setPreferences] = useState<Preferences | null>(null);
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
const [error, setError] = useState<Error | null>(null);
|
||||||
|
const location = useLocation();
|
||||||
|
|
||||||
|
const { data: viewsData } = useGetAllViews(dataSource);
|
||||||
|
|
||||||
|
console.log('uncaught viewsData', viewsData);
|
||||||
|
|
||||||
|
useEffect((): void => {
|
||||||
|
async function loadPreferences(): Promise<void> {
|
||||||
|
setLoading(true);
|
||||||
|
setError(null);
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (mode === 'savedView' && savedViewId) {
|
||||||
|
// 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 });
|
||||||
|
} else {
|
||||||
|
if (dataSource === DataSource.LOGS) {
|
||||||
|
const { columns, formatting } = await logsPreferencesLoader();
|
||||||
|
setPreferences({ columns, formatting });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dataSource === DataSource.TRACES) {
|
||||||
|
const { columns } = await tracesPreferencesLoader();
|
||||||
|
setPreferences({ columns });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
setError(e as Error);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
loadPreferences();
|
||||||
|
}, [mode, savedViewId, dataSource, location, reSync, viewsData]);
|
||||||
|
|
||||||
|
return { preferences, loading, error };
|
||||||
|
}
|
41
frontend/src/providers/preferences/sync/usePreferenceSync.ts
Normal file
41
frontend/src/providers/preferences/sync/usePreferenceSync.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { useState } from 'react';
|
||||||
|
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
|
import { DataSource } from 'types/common/queryBuilder';
|
||||||
|
|
||||||
|
import { usePreferenceLoader } from '../loader/usePreferenceLoader';
|
||||||
|
import { FormattingOptions, PreferenceMode, Preferences } from '../types';
|
||||||
|
import { usePreferenceUpdater } from '../updater/usePreferenceUpdater';
|
||||||
|
|
||||||
|
export function usePreferenceSync({
|
||||||
|
mode,
|
||||||
|
dataSource,
|
||||||
|
savedViewId,
|
||||||
|
}: {
|
||||||
|
mode: PreferenceMode;
|
||||||
|
dataSource: DataSource;
|
||||||
|
savedViewId: string | undefined;
|
||||||
|
}): {
|
||||||
|
preferences: Preferences | null;
|
||||||
|
loading: boolean;
|
||||||
|
error: Error | null;
|
||||||
|
updateColumns: (newColumns: BaseAutocompleteData[]) => void;
|
||||||
|
updateFormatting: (newFormatting: FormattingOptions) => void;
|
||||||
|
} {
|
||||||
|
// 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);
|
||||||
|
const { preferences, loading, error } = usePreferenceLoader({
|
||||||
|
mode,
|
||||||
|
savedViewId: savedViewId || '',
|
||||||
|
dataSource,
|
||||||
|
reSync,
|
||||||
|
});
|
||||||
|
|
||||||
|
const { updateColumns, updateFormatting } = usePreferenceUpdater({
|
||||||
|
dataSource,
|
||||||
|
mode,
|
||||||
|
setReSync,
|
||||||
|
});
|
||||||
|
|
||||||
|
return { preferences, loading, error, updateColumns, updateFormatting };
|
||||||
|
}
|
27
frontend/src/providers/preferences/types/index.ts
Normal file
27
frontend/src/providers/preferences/types/index.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
|
import { DataSource } from 'types/common/queryBuilder';
|
||||||
|
|
||||||
|
export type PreferenceMode = 'savedView' | 'direct';
|
||||||
|
|
||||||
|
export interface PreferenceContextValue {
|
||||||
|
preferences: Preferences | null;
|
||||||
|
loading: boolean;
|
||||||
|
error: Error | null;
|
||||||
|
mode: PreferenceMode;
|
||||||
|
savedViewId?: string;
|
||||||
|
dataSource: DataSource;
|
||||||
|
updateColumns: (newColumns: BaseAutocompleteData[]) => void;
|
||||||
|
updateFormatting: (newFormatting: FormattingOptions) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface FormattingOptions {
|
||||||
|
maxLines?: number;
|
||||||
|
format?: 'raw' | 'table';
|
||||||
|
fontSize?: 'small' | 'medium' | 'large';
|
||||||
|
version?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface Preferences {
|
||||||
|
columns: BaseAutocompleteData[];
|
||||||
|
formatting?: FormattingOptions;
|
||||||
|
}
|
@ -0,0 +1,50 @@
|
|||||||
|
import { Dispatch, SetStateAction } from 'react';
|
||||||
|
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||||
|
import { DataSource } from 'types/common/queryBuilder';
|
||||||
|
|
||||||
|
import logsUpdater from '../configs/logsUpdaterConfig';
|
||||||
|
import tracesUpdater from '../configs/tracesUpdaterConfig';
|
||||||
|
import { FormattingOptions } from '../types';
|
||||||
|
|
||||||
|
const metricsUpdater = {
|
||||||
|
updateColumns: (): void => {}, // no-op for metrics
|
||||||
|
updateFormatting: (): void => {}, // no-op for metrics
|
||||||
|
};
|
||||||
|
|
||||||
|
const updaterConfig: Record<
|
||||||
|
DataSource,
|
||||||
|
{
|
||||||
|
updateColumns: (newColumns: BaseAutocompleteData[], mode: string) => void;
|
||||||
|
updateFormatting: (newFormatting: FormattingOptions, mode: string) => void;
|
||||||
|
}
|
||||||
|
> = {
|
||||||
|
[DataSource.LOGS]: logsUpdater,
|
||||||
|
[DataSource.TRACES]: tracesUpdater,
|
||||||
|
[DataSource.METRICS]: metricsUpdater,
|
||||||
|
};
|
||||||
|
|
||||||
|
export function usePreferenceUpdater({
|
||||||
|
dataSource,
|
||||||
|
mode,
|
||||||
|
setReSync,
|
||||||
|
}: {
|
||||||
|
dataSource: DataSource;
|
||||||
|
mode: string;
|
||||||
|
setReSync: Dispatch<SetStateAction<number>>;
|
||||||
|
}): {
|
||||||
|
updateColumns: (newColumns: BaseAutocompleteData[]) => void;
|
||||||
|
updateFormatting: (newFormatting: FormattingOptions) => void;
|
||||||
|
} {
|
||||||
|
const updater = updaterConfig[dataSource];
|
||||||
|
|
||||||
|
return {
|
||||||
|
updateColumns: (newColumns: BaseAutocompleteData[]): void => {
|
||||||
|
updater.updateColumns(newColumns, mode);
|
||||||
|
setReSync((prev: number) => prev + 1);
|
||||||
|
},
|
||||||
|
updateFormatting: (newFormatting: FormattingOptions): void => {
|
||||||
|
updater.updateFormatting(newFormatting, mode);
|
||||||
|
setReSync((prev: number) => prev + 1);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user