mirror of
https://git.mirrors.martin98.com/https://github.com/SigNoz/signoz
synced 2025-08-14 15:45:57 +08:00
feat: support dashboard local state (#4475)
This commit is contained in:
parent
6837c41090
commit
26bc94fc46
@ -6,6 +6,12 @@
|
||||
.dynamicColumnTable-button {
|
||||
align-self: flex-end;
|
||||
margin: 10px 0;
|
||||
|
||||
&.filter-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,9 +1,9 @@
|
||||
/* eslint-disable react/jsx-props-no-spreading */
|
||||
import './DynamicColumnTable.syles.scss';
|
||||
|
||||
import { SettingOutlined } from '@ant-design/icons';
|
||||
import { Button, Dropdown, MenuProps, Switch } from 'antd';
|
||||
import { ColumnsType } from 'antd/lib/table';
|
||||
import { SlidersHorizontal } from 'lucide-react';
|
||||
import { memo, useEffect, useState } from 'react';
|
||||
import { popupContainer } from 'utils/selectPopupContainer';
|
||||
|
||||
@ -90,9 +90,9 @@ function DynamicColumnTable({
|
||||
trigger={['click']}
|
||||
>
|
||||
<Button
|
||||
className="dynamicColumnTable-button"
|
||||
className="dynamicColumnTable-button filter-btn"
|
||||
size="middle"
|
||||
icon={<SettingOutlined />}
|
||||
icon={<SlidersHorizontal size={14} />}
|
||||
/>
|
||||
</Dropdown>
|
||||
)}
|
||||
|
@ -15,4 +15,5 @@ export enum LOCALSTORAGE {
|
||||
LOGGED_IN_USER_EMAIL = 'LOGGED_IN_USER_EMAIL',
|
||||
CHAT_SUPPORT = 'CHAT_SUPPORT',
|
||||
IS_IDENTIFIED_USER = 'IS_IDENTIFIED_USER',
|
||||
DASHBOARD_VARIABLES = 'DASHBOARD_VARIABLES',
|
||||
}
|
||||
|
@ -1,18 +1,22 @@
|
||||
import { Row } from 'antd';
|
||||
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import { useDashboardVariablesFromLocalStorage } from 'hooks/dashboard/useDashboardFromLocalStorage';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { memo, useEffect, useState } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { Dashboard, IDashboardVariable } from 'types/api/dashboard/getAll';
|
||||
import AppReducer from 'types/reducer/app';
|
||||
import { IDashboardVariable } from 'types/api/dashboard/getAll';
|
||||
|
||||
import { convertVariablesToDbFormat } from './util';
|
||||
import VariableItem from './VariableItem';
|
||||
|
||||
function DashboardVariableSelection(): JSX.Element | null {
|
||||
const { selectedDashboard, setSelectedDashboard } = useDashboard();
|
||||
const {
|
||||
selectedDashboard,
|
||||
setSelectedDashboard,
|
||||
dashboardId,
|
||||
} = useDashboard();
|
||||
|
||||
const {
|
||||
updateLocalStorageDashboardVariables,
|
||||
} = useDashboardVariablesFromLocalStorage(dashboardId);
|
||||
|
||||
const { data } = selectedDashboard || {};
|
||||
|
||||
@ -23,8 +27,6 @@ function DashboardVariableSelection(): JSX.Element | null {
|
||||
|
||||
const [variablesTableData, setVariablesTableData] = useState<any>([]);
|
||||
|
||||
const { role } = useSelector<AppState, AppReducer>((state) => state.app);
|
||||
|
||||
useEffect(() => {
|
||||
if (variables) {
|
||||
const tableRowData = [];
|
||||
@ -52,40 +54,6 @@ function DashboardVariableSelection(): JSX.Element | null {
|
||||
setUpdate(!update);
|
||||
};
|
||||
|
||||
const updateMutation = useUpdateDashboard();
|
||||
const { notifications } = useNotifications();
|
||||
|
||||
const updateVariables = (
|
||||
name: string,
|
||||
updatedVariablesData: Dashboard['data']['variables'],
|
||||
): void => {
|
||||
if (!selectedDashboard) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateMutation.mutateAsync(
|
||||
{
|
||||
...selectedDashboard,
|
||||
data: {
|
||||
...selectedDashboard.data,
|
||||
variables: updatedVariablesData,
|
||||
},
|
||||
},
|
||||
{
|
||||
onSuccess: (updatedDashboard) => {
|
||||
if (updatedDashboard.payload) {
|
||||
setSelectedDashboard(updatedDashboard.payload);
|
||||
}
|
||||
},
|
||||
onError: () => {
|
||||
notifications.error({
|
||||
message: `Error updating ${name} variable`,
|
||||
});
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
const onValueUpdate = (
|
||||
name: string,
|
||||
id: string,
|
||||
@ -105,12 +73,22 @@ function DashboardVariableSelection(): JSX.Element | null {
|
||||
return variableCopy;
|
||||
},
|
||||
);
|
||||
updateLocalStorageDashboardVariables(id, value, allSelected);
|
||||
|
||||
const variables = convertVariablesToDbFormat(newVariablesArr);
|
||||
|
||||
if (role !== 'VIEWER' && selectedDashboard) {
|
||||
updateVariables(name, variables);
|
||||
if (selectedDashboard) {
|
||||
setSelectedDashboard({
|
||||
...selectedDashboard,
|
||||
data: {
|
||||
...selectedDashboard?.data,
|
||||
variables: {
|
||||
...variables,
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
onVarChanged(name);
|
||||
|
||||
setUpdate(!update);
|
||||
|
100
frontend/src/hooks/dashboard/useDashboardFromLocalStorage.tsx
Normal file
100
frontend/src/hooks/dashboard/useDashboardFromLocalStorage.tsx
Normal file
@ -0,0 +1,100 @@
|
||||
import getLocalStorageKey from 'api/browser/localstorage/get';
|
||||
import setLocalStorageKey from 'api/browser/localstorage/set';
|
||||
import { LOCALSTORAGE } from 'constants/localStorage';
|
||||
import { defaultTo } from 'lodash-es';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { IDashboardVariable } from 'types/api/dashboard/getAll';
|
||||
|
||||
interface LocalStoreDashboardVariables {
|
||||
[id: string]: {
|
||||
selectedValue: IDashboardVariable['selectedValue'];
|
||||
allSelected: boolean;
|
||||
};
|
||||
}
|
||||
interface DashboardLocalStorageVariables {
|
||||
[id: string]: LocalStoreDashboardVariables;
|
||||
}
|
||||
|
||||
interface UseDashboardVariablesFromLocalStorageReturn {
|
||||
currentDashboard: LocalStoreDashboardVariables;
|
||||
updateLocalStorageDashboardVariables: (
|
||||
id: string,
|
||||
selectedValue: IDashboardVariable['selectedValue'],
|
||||
allSelected: boolean,
|
||||
) => void;
|
||||
}
|
||||
|
||||
export const useDashboardVariablesFromLocalStorage = (
|
||||
dashboardId: string,
|
||||
): UseDashboardVariablesFromLocalStorageReturn => {
|
||||
const [
|
||||
allDashboards,
|
||||
setAllDashboards,
|
||||
] = useState<DashboardLocalStorageVariables>({});
|
||||
|
||||
const [
|
||||
currentDashboard,
|
||||
setCurrentDashboard,
|
||||
] = useState<LocalStoreDashboardVariables>({});
|
||||
|
||||
useEffect(() => {
|
||||
const localStoreDashboardVariablesString = getLocalStorageKey(
|
||||
LOCALSTORAGE.DASHBOARD_VARIABLES,
|
||||
);
|
||||
let localStoreDashboardVariables: DashboardLocalStorageVariables = {};
|
||||
if (localStoreDashboardVariablesString === null) {
|
||||
try {
|
||||
const serialzedData = JSON.stringify({
|
||||
[dashboardId]: {},
|
||||
});
|
||||
|
||||
setLocalStorageKey(LOCALSTORAGE.DASHBOARD_VARIABLES, serialzedData);
|
||||
} catch {
|
||||
console.error('Failed to seralise the data');
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
localStoreDashboardVariables = JSON.parse(
|
||||
localStoreDashboardVariablesString,
|
||||
);
|
||||
} catch {
|
||||
console.error('Failed to parse dashboards from local storage');
|
||||
localStoreDashboardVariables = {};
|
||||
} finally {
|
||||
setAllDashboards(localStoreDashboardVariables);
|
||||
}
|
||||
}
|
||||
setCurrentDashboard(defaultTo(localStoreDashboardVariables[dashboardId], {}));
|
||||
}, [dashboardId]);
|
||||
|
||||
const updateLocalStorageDashboardVariables = (
|
||||
id: string,
|
||||
selectedValue: IDashboardVariable['selectedValue'],
|
||||
allSelected: boolean,
|
||||
): void => {
|
||||
const newCurrentDashboard = {
|
||||
...currentDashboard,
|
||||
[id]: { selectedValue, allSelected },
|
||||
};
|
||||
|
||||
const newAllDashboards = {
|
||||
...allDashboards,
|
||||
[dashboardId]: newCurrentDashboard,
|
||||
};
|
||||
|
||||
try {
|
||||
const serializedData = JSON.stringify(newAllDashboards);
|
||||
setLocalStorageKey(LOCALSTORAGE.DASHBOARD_VARIABLES, serializedData);
|
||||
} catch {
|
||||
console.error('Failed to set dashboards in local storage');
|
||||
}
|
||||
|
||||
setAllDashboards(newAllDashboards);
|
||||
setCurrentDashboard(newCurrentDashboard);
|
||||
};
|
||||
|
||||
return {
|
||||
currentDashboard,
|
||||
updateLocalStorageDashboardVariables,
|
||||
};
|
||||
};
|
@ -6,6 +6,7 @@ import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||
import ROUTES from 'constants/routes';
|
||||
import { getMinMax } from 'container/TopNav/AutoRefresh/config';
|
||||
import dayjs, { Dayjs } from 'dayjs';
|
||||
import { useDashboardVariablesFromLocalStorage } from 'hooks/dashboard/useDashboardFromLocalStorage';
|
||||
import useAxiosError from 'hooks/useAxiosError';
|
||||
import useTabVisibility from 'hooks/useTabFocus';
|
||||
import { getUpdatedLayout } from 'lib/dashboard/getUpdatedLayout';
|
||||
@ -95,6 +96,10 @@ export function DashboardProvider({
|
||||
|
||||
const [selectedDashboard, setSelectedDashboard] = useState<Dashboard>();
|
||||
|
||||
const { currentDashboard } = useDashboardVariablesFromLocalStorage(
|
||||
dashboardId,
|
||||
);
|
||||
|
||||
const updatedTimeRef = useRef<Dayjs | null>(null); // Using ref to store the updated time
|
||||
const modalRef = useRef<any>(null);
|
||||
|
||||
@ -103,11 +108,33 @@ export function DashboardProvider({
|
||||
const { t } = useTranslation(['dashboard']);
|
||||
const dashboardRef = useRef<Dashboard>();
|
||||
|
||||
const mergeDBWithLocalStorage = (
|
||||
data: Dashboard,
|
||||
localStorageVariables: any,
|
||||
): Dashboard => {
|
||||
const updatedData = data;
|
||||
if (data && localStorageVariables) {
|
||||
const updatedVariables = data.data.variables;
|
||||
Object.keys(data.data.variables).forEach((variable) => {
|
||||
const updatedVariable = {
|
||||
...data.data.variables[variable],
|
||||
...localStorageVariables[variable as any],
|
||||
};
|
||||
|
||||
updatedVariables[variable] = updatedVariable;
|
||||
});
|
||||
updatedData.data.variables = updatedVariables;
|
||||
}
|
||||
return updatedData;
|
||||
};
|
||||
// As we do not have order and ID's in the variables object, we have to process variables to add order and ID if they do not exist in the variables object
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
const transformDashboardVariables = (data: Dashboard): Dashboard => {
|
||||
if (data && data.data && data.data.variables) {
|
||||
const clonedDashboardData = JSON.parse(JSON.stringify(data));
|
||||
const clonedDashboardData = mergeDBWithLocalStorage(
|
||||
JSON.parse(JSON.stringify(data)),
|
||||
currentDashboard,
|
||||
);
|
||||
const { variables } = clonedDashboardData.data;
|
||||
const existingOrders: Set<number> = new Set();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user