mirror of
https://github.com/SigNoz/signoz.git
synced 2026-02-09 11:12:21 +00:00
Compare commits
1 Commits
test/uplot
...
chore/issu
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5859cb9485 |
2
.github/CODEOWNERS
vendored
2
.github/CODEOWNERS
vendored
@@ -110,6 +110,7 @@
|
||||
# Dashboard Owners
|
||||
|
||||
/frontend/src/hooks/dashboard/ @SigNoz/pulse-frontend
|
||||
/frontend/src/providers/Dashboard/ @SigNoz/pulse-frontend
|
||||
|
||||
## Dashboard Types
|
||||
|
||||
@@ -131,3 +132,4 @@
|
||||
|
||||
/frontend/src/pages/PublicDashboard/ @SigNoz/pulse-frontend
|
||||
/frontend/src/container/PublicDashboardContainer/ @SigNoz/pulse-frontend
|
||||
|
||||
|
||||
@@ -105,6 +105,7 @@
|
||||
"i18next": "^21.6.12",
|
||||
"i18next-browser-languagedetector": "^6.1.3",
|
||||
"i18next-http-backend": "^1.3.2",
|
||||
"immer": "11.1.3",
|
||||
"jest": "^27.5.1",
|
||||
"js-base64": "^3.7.2",
|
||||
"less": "^4.1.2",
|
||||
|
||||
@@ -32,13 +32,12 @@ import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import useDebounce from 'hooks/useDebounce';
|
||||
import { debounce, isNull } from 'lodash-es';
|
||||
import { Info, TriangleAlert } from 'lucide-react';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { useDashboardVariablesByType } from 'providers/Dashboard/store/useDashboardVariablesByType';
|
||||
import {
|
||||
IDetailedError,
|
||||
IQueryContext,
|
||||
IValidationResult,
|
||||
} from 'types/antlrQueryTypes';
|
||||
import { IDashboardVariable } from 'types/api/dashboard/getAll';
|
||||
import { IBuilderQuery } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { QueryKeyDataSuggestionsProps } from 'types/api/querySuggestions/types';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
@@ -207,14 +206,9 @@ function QuerySearch({
|
||||
const lastValueRef = useRef<string>('');
|
||||
const isMountedRef = useRef<boolean>(true);
|
||||
|
||||
const { selectedDashboard } = useDashboard();
|
||||
|
||||
const dynamicVariables = useMemo(
|
||||
() =>
|
||||
Object.values(selectedDashboard?.data?.variables || {})?.filter(
|
||||
(variable: IDashboardVariable) => variable.type === 'DYNAMIC',
|
||||
),
|
||||
[selectedDashboard],
|
||||
const dashboardDynamicVariables = useDashboardVariablesByType(
|
||||
'DYNAMIC',
|
||||
'values',
|
||||
);
|
||||
|
||||
// Add back the generateOptions function and useEffect
|
||||
@@ -1069,7 +1063,7 @@ function QuerySearch({
|
||||
);
|
||||
|
||||
// Add dynamic variables suggestions for the current key
|
||||
const variableName = dynamicVariables?.find(
|
||||
const variableName = dashboardDynamicVariables?.find(
|
||||
(variable) => variable?.dynamicVariablesAttribute === keyName,
|
||||
)?.name;
|
||||
|
||||
|
||||
@@ -43,8 +43,9 @@ import {
|
||||
} from 'lucide-react';
|
||||
import { useAppContext } from 'providers/App/App';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { useDashboardVariables } from 'providers/Dashboard/store/useDashboardVariables';
|
||||
import { sortLayout } from 'providers/Dashboard/util';
|
||||
import { DashboardData, IDashboardVariable } from 'types/api/dashboard/getAll';
|
||||
import { DashboardData } from 'types/api/dashboard/getAll';
|
||||
import { Props } from 'types/api/dashboard/update';
|
||||
import { ROLES, USER_ROLES } from 'types/roles';
|
||||
import { ComponentTypes } from 'utils/permission';
|
||||
@@ -56,7 +57,11 @@ import { Base64Icons } from '../DashboardSettings/General/utils';
|
||||
import DashboardVariableSelection from '../DashboardVariablesSelection';
|
||||
import SettingsDrawer from './SettingsDrawer';
|
||||
import { VariablesSettingsTab } from './types';
|
||||
import { DEFAULT_ROW_NAME, downloadObjectAsJson } from './utils';
|
||||
import {
|
||||
DEFAULT_ROW_NAME,
|
||||
downloadObjectAsJson,
|
||||
sanitizeDashboardData,
|
||||
} from './utils';
|
||||
|
||||
import './Description.styles.scss';
|
||||
|
||||
@@ -64,28 +69,6 @@ interface DashboardDescriptionProps {
|
||||
handle: FullScreenHandle;
|
||||
}
|
||||
|
||||
export function sanitizeDashboardData(
|
||||
selectedData: DashboardData,
|
||||
): DashboardData {
|
||||
if (!selectedData?.variables) {
|
||||
return selectedData;
|
||||
}
|
||||
|
||||
const updatedVariables = Object.entries(selectedData.variables).reduce(
|
||||
(acc, [key, value]) => {
|
||||
const { selectedValue: _selectedValue, ...rest } = value;
|
||||
acc[key] = rest;
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, IDashboardVariable>,
|
||||
);
|
||||
|
||||
return {
|
||||
...selectedData,
|
||||
variables: updatedVariables,
|
||||
};
|
||||
}
|
||||
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
function DashboardDescription(props: DashboardDescriptionProps): JSX.Element {
|
||||
const { safeNavigate } = useSafeNavigate();
|
||||
@@ -119,6 +102,7 @@ function DashboardDescription(props: DashboardDescriptionProps): JSX.Element {
|
||||
uuid: selectedDashboard.id,
|
||||
}
|
||||
: ({} as DashboardData);
|
||||
const { dashboardVariables } = useDashboardVariables();
|
||||
|
||||
const { title = '', description, tags, image = Base64Icons[0] } =
|
||||
selectedData || {};
|
||||
@@ -576,7 +560,7 @@ function DashboardDescription(props: DashboardDescriptionProps): JSX.Element {
|
||||
<section className="dashboard-description-section">{description}</section>
|
||||
)}
|
||||
|
||||
{!isEmpty(selectedData.variables) && (
|
||||
{!isEmpty(dashboardVariables) && (
|
||||
<section className="dashboard-variables">
|
||||
<DashboardVariableSelection />
|
||||
</section>
|
||||
|
||||
@@ -1,3 +1,27 @@
|
||||
import { DashboardData, IDashboardVariable } from 'types/api/dashboard/getAll';
|
||||
|
||||
export function sanitizeDashboardData(
|
||||
selectedData: DashboardData,
|
||||
): DashboardData {
|
||||
if (!selectedData?.variables) {
|
||||
return selectedData;
|
||||
}
|
||||
|
||||
const updatedVariables = Object.entries(selectedData.variables).reduce(
|
||||
(acc, [key, value]) => {
|
||||
const { selectedValue: _selectedValue, ...rest } = value;
|
||||
acc[key] = rest;
|
||||
return acc;
|
||||
},
|
||||
{} as Record<string, IDashboardVariable>,
|
||||
);
|
||||
|
||||
return {
|
||||
...selectedData,
|
||||
variables: updatedVariables,
|
||||
};
|
||||
}
|
||||
|
||||
export function downloadObjectAsJson(
|
||||
exportObj: unknown,
|
||||
exportName: string,
|
||||
|
||||
@@ -14,10 +14,7 @@ import { CustomSelect } from 'components/NewSelect';
|
||||
import TextToolTip from 'components/TextToolTip';
|
||||
import { PANEL_GROUP_TYPES } from 'constants/queryBuilder';
|
||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||
import {
|
||||
createDynamicVariableToWidgetsMap,
|
||||
getWidgetsHavingDynamicVariableAttribute,
|
||||
} from 'hooks/dashboard/utils';
|
||||
import { getWidgetsHavingDynamicVariableAttribute } from 'hooks/dashboard/utils';
|
||||
import { useGetFieldValues } from 'hooks/dynamicVariables/useGetFieldValues';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import { commaValuesParser } from 'lib/dashbaordVariables/customCommaValuesParser';
|
||||
@@ -34,6 +31,7 @@ import {
|
||||
X,
|
||||
} from 'lucide-react';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { useWidgetsByDynamicVariableId } from 'providers/Dashboard/store/useWidgetsByDynamicVariableId';
|
||||
import { AppState } from 'store/reducers';
|
||||
import {
|
||||
IDashboardVariable,
|
||||
@@ -243,23 +241,11 @@ function VariableItem({
|
||||
const [selectedWidgets, setSelectedWidgets] = useState<string[]>([]);
|
||||
|
||||
const { selectedDashboard } = useDashboard();
|
||||
const widgetsByDynamicVariableId = useWidgetsByDynamicVariableId();
|
||||
|
||||
useEffect(() => {
|
||||
const dynamicVariables = Object.values(
|
||||
selectedDashboard?.data?.variables || {},
|
||||
)?.filter((variable: IDashboardVariable) => variable.type === 'DYNAMIC');
|
||||
|
||||
const widgets =
|
||||
selectedDashboard?.data?.widgets?.filter(
|
||||
(widget) => widget.panelTypes !== PANEL_GROUP_TYPES.ROW,
|
||||
) || [];
|
||||
const widgetsHavingDynamicVariables = createDynamicVariableToWidgetsMap(
|
||||
dynamicVariables,
|
||||
widgets as Widgets[],
|
||||
);
|
||||
|
||||
if (variableData?.id && variableData.id in widgetsHavingDynamicVariables) {
|
||||
setSelectedWidgets(widgetsHavingDynamicVariables[variableData.id] || []);
|
||||
if (variableData?.id && variableData.id in widgetsByDynamicVariableId) {
|
||||
setSelectedWidgets(widgetsByDynamicVariableId[variableData.id] || []);
|
||||
} else if (dynamicVariablesSelectedValue?.name) {
|
||||
const widgets = getWidgetsHavingDynamicVariableAttribute(
|
||||
dynamicVariablesSelectedValue?.name,
|
||||
@@ -275,6 +261,7 @@ function VariableItem({
|
||||
selectedDashboard,
|
||||
variableData.id,
|
||||
variableData.name,
|
||||
widgetsByDynamicVariableId,
|
||||
]);
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||
import React, { useEffect, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { HolderOutlined, PlusOutlined } from '@ant-design/icons';
|
||||
import type { DragEndEvent, UniqueIdentifier } from '@dnd-kit/core';
|
||||
@@ -21,7 +21,9 @@ import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
||||
import { useNotifications } from 'hooks/useNotifications';
|
||||
import { PenLine, Trash2 } from 'lucide-react';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { Dashboard, IDashboardVariable } from 'types/api/dashboard/getAll';
|
||||
import { IDashboardVariables } from 'providers/Dashboard/store/dashboardVariablesStore';
|
||||
import { useDashboardVariables } from 'providers/Dashboard/store/useDashboardVariables';
|
||||
import { IDashboardVariable } from 'types/api/dashboard/getAll';
|
||||
|
||||
import { TVariableMode } from './types';
|
||||
import VariableItem from './VariableItem/VariableItem';
|
||||
@@ -91,13 +93,10 @@ function VariablesSettings({
|
||||
const { t } = useTranslation(['dashboard']);
|
||||
|
||||
const { selectedDashboard, setSelectedDashboard } = useDashboard();
|
||||
const { dashboardVariables } = useDashboardVariables();
|
||||
|
||||
const { notifications } = useNotifications();
|
||||
|
||||
const variables = useMemo(() => selectedDashboard?.data?.variables || {}, [
|
||||
selectedDashboard?.data?.variables,
|
||||
]);
|
||||
|
||||
const [variablesTableData, setVariablesTableData] = useState<any>([]);
|
||||
const [variblesOrderArr, setVariablesOrderArr] = useState<number[]>([]);
|
||||
const [existingVariableNamesMap, setExistingVariableNamesMap] = useState<
|
||||
@@ -147,13 +146,13 @@ function VariablesSettings({
|
||||
const variableNamesMap = {};
|
||||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const [key, value] of Object.entries(variables)) {
|
||||
for (const [key, value] of Object.entries(dashboardVariables)) {
|
||||
const { order, id, name } = value;
|
||||
|
||||
tableRowData.push({
|
||||
key,
|
||||
name: key,
|
||||
...variables[key],
|
||||
...dashboardVariables[key],
|
||||
id,
|
||||
});
|
||||
|
||||
@@ -174,10 +173,10 @@ function VariablesSettings({
|
||||
setVariablesTableData(tableRowData);
|
||||
setVariablesOrderArr(variableOrderArr);
|
||||
setExistingVariableNamesMap(variableNamesMap);
|
||||
}, [variables]);
|
||||
}, [dashboardVariables]);
|
||||
|
||||
const updateVariables = (
|
||||
updatedVariablesData: Dashboard['data']['variables'],
|
||||
updatedVariablesData: IDashboardVariables,
|
||||
currentRequestedId?: string,
|
||||
widgetIds?: string[],
|
||||
applyToAll?: boolean,
|
||||
@@ -312,7 +311,7 @@ function VariablesSettings({
|
||||
currentVariableId?: string,
|
||||
): boolean => {
|
||||
// Check if any other dynamic variable already uses this attribute key
|
||||
const isDuplicateAttributeKey = Object.values(variables).some(
|
||||
const isDuplicateAttributeKey = Object.values(dashboardVariables).some(
|
||||
(variable: IDashboardVariable) =>
|
||||
variable.type === 'DYNAMIC' &&
|
||||
variable.dynamicVariablesAttribute === attributeKey &&
|
||||
@@ -422,7 +421,7 @@ function VariablesSettings({
|
||||
{variableViewMode ? (
|
||||
<VariableItem
|
||||
variableData={{ ...variableEditData } as IDashboardVariable}
|
||||
existingVariables={variables}
|
||||
existingVariables={dashboardVariables}
|
||||
onSave={onVariableSaveHandler}
|
||||
onCancel={onDoneVariableViewMode}
|
||||
validateName={validateVariableName}
|
||||
|
||||
@@ -6,6 +6,7 @@ import useVariablesFromUrl from 'hooks/dashboard/useVariablesFromUrl';
|
||||
import { isEmpty } from 'lodash-es';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { initializeDefaultVariables } from 'providers/Dashboard/initializeDefaultVariables';
|
||||
import { useDashboardVariables } from 'providers/Dashboard/store/useDashboardVariables';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { IDashboardVariable } from 'types/api/dashboard/getAll';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
@@ -33,9 +34,7 @@ function DashboardVariableSelection(): JSX.Element | null {
|
||||
|
||||
const { updateUrlVariable, getUrlVariables } = useVariablesFromUrl();
|
||||
|
||||
const { data } = selectedDashboard || {};
|
||||
|
||||
const { variables } = data || {};
|
||||
const { dashboardVariables } = useDashboardVariables();
|
||||
|
||||
const [variablesTableData, setVariablesTableData] = useState<any>([]);
|
||||
|
||||
@@ -48,29 +47,31 @@ function DashboardVariableSelection(): JSX.Element | null {
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (variables) {
|
||||
const tableRowData = [];
|
||||
const tableRowData = [];
|
||||
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const [key, value] of Object.entries(variables)) {
|
||||
const { id } = value;
|
||||
// eslint-disable-next-line no-restricted-syntax
|
||||
for (const [key, value] of Object.entries(dashboardVariables)) {
|
||||
const { id } = value;
|
||||
|
||||
tableRowData.push({
|
||||
key,
|
||||
name: key,
|
||||
...variables[key],
|
||||
id,
|
||||
});
|
||||
}
|
||||
|
||||
tableRowData.sort((a, b) => a.order - b.order);
|
||||
|
||||
setVariablesTableData(tableRowData);
|
||||
|
||||
// Initialize variables with default values if not in URL
|
||||
initializeDefaultVariables(variables, getUrlVariables, updateUrlVariable);
|
||||
tableRowData.push({
|
||||
key,
|
||||
name: key,
|
||||
...dashboardVariables[key],
|
||||
id,
|
||||
});
|
||||
}
|
||||
}, [getUrlVariables, updateUrlVariable, variables]);
|
||||
|
||||
tableRowData.sort((a, b) => a.order - b.order);
|
||||
|
||||
setVariablesTableData(tableRowData);
|
||||
|
||||
// Initialize variables with default values if not in URL
|
||||
initializeDefaultVariables(
|
||||
dashboardVariables,
|
||||
getUrlVariables,
|
||||
updateUrlVariable,
|
||||
);
|
||||
}, [getUrlVariables, updateUrlVariable, dashboardVariables]);
|
||||
|
||||
useEffect(() => {
|
||||
if (variablesTableData.length > 0) {
|
||||
@@ -94,7 +95,7 @@ function DashboardVariableSelection(): JSX.Element | null {
|
||||
cycleNodes,
|
||||
});
|
||||
}
|
||||
}, [variables, variablesTableData]);
|
||||
}, [dashboardVariables, variablesTableData]);
|
||||
|
||||
// this handles the case where the dependency order changes i.e. variable list updated via creation or deletion etc. and we need to refetch the variables
|
||||
// also trigger when the global time changes
|
||||
@@ -122,7 +123,7 @@ function DashboardVariableSelection(): JSX.Element | null {
|
||||
if (id) {
|
||||
// For dynamic variables, only store in localStorage when NOT allSelected
|
||||
// This makes localStorage much lighter by avoiding storing all individual values
|
||||
const variable = variables?.[id] || variables?.[name];
|
||||
const variable = dashboardVariables?.[id] || dashboardVariables?.[name];
|
||||
const isDynamic = variable?.type === 'DYNAMIC';
|
||||
updateLocalStorageDashboardVariables(name, value, allSelected, isDynamic);
|
||||
|
||||
@@ -185,7 +186,7 @@ function DashboardVariableSelection(): JSX.Element | null {
|
||||
}
|
||||
};
|
||||
|
||||
if (!variables) {
|
||||
if (!dashboardVariables) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -202,7 +203,7 @@ function DashboardVariableSelection(): JSX.Element | null {
|
||||
variable.type === 'DYNAMIC' ? (
|
||||
<DynamicVariableSelection
|
||||
key={`${variable.name}${variable.id}${variable.order}`}
|
||||
existingVariables={variables}
|
||||
existingVariables={dashboardVariables}
|
||||
variableData={{
|
||||
name: variable.name,
|
||||
...variable,
|
||||
@@ -212,7 +213,7 @@ function DashboardVariableSelection(): JSX.Element | null {
|
||||
) : (
|
||||
<VariableItem
|
||||
key={`${variable.name}${variable.id}}${variable.order}`}
|
||||
existingVariables={variables}
|
||||
existingVariables={dashboardVariables}
|
||||
variableData={{
|
||||
name: variable.name,
|
||||
...variable,
|
||||
|
||||
@@ -3,7 +3,8 @@ import { useCallback } from 'react';
|
||||
import { useAddDynamicVariableToPanels } from 'hooks/dashboard/useAddDynamicVariableToPanels';
|
||||
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { Dashboard, IDashboardVariable } from 'types/api/dashboard/getAll';
|
||||
import { IDashboardVariables } from 'providers/Dashboard/store/dashboardVariablesStore';
|
||||
import { IDashboardVariable } from 'types/api/dashboard/getAll';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { convertVariablesToDbFormat } from './util';
|
||||
@@ -27,7 +28,7 @@ interface UseDashboardVariableUpdateReturn {
|
||||
widgetId?: string,
|
||||
) => void;
|
||||
updateVariables: (
|
||||
updatedVariablesData: Dashboard['data']['variables'],
|
||||
updatedVariablesData: IDashboardVariables,
|
||||
currentRequestedId?: string,
|
||||
widgetIds?: string[],
|
||||
applyToAll?: boolean,
|
||||
@@ -106,7 +107,7 @@ export const useDashboardVariableUpdate = (): UseDashboardVariableUpdateReturn =
|
||||
|
||||
const updateVariables = useCallback(
|
||||
(
|
||||
updatedVariablesData: Dashboard['data']['variables'],
|
||||
updatedVariablesData: IDashboardVariables,
|
||||
currentRequestedId?: string,
|
||||
widgetIds?: string[],
|
||||
applyToAll?: boolean,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { OptionData } from 'components/NewSelect/types';
|
||||
import { isEmpty, isNull } from 'lodash-es';
|
||||
import { Dashboard, IDashboardVariable } from 'types/api/dashboard/getAll';
|
||||
import { IDashboardVariables } from 'providers/Dashboard/store/dashboardVariablesStore';
|
||||
import { IDashboardVariable } from 'types/api/dashboard/getAll';
|
||||
|
||||
export function areArraysEqual(
|
||||
a: (string | number | boolean)[],
|
||||
@@ -21,7 +22,7 @@ export function areArraysEqual(
|
||||
|
||||
export const convertVariablesToDbFormat = (
|
||||
variblesArr: IDashboardVariable[],
|
||||
): Dashboard['data']['variables'] =>
|
||||
): IDashboardVariables =>
|
||||
variblesArr.reduce((result, obj: IDashboardVariable) => {
|
||||
const { id } = obj;
|
||||
|
||||
|
||||
@@ -37,6 +37,7 @@ import GetMinMax from 'lib/getMinMax';
|
||||
import { isEmpty } from 'lodash-es';
|
||||
import { useAppContext } from 'providers/App/App';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { useDashboardVariables } from 'providers/Dashboard/store/useDashboardVariables';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { Warning } from 'types/api';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
@@ -79,6 +80,7 @@ function FullView({
|
||||
}, [setCurrentGraphRef]);
|
||||
|
||||
const { selectedDashboard, isDashboardLocked } = useDashboard();
|
||||
const { dashboardVariables } = useDashboardVariables();
|
||||
const { user } = useAppContext();
|
||||
|
||||
const [editWidget] = useComponentPermission(['edit_widget'], user.role);
|
||||
@@ -114,7 +116,7 @@ function FullView({
|
||||
graphType: getGraphType(selectedPanelType),
|
||||
query: updatedQuery,
|
||||
globalSelectedInterval: globalSelectedTime,
|
||||
variables: getDashboardVariables(selectedDashboard?.data.variables),
|
||||
variables: getDashboardVariables(dashboardVariables),
|
||||
fillGaps: widget.fillSpans,
|
||||
formatForWeb: selectedPanelType === PANEL_TYPES.TABLE,
|
||||
originalGraphType: selectedPanelType,
|
||||
@@ -125,7 +127,7 @@ function FullView({
|
||||
graphType: PANEL_TYPES.LIST,
|
||||
selectedTime: widget?.timePreferance || 'GLOBAL_TIME',
|
||||
globalSelectedInterval: globalSelectedTime,
|
||||
variables: getDashboardVariables(selectedDashboard?.data.variables),
|
||||
variables: getDashboardVariables(dashboardVariables),
|
||||
tableParams: {
|
||||
pagination: {
|
||||
offset: 0,
|
||||
|
||||
@@ -53,7 +53,7 @@ function GridCardGraph({
|
||||
customOnRowClick,
|
||||
customTimeRangeWindowForCoRelation,
|
||||
enableDrillDown,
|
||||
widgetsHavingDynamicVariables,
|
||||
widgetsByDynamicVariableId,
|
||||
}: GridCardGraphProps): JSX.Element {
|
||||
const dispatch = useDispatch();
|
||||
const [errorMessage, setErrorMessage] = useState<string>();
|
||||
@@ -226,8 +226,8 @@ function GridCardGraph({
|
||||
? Object.entries(variables).reduce((acc, [id, variable]) => {
|
||||
if (
|
||||
variable.type !== 'DYNAMIC' ||
|
||||
(widgetsHavingDynamicVariables?.[variable.id] &&
|
||||
widgetsHavingDynamicVariables?.[variable.id].includes(widget.id))
|
||||
(widgetsByDynamicVariableId?.[variable.id] &&
|
||||
widgetsByDynamicVariableId?.[variable.id].includes(widget.id))
|
||||
) {
|
||||
return { ...acc, [id]: variable.selectedValue };
|
||||
}
|
||||
|
||||
@@ -4,8 +4,9 @@ import { ToggleGraphProps } from 'components/Graph/types';
|
||||
import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
|
||||
import { RowData } from 'lib/query/createTableColumnsFromQuery';
|
||||
import { OnClickPluginOpts } from 'lib/uPlotLib/plugins/onClickPlugin';
|
||||
import { IDashboardVariables } from 'providers/Dashboard/store/dashboardVariablesStore';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
import { Dashboard, Widgets } from 'types/api/dashboard/getAll';
|
||||
import { Widgets } from 'types/api/dashboard/getAll';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
import { QueryData } from 'types/api/widgets/getQuery';
|
||||
import uPlot from 'uplot';
|
||||
@@ -50,7 +51,7 @@ export interface GridCardGraphProps {
|
||||
headerMenuList?: WidgetGraphComponentProps['headerMenuList'];
|
||||
onClickHandler?: OnClickPluginOpts['onClick'];
|
||||
isQueryEnabled: boolean;
|
||||
variables?: Dashboard['data']['variables'];
|
||||
variables?: IDashboardVariables;
|
||||
version?: string;
|
||||
onDragSelect: (start: number, end: number) => void;
|
||||
customOnDragSelect?: (start: number, end: number) => void;
|
||||
@@ -71,7 +72,7 @@ export interface GridCardGraphProps {
|
||||
customOnRowClick?: (record: RowData) => void;
|
||||
customTimeRangeWindowForCoRelation?: string | undefined;
|
||||
enableDrillDown?: boolean;
|
||||
widgetsHavingDynamicVariables?: Record<string, string[]>;
|
||||
widgetsByDynamicVariableId?: Record<string, string[]>;
|
||||
}
|
||||
|
||||
export interface GetGraphVisibilityStateOnLegendClickProps {
|
||||
|
||||
@@ -15,7 +15,6 @@ import { PANEL_GROUP_TYPES, PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { themeColors } from 'constants/theme';
|
||||
import { DEFAULT_ROW_NAME } from 'container/DashboardContainer/DashboardDescription/utils';
|
||||
import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard';
|
||||
import { createDynamicVariableToWidgetsMap } from 'hooks/dashboard/utils';
|
||||
import useComponentPermission from 'hooks/useComponentPermission';
|
||||
import { useIsDarkMode } from 'hooks/useDarkMode';
|
||||
import { useSafeNavigate } from 'hooks/useSafeNavigate';
|
||||
@@ -32,9 +31,11 @@ import {
|
||||
} from 'lucide-react';
|
||||
import { useAppContext } from 'providers/App/App';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { useDashboardVariables } from 'providers/Dashboard/store/useDashboardVariables';
|
||||
import { useWidgetsByDynamicVariableId } from 'providers/Dashboard/store/useWidgetsByDynamicVariableId';
|
||||
import { sortLayout } from 'providers/Dashboard/util';
|
||||
import { UpdateTimeInterval } from 'store/actions';
|
||||
import { IDashboardVariable, Widgets } from 'types/api/dashboard/getAll';
|
||||
import { Widgets } from 'types/api/dashboard/getAll';
|
||||
import { Props } from 'types/api/dashboard/update';
|
||||
import { ROLES, USER_ROLES } from 'types/roles';
|
||||
import { ComponentTypes } from 'utils/permission';
|
||||
@@ -79,7 +80,9 @@ function GraphLayout(props: GraphLayoutProps): JSX.Element {
|
||||
const { pathname } = useLocation();
|
||||
const dispatch = useDispatch();
|
||||
|
||||
const { widgets, variables } = data || {};
|
||||
const { widgets } = data || {};
|
||||
|
||||
const { dashboardVariables } = useDashboardVariables();
|
||||
|
||||
const { user } = useAppContext();
|
||||
|
||||
@@ -99,21 +102,7 @@ function GraphLayout(props: GraphLayoutProps): JSX.Element {
|
||||
Record<string, { widgets: Layout[]; collapsed: boolean }>
|
||||
>({});
|
||||
|
||||
const widgetsHavingDynamicVariables = useMemo(() => {
|
||||
const dynamicVariables = Object.values(
|
||||
selectedDashboard?.data?.variables || {},
|
||||
)?.filter((variable: IDashboardVariable) => variable.type === 'DYNAMIC');
|
||||
|
||||
const widgets =
|
||||
selectedDashboard?.data?.widgets?.filter(
|
||||
(widget) => widget.panelTypes !== PANEL_GROUP_TYPES.ROW,
|
||||
) || [];
|
||||
|
||||
return createDynamicVariableToWidgetsMap(
|
||||
dynamicVariables,
|
||||
widgets as Widgets[],
|
||||
);
|
||||
}, [selectedDashboard]);
|
||||
const widgetsByDynamicVariableId = useWidgetsByDynamicVariableId();
|
||||
|
||||
useEffect(() => {
|
||||
setCurrentPanelMap(panelMap);
|
||||
@@ -178,11 +167,11 @@ function GraphLayout(props: GraphLayoutProps): JSX.Element {
|
||||
dashboardId: selectedDashboard?.id,
|
||||
dashboardName: data.title,
|
||||
numberOfPanels: data.widgets?.length,
|
||||
numberOfVariables: Object.keys(data?.variables || {}).length || 0,
|
||||
numberOfVariables: Object.keys(dashboardVariables).length || 0,
|
||||
});
|
||||
logEventCalledRef.current = true;
|
||||
}
|
||||
}, [data, selectedDashboard?.id]);
|
||||
}, [dashboardVariables, data, selectedDashboard?.id]);
|
||||
|
||||
const onSaveHandler = (): void => {
|
||||
if (!selectedDashboard) {
|
||||
@@ -622,13 +611,13 @@ function GraphLayout(props: GraphLayoutProps): JSX.Element {
|
||||
<GridCard
|
||||
widget={(currentWidget as Widgets) || ({ id, query: {} } as Widgets)}
|
||||
headerMenuList={widgetActions}
|
||||
variables={variables}
|
||||
variables={dashboardVariables}
|
||||
// version={selectedDashboard?.data?.version}
|
||||
version={ENTITY_VERSION_V5}
|
||||
onDragSelect={onDragSelect}
|
||||
dataAvailable={checkIfDataExists}
|
||||
enableDrillDown={enableDrillDown}
|
||||
widgetsHavingDynamicVariables={widgetsHavingDynamicVariables}
|
||||
widgetsByDynamicVariableId={widgetsByDynamicVariableId}
|
||||
/>
|
||||
</Card>
|
||||
</CardContainer>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import { useMutation } from 'react-query';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { getSubstituteVars } from 'api/dashboard/substitute_vars';
|
||||
@@ -7,9 +7,8 @@ import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { timePreferenceType } from 'container/NewWidget/RightContainer/timeItems';
|
||||
import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables';
|
||||
import { mapQueryDataFromApi } from 'lib/newQueryBuilder/queryBuilderMappers/mapQueryDataFromApi';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { useDashboardVariablesByType } from 'providers/Dashboard/store/useDashboardVariablesByType';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { IDashboardVariable } from 'types/api/dashboard/getAll';
|
||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { getGraphType } from 'utils/getGraphType';
|
||||
@@ -36,14 +35,9 @@ function useUpdatedQuery(): UseUpdatedQueryResult {
|
||||
|
||||
const queryRangeMutation = useMutation(getSubstituteVars);
|
||||
|
||||
const { selectedDashboard } = useDashboard();
|
||||
|
||||
const dynamicVariables = useMemo(
|
||||
() =>
|
||||
Object.values(selectedDashboard?.data?.variables || {})?.filter(
|
||||
(variable: IDashboardVariable) => variable.type === 'DYNAMIC',
|
||||
),
|
||||
[selectedDashboard],
|
||||
const dashboardDynamicVariables = useDashboardVariablesByType(
|
||||
'DYNAMIC',
|
||||
'values',
|
||||
);
|
||||
|
||||
const getUpdatedQuery = useCallback(
|
||||
@@ -59,7 +53,7 @@ function useUpdatedQuery(): UseUpdatedQueryResult {
|
||||
globalSelectedInterval,
|
||||
variables: getDashboardVariables(selectedDashboard?.data?.variables),
|
||||
originalGraphType: widgetConfig.panelTypes,
|
||||
dynamicVariables,
|
||||
dynamicVariables: dashboardDynamicVariables,
|
||||
});
|
||||
|
||||
// Execute query and process results
|
||||
@@ -68,7 +62,7 @@ function useUpdatedQuery(): UseUpdatedQueryResult {
|
||||
// Map query data from API response
|
||||
return mapQueryDataFromApi(queryResult.data.compositeQuery);
|
||||
},
|
||||
[dynamicVariables, globalSelectedInterval, queryRangeMutation],
|
||||
[dashboardDynamicVariables, globalSelectedInterval, queryRangeMutation],
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
@@ -39,8 +39,10 @@ import cx from 'classnames';
|
||||
import { ENTITY_VERSION_V5 } from 'constants/app';
|
||||
import { DATE_TIME_FORMATS } from 'constants/dateTimeFormats';
|
||||
import ROUTES from 'constants/routes';
|
||||
import { sanitizeDashboardData } from 'container/DashboardContainer/DashboardDescription';
|
||||
import { downloadObjectAsJson } from 'container/DashboardContainer/DashboardDescription/utils';
|
||||
import {
|
||||
downloadObjectAsJson,
|
||||
sanitizeDashboardData,
|
||||
} from 'container/DashboardContainer/DashboardDescription/utils';
|
||||
import { Base64Icons } from 'container/DashboardContainer/DashboardSettings/General/utils';
|
||||
import dayjs from 'dayjs';
|
||||
import { useGetAllDashboard } from 'hooks/dashboard/useGetAllDashboard';
|
||||
|
||||
@@ -35,7 +35,7 @@ import {
|
||||
Spline,
|
||||
SquareArrowOutUpRight,
|
||||
} from 'lucide-react';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { useDashboardVariables } from 'providers/Dashboard/store/useDashboardVariables';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
import {
|
||||
ColumnUnit,
|
||||
@@ -131,7 +131,7 @@ function RightContainer({
|
||||
enableDrillDown = false,
|
||||
isNewDashboard,
|
||||
}: RightContainerProps): JSX.Element {
|
||||
const { selectedDashboard } = useDashboard();
|
||||
const { dashboardVariables } = useDashboardVariables();
|
||||
const [inputValue, setInputValue] = useState(title);
|
||||
const [autoCompleteOpen, setAutoCompleteOpen] = useState(false);
|
||||
const [cursorPos, setCursorPos] = useState(0);
|
||||
@@ -173,16 +173,12 @@ function RightContainer({
|
||||
|
||||
const [graphTypes, setGraphTypes] = useState<ItemsProps[]>(GraphTypes);
|
||||
|
||||
// Get dashboard variables
|
||||
const dashboardVariables = useMemo<VariableOption[]>(() => {
|
||||
if (!selectedDashboard?.data?.variables) {
|
||||
return [];
|
||||
}
|
||||
return Object.entries(selectedDashboard.data.variables).map(([, value]) => ({
|
||||
const dashboardVariableOptions = useMemo<VariableOption[]>(() => {
|
||||
return Object.entries(dashboardVariables).map(([, value]) => ({
|
||||
value: value.name || '',
|
||||
label: value.name || '',
|
||||
}));
|
||||
}, [selectedDashboard?.data?.variables]);
|
||||
}, [dashboardVariables]);
|
||||
|
||||
const updateCursorAndDropdown = (value: string, pos: number): void => {
|
||||
setCursorPos(pos);
|
||||
@@ -274,7 +270,7 @@ function RightContainer({
|
||||
<section className="name-description">
|
||||
<Typography.Text className="typography">Name</Typography.Text>
|
||||
<AutoComplete
|
||||
options={dashboardVariables}
|
||||
options={dashboardVariableOptions}
|
||||
value={inputValue}
|
||||
onChange={onInputChange}
|
||||
onSelect={onSelect}
|
||||
|
||||
@@ -72,6 +72,7 @@ import {
|
||||
placeWidgetAtBottom,
|
||||
placeWidgetBetweenRows,
|
||||
} from './utils';
|
||||
import { useDashboardVariables } from 'providers/Dashboard/store/useDashboardVariables';
|
||||
|
||||
import './NewWidget.styles.scss';
|
||||
|
||||
@@ -89,6 +90,8 @@ function NewWidget({
|
||||
columnWidths,
|
||||
} = useDashboard();
|
||||
|
||||
const { dashboardVariables } = useDashboardVariables();
|
||||
|
||||
const { t } = useTranslation(['dashboard']);
|
||||
|
||||
const { registerShortcut, deregisterShortcut } = useKeyboardHotkeys();
|
||||
@@ -377,7 +380,7 @@ function NewWidget({
|
||||
graphType: PANEL_TYPES.LIST,
|
||||
selectedTime: selectedTime.enum || 'GLOBAL_TIME',
|
||||
globalSelectedInterval: customGlobalSelectedInterval,
|
||||
variables: getDashboardVariables(selectedDashboard?.data.variables),
|
||||
variables: getDashboardVariables(dashboardVariables),
|
||||
tableParams: {
|
||||
pagination: {
|
||||
offset: 0,
|
||||
@@ -394,7 +397,7 @@ function NewWidget({
|
||||
formatForWeb:
|
||||
getGraphTypeForFormat(selectedGraph || selectedWidget.panelTypes) ===
|
||||
PANEL_TYPES.TABLE,
|
||||
variables: getDashboardVariables(selectedDashboard?.data.variables),
|
||||
variables: getDashboardVariables(dashboardVariables),
|
||||
originalGraphType: selectedGraph || selectedWidget?.panelTypes,
|
||||
};
|
||||
}
|
||||
@@ -408,7 +411,7 @@ function NewWidget({
|
||||
graphType: selectedGraph,
|
||||
selectedTime: selectedTime.enum || 'GLOBAL_TIME',
|
||||
globalSelectedInterval: customGlobalSelectedInterval,
|
||||
variables: getDashboardVariables(selectedDashboard?.data.variables),
|
||||
variables: getDashboardVariables(dashboardVariables),
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
@@ -38,9 +38,8 @@ import {
|
||||
unset,
|
||||
} from 'lodash-es';
|
||||
import { ChevronDown, ChevronUp } from 'lucide-react';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { useDashboardVariablesByType } from 'providers/Dashboard/store/useDashboardVariablesByType';
|
||||
import type { BaseSelectRef } from 'rc-select';
|
||||
import { IDashboardVariable } from 'types/api/dashboard/getAll';
|
||||
import {
|
||||
BaseAutocompleteData,
|
||||
DataTypes,
|
||||
@@ -248,14 +247,9 @@ function QueryBuilderSearchV2(
|
||||
return false;
|
||||
}, [currentState, query.aggregateAttribute?.dataType, query.dataSource]);
|
||||
|
||||
const { selectedDashboard } = useDashboard();
|
||||
|
||||
const dynamicVariables = useMemo(
|
||||
() =>
|
||||
Object.values(selectedDashboard?.data?.variables || {})?.filter(
|
||||
(variable: IDashboardVariable) => variable.type === 'DYNAMIC',
|
||||
),
|
||||
[selectedDashboard],
|
||||
const dashboardDynamicVariables = useDashboardVariablesByType(
|
||||
'DYNAMIC',
|
||||
'values',
|
||||
);
|
||||
|
||||
const { data, isFetching } = useGetAggregateKeys(
|
||||
@@ -806,7 +800,7 @@ function QueryBuilderSearchV2(
|
||||
values.push(...(attributeValues?.payload?.[key] || []));
|
||||
|
||||
// here we want to suggest the variable name matching with the key here, we will go over the dynamic variables for the keys
|
||||
const variableName = dynamicVariables?.find(
|
||||
const variableName = dashboardDynamicVariables?.find(
|
||||
(variable) =>
|
||||
variable?.dynamicVariablesAttribute === currentFilterItem?.key?.key,
|
||||
)?.name;
|
||||
@@ -837,7 +831,7 @@ function QueryBuilderSearchV2(
|
||||
suggestionsData?.payload?.attributes,
|
||||
operatorConfigKey,
|
||||
currentFilterItem?.key?.key,
|
||||
dynamicVariables,
|
||||
dashboardDynamicVariables,
|
||||
]);
|
||||
|
||||
// keep the query in sync with the selected tags in logs explorer page
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useCallback, useMemo } from 'react';
|
||||
import OverlayScrollbar from 'components/OverlayScrollbar/OverlayScrollbar';
|
||||
import { ArrowLeft, Plus, Settings, X } from 'lucide-react';
|
||||
import ContextMenu from 'periscope/components/ContextMenu';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { useDashboardVariablesByType } from 'providers/Dashboard/store/useDashboardVariablesByType';
|
||||
import { IDashboardVariable } from 'types/api/dashboard/getAll';
|
||||
// import { PANEL_TYPES } from 'constants/queryBuilder';
|
||||
import { Query } from 'types/api/queryBuilder/queryBuilderData';
|
||||
@@ -33,17 +33,9 @@ const useDashboardVarConfig = ({
|
||||
};
|
||||
// contextItems: React.ReactNode;
|
||||
} => {
|
||||
const { selectedDashboard } = useDashboard();
|
||||
const dashboardDynamicVariables = useDashboardVariablesByType('DYNAMIC');
|
||||
const { onValueUpdate, createVariable } = useDashboardVariableUpdate();
|
||||
|
||||
const dynamicDashboardVariables = useMemo(
|
||||
(): [string, IDashboardVariable][] =>
|
||||
Object.entries(selectedDashboard?.data?.variables || {}).filter(
|
||||
([, value]) => value.name && value.type === 'DYNAMIC',
|
||||
),
|
||||
[selectedDashboard],
|
||||
);
|
||||
|
||||
// Function to determine the source from query data
|
||||
const getSourceFromQuery = useCallback(():
|
||||
| 'logs'
|
||||
@@ -116,7 +108,7 @@ const useDashboardVarConfig = ({
|
||||
<>
|
||||
{' '}
|
||||
{Object.entries(fieldVariables).map(([fieldName, value]) => {
|
||||
const dashboardVar = dynamicDashboardVariables.find(
|
||||
const dashboardVar = dashboardDynamicVariables.find(
|
||||
([, dynamicValue]) =>
|
||||
dynamicValue.dynamicVariablesAttribute === fieldName,
|
||||
);
|
||||
@@ -178,7 +170,7 @@ const useDashboardVarConfig = ({
|
||||
),
|
||||
[
|
||||
fieldVariables,
|
||||
dynamicDashboardVariables,
|
||||
dashboardDynamicVariables,
|
||||
handleSetVariable,
|
||||
handleUnsetVariable,
|
||||
handleCreateVariable,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useMemo } from 'react';
|
||||
import { useSelector } from 'react-redux';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { useDashboardVariables } from 'providers/Dashboard/store/useDashboardVariables';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
|
||||
@@ -38,20 +38,17 @@ interface ResolvedTextUtilsResult {
|
||||
|
||||
function useContextVariables({
|
||||
maxValues = 2,
|
||||
// ! To be noted: This customVariables is not Dashboard Custom Variables
|
||||
customVariables,
|
||||
}: UseContextVariablesProps): UseContextVariablesResult {
|
||||
const { selectedDashboard } = useDashboard();
|
||||
const { dashboardVariables } = useDashboardVariables();
|
||||
const globalTime = useSelector<AppState, GlobalReducer>(
|
||||
(state) => state.globalTime,
|
||||
);
|
||||
|
||||
// Extract dashboard variables
|
||||
const dashboardVariables = useMemo(() => {
|
||||
if (!selectedDashboard?.data?.variables) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return Object.entries(selectedDashboard.data.variables)
|
||||
const processedDashboardVariables = useMemo(() => {
|
||||
return Object.entries(dashboardVariables)
|
||||
.filter(([, value]) => value.name)
|
||||
.map(([, value]) => {
|
||||
let processedValue: string | number | boolean;
|
||||
@@ -74,7 +71,7 @@ function useContextVariables({
|
||||
originalValue: value.selectedValue,
|
||||
};
|
||||
});
|
||||
}, [selectedDashboard]);
|
||||
}, [dashboardVariables]);
|
||||
|
||||
// Extract global variables
|
||||
const globalVariables = useMemo(
|
||||
@@ -111,8 +108,12 @@ function useContextVariables({
|
||||
|
||||
// Combine all variables
|
||||
const allVariables = useMemo(
|
||||
() => [...dashboardVariables, ...globalVariables, ...customVariablesList],
|
||||
[dashboardVariables, globalVariables, customVariablesList],
|
||||
() => [
|
||||
...processedDashboardVariables,
|
||||
...globalVariables,
|
||||
...customVariablesList,
|
||||
],
|
||||
[processedDashboardVariables, globalVariables, customVariablesList],
|
||||
);
|
||||
|
||||
// Create processed variables with truncation logic
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
// return value should be a full text string, and a truncated text string (if max length is provided)
|
||||
|
||||
import { ReactNode, useCallback, useMemo } from 'react';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { useDashboardVariables } from 'providers/Dashboard/store/useDashboardVariables';
|
||||
|
||||
interface UseGetResolvedTextProps {
|
||||
text: string | ReactNode;
|
||||
@@ -23,23 +23,15 @@ interface ResolvedTextResult {
|
||||
// eslint-disable-next-line sonarjs/cognitive-complexity
|
||||
function useGetResolvedText({
|
||||
text,
|
||||
variables,
|
||||
maxLength,
|
||||
matcher = '$',
|
||||
maxValues = 2, // Default to showing 2 values before +n more
|
||||
}: UseGetResolvedTextProps): ResolvedTextResult {
|
||||
const { selectedDashboard } = useDashboard();
|
||||
const { dashboardVariables } = useDashboardVariables();
|
||||
const isString = typeof text === 'string';
|
||||
|
||||
const processedDashboardVariables = useMemo(() => {
|
||||
if (variables) {
|
||||
return variables;
|
||||
}
|
||||
if (!selectedDashboard?.data.variables) {
|
||||
return {};
|
||||
}
|
||||
|
||||
return Object.entries(selectedDashboard.data.variables).reduce<
|
||||
return Object.entries(dashboardVariables).reduce<
|
||||
Record<string, string | number | boolean>
|
||||
>((acc, [, value]) => {
|
||||
if (!value.name) {
|
||||
@@ -54,7 +46,7 @@ function useGetResolvedText({
|
||||
}
|
||||
return acc;
|
||||
}, {});
|
||||
}, [variables, selectedDashboard?.data.variables]);
|
||||
}, [dashboardVariables]);
|
||||
|
||||
// Process array values to add +n more notation for truncated text
|
||||
const processedVariables = useMemo(() => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useCallback, useMemo } from 'react';
|
||||
import { useCallback } from 'react';
|
||||
import { useMutation } from 'react-query';
|
||||
import { useSelector } from 'react-redux';
|
||||
import logEvent from 'api/common/logEvent';
|
||||
@@ -15,8 +15,10 @@ import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariab
|
||||
import { mapQueryDataFromApi } from 'lib/newQueryBuilder/queryBuilderMappers/mapQueryDataFromApi';
|
||||
import { isEmpty } from 'lodash-es';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { useDashboardVariables } from 'providers/Dashboard/store/useDashboardVariables';
|
||||
import { useDashboardVariablesByType } from 'providers/Dashboard/store/useDashboardVariablesByType';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { IDashboardVariable, Widgets } from 'types/api/dashboard/getAll';
|
||||
import { Widgets } from 'types/api/dashboard/getAll';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { getGraphType } from 'utils/getGraphType';
|
||||
|
||||
@@ -32,12 +34,10 @@ const useCreateAlerts = (widget?: Widgets, caller?: string): VoidFunction => {
|
||||
|
||||
const { selectedDashboard } = useDashboard();
|
||||
|
||||
const dynamicVariables = useMemo(
|
||||
() =>
|
||||
Object.values(selectedDashboard?.data?.variables || {})?.filter(
|
||||
(variable: IDashboardVariable) => variable.type === 'DYNAMIC',
|
||||
),
|
||||
[selectedDashboard],
|
||||
const { dashboardVariables } = useDashboardVariables();
|
||||
const dashboardDynamicVariables = useDashboardVariablesByType(
|
||||
'DYNAMIC',
|
||||
'values',
|
||||
);
|
||||
|
||||
return useCallback(() => {
|
||||
@@ -68,9 +68,9 @@ const useCreateAlerts = (widget?: Widgets, caller?: string): VoidFunction => {
|
||||
globalSelectedInterval,
|
||||
graphType: getGraphType(widget.panelTypes),
|
||||
selectedTime: widget.timePreferance,
|
||||
variables: getDashboardVariables(selectedDashboard?.data.variables),
|
||||
variables: getDashboardVariables(dashboardVariables),
|
||||
originalGraphType: widget.panelTypes,
|
||||
dynamicVariables,
|
||||
dynamicVariables: dashboardDynamicVariables,
|
||||
});
|
||||
queryRangeMutation.mutate(queryPayload, {
|
||||
onSuccess: (data) => {
|
||||
@@ -104,10 +104,10 @@ const useCreateAlerts = (widget?: Widgets, caller?: string): VoidFunction => {
|
||||
globalSelectedInterval,
|
||||
notifications,
|
||||
queryRangeMutation,
|
||||
selectedDashboard?.data.variables,
|
||||
dashboardVariables,
|
||||
dashboardDynamicVariables,
|
||||
selectedDashboard?.data.version,
|
||||
widget,
|
||||
dynamicVariables,
|
||||
]);
|
||||
};
|
||||
|
||||
|
||||
@@ -9,9 +9,8 @@ import {
|
||||
GetQueryResultsProps,
|
||||
} from 'lib/dashboard/getQueryResults';
|
||||
import getStartEndRangeTime from 'lib/getStartEndRangeTime';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { useDashboardVariablesByType } from 'providers/Dashboard/store/useDashboardVariablesByType';
|
||||
import { SuccessResponse, Warning } from 'types/api';
|
||||
import { IDashboardVariable } from 'types/api/dashboard/getAll';
|
||||
import APIError from 'types/api/error';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
import { DataSource } from 'types/common/queryBuilder';
|
||||
@@ -43,14 +42,9 @@ export const useGetQueryRange: UseGetQueryRange = (
|
||||
headers,
|
||||
publicQueryMeta,
|
||||
) => {
|
||||
const { selectedDashboard } = useDashboard();
|
||||
|
||||
const dynamicVariables = useMemo(
|
||||
() =>
|
||||
Object.values(selectedDashboard?.data?.variables || {})?.filter(
|
||||
(variable: IDashboardVariable) => variable.type === 'DYNAMIC',
|
||||
),
|
||||
[selectedDashboard],
|
||||
const dashboardDynamicVariables = useDashboardVariablesByType(
|
||||
'DYNAMIC',
|
||||
'values',
|
||||
);
|
||||
|
||||
const newRequestData: GetQueryResultsProps = useMemo(() => {
|
||||
@@ -159,7 +153,7 @@ export const useGetQueryRange: UseGetQueryRange = (
|
||||
GetMetricQueryRange(
|
||||
modifiedRequestData,
|
||||
version,
|
||||
dynamicVariables,
|
||||
dashboardDynamicVariables,
|
||||
signal,
|
||||
headers,
|
||||
undefined,
|
||||
|
||||
@@ -4,7 +4,7 @@ import { initialQueriesMap } from 'constants/queryBuilder';
|
||||
import { REACT_QUERY_KEY } from 'constants/reactQueryKeys';
|
||||
import { getDashboardVariables } from 'lib/dashbaordVariables/getDashboardVariables';
|
||||
import { GetQueryResultsProps } from 'lib/dashboard/getQueryResults';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { useDashboardVariables } from 'providers/Dashboard/store/useDashboardVariables';
|
||||
import { AppState } from 'store/reducers';
|
||||
import { SuccessResponse } from 'types/api';
|
||||
import { MetricRangePayloadProps } from 'types/api/metrics/getQueryRange';
|
||||
@@ -28,7 +28,7 @@ export const useGetWidgetQueryRange = (
|
||||
|
||||
const { stagedQuery } = useQueryBuilder();
|
||||
|
||||
const { selectedDashboard } = useDashboard();
|
||||
const { dashboardVariables } = useDashboardVariables();
|
||||
|
||||
return useGetQueryRange(
|
||||
{
|
||||
@@ -36,7 +36,7 @@ export const useGetWidgetQueryRange = (
|
||||
selectedTime,
|
||||
globalSelectedInterval,
|
||||
query: stagedQuery || initialQueriesMap.metrics,
|
||||
variables: getDashboardVariables(selectedDashboard?.data.variables),
|
||||
variables: getDashboardVariables(dashboardVariables),
|
||||
},
|
||||
version,
|
||||
{
|
||||
|
||||
@@ -5,8 +5,7 @@ import {
|
||||
} from 'container/QueryBuilder/filters/QueryBuilderSearch/utils';
|
||||
import { Option } from 'container/QueryBuilder/type';
|
||||
import { isEmpty } from 'lodash-es';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { IDashboardVariable } from 'types/api/dashboard/getAll';
|
||||
import { useDashboardVariablesByType } from 'providers/Dashboard/store/useDashboardVariablesByType';
|
||||
import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse';
|
||||
|
||||
import { WhereClauseConfig } from './useAutoComplete';
|
||||
@@ -32,16 +31,12 @@ export const useOptions = (
|
||||
const operators = useOperators(key, keys);
|
||||
|
||||
// get matching dynamic variables to suggest
|
||||
const { selectedDashboard } = useDashboard();
|
||||
|
||||
const dynamicVariables = useMemo(
|
||||
() =>
|
||||
Object.values(selectedDashboard?.data?.variables || {})?.filter(
|
||||
(variable: IDashboardVariable) => variable.type === 'DYNAMIC',
|
||||
),
|
||||
[selectedDashboard],
|
||||
const dashboardDynamicVariables = useDashboardVariablesByType(
|
||||
'DYNAMIC',
|
||||
'values',
|
||||
);
|
||||
const variableName = dynamicVariables?.find(
|
||||
|
||||
const variableName = dashboardDynamicVariables?.find(
|
||||
(variable) => variable?.dynamicVariablesAttribute === key,
|
||||
)?.name;
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import getStartEndRangeTime from 'lib/getStartEndRangeTime';
|
||||
import { IDashboardVariables } from 'providers/Dashboard/store/dashboardVariablesStore';
|
||||
import store from 'store';
|
||||
import { Dashboard } from 'types/api/dashboard/getAll';
|
||||
|
||||
export const getDashboardVariables = (
|
||||
variables?: Dashboard['data']['variables'],
|
||||
variables?: IDashboardVariables,
|
||||
): Record<string, unknown> => {
|
||||
if (!variables) {
|
||||
return {};
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/* eslint-disable sonarjs/no-duplicate-string */
|
||||
import { MemoryRouter, useLocation } from 'react-router-dom';
|
||||
import ROUTES from 'constants/routes';
|
||||
import * as dashboardUtils from 'container/DashboardContainer/DashboardDescription';
|
||||
import { sanitizeDashboardData } from 'container/DashboardContainer/DashboardDescription/utils';
|
||||
import DashboardsList from 'container/ListOfDashboard';
|
||||
import {
|
||||
dashboardEmptyState,
|
||||
@@ -232,7 +232,7 @@ describe('dashboard list page', () => {
|
||||
expect(exportJsonBtn).toBeInTheDocument();
|
||||
fireEvent.click(exportJsonBtn);
|
||||
const firstDashboardData = dashboardSuccessResponse.data[0];
|
||||
expect(dashboardUtils.sanitizeDashboardData).toHaveBeenCalledWith(
|
||||
expect(sanitizeDashboardData).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
title: firstDashboardData.data.title,
|
||||
createdAt: firstDashboardData.createdAt,
|
||||
|
||||
@@ -45,6 +45,8 @@ import APIError from 'types/api/error';
|
||||
import { GlobalReducer } from 'types/reducer/globalTime';
|
||||
import { v4 as generateUUID } from 'uuid';
|
||||
|
||||
import { updateDashboardVariablesStore } from './store/dashboardVariablesStore';
|
||||
import { useDashboardVariables } from './store/useDashboardVariables';
|
||||
import {
|
||||
DashboardSortOrder,
|
||||
IDashboardContext,
|
||||
@@ -196,6 +198,16 @@ export function DashboardProvider({
|
||||
: isDashboardWidgetPage?.params.dashboardId) || '';
|
||||
|
||||
const [selectedDashboard, setSelectedDashboard] = useState<Dashboard>();
|
||||
const dashboardVariables = useDashboardVariables();
|
||||
|
||||
useEffect(() => {
|
||||
const existingVariables = dashboardVariables;
|
||||
const updatedVariables = selectedDashboard?.data.variables || {};
|
||||
|
||||
if (!isEqual(existingVariables, updatedVariables)) {
|
||||
updateDashboardVariablesStore(updatedVariables);
|
||||
}
|
||||
}, [selectedDashboard]);
|
||||
|
||||
const {
|
||||
currentDashboard,
|
||||
|
||||
@@ -10,6 +10,7 @@ import { IDashboardVariable } from 'types/api/dashboard/getAll';
|
||||
|
||||
import { initializeDefaultVariables } from '../initializeDefaultVariables';
|
||||
import { normalizeUrlValueForVariable } from '../normalizeUrlValue';
|
||||
import { useDashboardVariables } from '../store/useDashboardVariables';
|
||||
|
||||
// Mock the dashboard API
|
||||
jest.mock('api/v1/dashboards/id/get');
|
||||
@@ -55,6 +56,7 @@ jest.mock('uuid', () => ({ v4: jest.fn(() => 'mock-uuid') }));
|
||||
|
||||
function TestComponent(): JSX.Element {
|
||||
const { dashboardResponse, dashboardId, selectedDashboard } = useDashboard();
|
||||
const { dashboardVariables } = useDashboardVariables();
|
||||
|
||||
return (
|
||||
<div>
|
||||
@@ -65,9 +67,7 @@ function TestComponent(): JSX.Element {
|
||||
{dashboardResponse.isFetching.toString()}
|
||||
</div>
|
||||
<div data-testid="dashboard-variables">
|
||||
{selectedDashboard?.data?.variables
|
||||
? JSON.stringify(selectedDashboard.data.variables)
|
||||
: 'null'}
|
||||
{dashboardVariables ? JSON.stringify(dashboardVariables) : 'null'}
|
||||
</div>
|
||||
<div data-testid="dashboard-data">
|
||||
{selectedDashboard?.data?.title || 'No Title'}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { IDashboardVariable } from 'types/api/dashboard/getAll';
|
||||
|
||||
import createStore from './store';
|
||||
|
||||
// export type IDashboardVariables = DashboardData['variables'];
|
||||
export type IDashboardVariables = Record<string, IDashboardVariable>;
|
||||
|
||||
export const dashboardVariablesStore = createStore<IDashboardVariables>({});
|
||||
|
||||
export function updateDashboardVariablesStore(
|
||||
variables: Partial<IDashboardVariables>,
|
||||
): void {
|
||||
dashboardVariablesStore.update((currentVariables) => ({
|
||||
...currentVariables,
|
||||
...variables,
|
||||
}));
|
||||
}
|
||||
44
frontend/src/providers/Dashboard/store/store.ts
Normal file
44
frontend/src/providers/Dashboard/store/store.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { produce } from 'immer';
|
||||
type ListenerFn = () => void;
|
||||
|
||||
export default function createStore<T>(
|
||||
init: T,
|
||||
): {
|
||||
set: (setter: any) => void;
|
||||
update: (updater: (draft: T) => void) => void;
|
||||
subscribe: (listener: ListenerFn) => () => void;
|
||||
getSnapshot: () => T;
|
||||
} {
|
||||
let listeners: ListenerFn[] = [];
|
||||
let state = init;
|
||||
|
||||
function emitChange(): void {
|
||||
for (const listener of listeners) {
|
||||
listener();
|
||||
}
|
||||
}
|
||||
|
||||
function set(setter: any): void {
|
||||
state = produce(state, setter);
|
||||
emitChange();
|
||||
}
|
||||
|
||||
function update(updater: (draft: T) => void): void {
|
||||
state = produce(state, updater);
|
||||
emitChange();
|
||||
}
|
||||
|
||||
return {
|
||||
set,
|
||||
update,
|
||||
subscribe(listener: ListenerFn): () => void {
|
||||
listeners = [...listeners, listener];
|
||||
return (): void => {
|
||||
listeners = listeners.filter((l) => l !== listener);
|
||||
};
|
||||
},
|
||||
getSnapshot(): T {
|
||||
return state;
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import { useSyncExternalStore } from 'react';
|
||||
|
||||
import {
|
||||
dashboardVariablesStore,
|
||||
IDashboardVariables,
|
||||
} from './dashboardVariablesStore';
|
||||
|
||||
export const useDashboardVariables = (): {
|
||||
dashboardVariables: IDashboardVariables;
|
||||
} => {
|
||||
const dashboardVariables = useSyncExternalStore(
|
||||
dashboardVariablesStore.subscribe,
|
||||
dashboardVariablesStore.getSnapshot,
|
||||
);
|
||||
|
||||
return {
|
||||
dashboardVariables,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
import {
|
||||
IDashboardVariable,
|
||||
TVariableQueryType,
|
||||
} from 'types/api/dashboard/getAll';
|
||||
import { useDashboardVariables } from './useDashboardVariables';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export function useDashboardVariablesByType(
|
||||
variableType: TVariableQueryType,
|
||||
returnType: 'values',
|
||||
): IDashboardVariable[];
|
||||
export function useDashboardVariablesByType(
|
||||
variableType: TVariableQueryType,
|
||||
returnType?: 'entries',
|
||||
): [string, IDashboardVariable][];
|
||||
export function useDashboardVariablesByType(
|
||||
variableType: TVariableQueryType,
|
||||
returnType?: 'values' | 'entries',
|
||||
): IDashboardVariable[] | [string, IDashboardVariable][] {
|
||||
const { dashboardVariables } = useDashboardVariables();
|
||||
|
||||
return useMemo(() => {
|
||||
const entries = Object.entries(dashboardVariables || {}).filter(
|
||||
(entry): entry is [string, IDashboardVariable] =>
|
||||
Boolean(entry[1].name) && entry[1].type === variableType,
|
||||
);
|
||||
return returnType === 'values' ? entries.map(([, value]) => value) : entries;
|
||||
}, [dashboardVariables, variableType, returnType]);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import { useMemo } from 'react';
|
||||
import { PANEL_GROUP_TYPES } from 'constants/queryBuilder';
|
||||
import { createDynamicVariableToWidgetsMap } from 'hooks/dashboard/utils';
|
||||
import { useDashboard } from 'providers/Dashboard/Dashboard';
|
||||
import { Widgets } from 'types/api/dashboard/getAll';
|
||||
|
||||
import { useDashboardVariablesByType } from './useDashboardVariablesByType';
|
||||
|
||||
/**
|
||||
* Hook to get a map of dynamic variable IDs to widget IDs that use them.
|
||||
* This is useful for determining which widgets need to be refreshed when a dynamic variable changes.
|
||||
*/
|
||||
export function useWidgetsByDynamicVariableId(): Record<string, string[]> {
|
||||
const dynamicVariables = useDashboardVariablesByType('DYNAMIC', 'values');
|
||||
const { selectedDashboard } = useDashboard();
|
||||
|
||||
return useMemo(() => {
|
||||
const widgets =
|
||||
selectedDashboard?.data?.widgets?.filter(
|
||||
(widget) => widget.panelTypes !== PANEL_GROUP_TYPES.ROW,
|
||||
) || [];
|
||||
|
||||
return createDynamicVariableToWidgetsMap(
|
||||
dynamicVariables,
|
||||
widgets as Widgets[],
|
||||
);
|
||||
}, [selectedDashboard, dynamicVariables]);
|
||||
}
|
||||
@@ -12030,6 +12030,11 @@ immediate@~3.0.5:
|
||||
resolved "https://registry.yarnpkg.com/immediate/-/immediate-3.0.6.tgz#9db1dbd0faf8de6fbe0f5dd5e56bb606280de69b"
|
||||
integrity sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==
|
||||
|
||||
immer@11.1.3:
|
||||
version "11.1.3"
|
||||
resolved "https://registry.yarnpkg.com/immer/-/immer-11.1.3.tgz#78681e1deb6cec39753acf04eb16d7576c04f4d6"
|
||||
integrity sha512-6jQTc5z0KJFtr1UgFpIL3N9XSC3saRaI9PwWtzM2pSqkNGtiNkYY2OSwkOGDK2XcTRcLb1pi/aNkKZz0nxVH4Q==
|
||||
|
||||
immer@^9.0.6:
|
||||
version "9.0.21"
|
||||
resolved "https://registry.yarnpkg.com/immer/-/immer-9.0.21.tgz#1e025ea31a40f24fb064f1fef23e931496330176"
|
||||
|
||||
Reference in New Issue
Block a user