diff --git a/frontend/src/container/NewDashboard/DashboardSettings/Variables/VariableItem/VariableItem.tsx b/frontend/src/container/NewDashboard/DashboardSettings/Variables/VariableItem/VariableItem.tsx index 72d2238becab..d309f4e9d8ea 100644 --- a/frontend/src/container/NewDashboard/DashboardSettings/Variables/VariableItem/VariableItem.tsx +++ b/frontend/src/container/NewDashboard/DashboardSettings/Variables/VariableItem/VariableItem.tsx @@ -42,6 +42,7 @@ import { variablePropsToPayloadVariables } from '../../../utils'; import { TVariableMode } from '../types'; import DynamicVariable from './DynamicVariable/DynamicVariable'; import { LabelContainer, VariableItemRow } from './styles'; +import { WidgetSelector } from './WidgetSelector'; const { Option } = Select; @@ -139,6 +140,14 @@ function VariableItem({ endUnixMilli: maxTime, }); + const [selectedWidgets, setSelectedWidgets] = useState([]); + + useEffect(() => { + if (queryType === 'DYNAMIC') { + setSelectedWidgets(variableData?.dynamicVariablesWidgetIds || []); + } + }, [queryType, variableData?.dynamicVariablesWidgetIds]); + useEffect(() => { if (queryType === 'CUSTOM') { setPreviewValues( @@ -208,6 +217,10 @@ function VariableItem({ dynamicVariablesAttribute: dynamicVariablesSelectedValue?.name, dynamicVariablesSource: dynamicVariablesSelectedValue?.value, }), + ...(queryType === 'DYNAMIC' && { + dynamicVariablesWidgetIds: + selectedWidgets?.length > 0 ? selectedWidgets : [], + }), }; const allVariables = [...Object.values(existingVariables), newVariable]; @@ -582,6 +595,19 @@ function VariableItem({ )} + {queryType === 'DYNAMIC' && ( + + + + Select Widgets to apply + + + + + )}
diff --git a/frontend/src/container/NewDashboard/DashboardSettings/Variables/VariableItem/WidgetSelector.tsx b/frontend/src/container/NewDashboard/DashboardSettings/Variables/VariableItem/WidgetSelector.tsx new file mode 100644 index 000000000000..70b38d93ff84 --- /dev/null +++ b/frontend/src/container/NewDashboard/DashboardSettings/Variables/VariableItem/WidgetSelector.tsx @@ -0,0 +1,28 @@ +import { CustomMultiSelect } from 'components/NewSelect'; +import { generateGridTitle } from 'container/GridPanelSwitch/utils'; +import { useDashboard } from 'providers/Dashboard/Dashboard'; + +export function WidgetSelector({ + selectedWidgets, + setSelectedWidgets, +}: { + selectedWidgets: string[]; + setSelectedWidgets: (widgets: string[]) => void; +}): JSX.Element { + const { selectedDashboard } = useDashboard(); + + const widgets = selectedDashboard?.data?.widgets || []; + + return ( + ({ + label: generateGridTitle(widget.title), + value: widget.id, + }))} + value={selectedWidgets} + labelInValue + onChange={(value): void => setSelectedWidgets(value as string[])} + /> + ); +} diff --git a/frontend/src/hooks/dashboard/useAddTagFiltersToDashboard.tsx b/frontend/src/container/NewDashboard/DashboardSettings/Variables/addTagFiltersToDashboard.tsx similarity index 57% rename from frontend/src/hooks/dashboard/useAddTagFiltersToDashboard.tsx rename to frontend/src/container/NewDashboard/DashboardSettings/Variables/addTagFiltersToDashboard.tsx index 07e9d07cddbf..8fff91785409 100644 --- a/frontend/src/hooks/dashboard/useAddTagFiltersToDashboard.tsx +++ b/frontend/src/container/NewDashboard/DashboardSettings/Variables/addTagFiltersToDashboard.tsx @@ -1,5 +1,4 @@ import { cloneDeep } from 'lodash-es'; -import { useMemo } from 'react'; import { Dashboard, Widgets } from 'types/api/dashboard/getAll'; import { IBuilderQuery, @@ -16,7 +15,8 @@ const updateQueryFilters = ( ...queryData, filters: { ...queryData.filters, - items: [...queryData.filters.items, ...filters], + items: [...(queryData.filters?.items || []), ...filters], + op: queryData.filters?.op || 'AND', }, }); @@ -46,7 +46,7 @@ const updateSingleWidget = ( }; /** - * A hook that takes a dashboard configuration and a list of tag filters + * A function that takes a dashboard configuration and a list of tag filters * and returns an updated dashboard with the filters appended to widget queries. * * @param dashboard The dashboard configuration @@ -54,35 +54,34 @@ const updateSingleWidget = ( * @param widgetIds Optional array of widget IDs to filter which widgets get updated * @returns Updated dashboard configuration with filters applied */ -export const useAddTagFiltersToDashboard = ( +export const addTagFiltersToDashboard = ( dashboard: Dashboard | undefined, filters: TagFilterItem[], widgetIds?: string[], -): Dashboard | undefined => - useMemo(() => { - if (!dashboard || !filters.length) { - return dashboard; - } +): Dashboard | undefined => { + if (!dashboard || !filters.length) { + return dashboard; + } - // Create a deep copy to avoid mutating the original dashboard - const updatedDashboard = cloneDeep(dashboard); + // Create a deep copy to avoid mutating the original dashboard + const updatedDashboard = cloneDeep(dashboard); - // Process each widget to add filters - if (updatedDashboard.data.widgets) { - updatedDashboard.data.widgets = updatedDashboard.data.widgets.map( - (widget) => { - // Only apply to widgets with 'query' property - if ('query' in widget) { - // If widgetIds is provided, only update widgets with matching IDs - if (widgetIds && !widgetIds.includes(widget.id)) { - return widget; - } - return updateSingleWidget(widget as Widgets, filters); + // Process each widget to add filters + if (updatedDashboard.data.widgets) { + updatedDashboard.data.widgets = updatedDashboard.data.widgets.map( + (widget) => { + // Only apply to widgets with 'query' property + if ('query' in widget) { + // If widgetIds is provided, only update widgets with matching IDs + if (widgetIds && !widgetIds.includes(widget.id)) { + return widget; } - return widget; - }, - ); - } + return updateSingleWidget(widget as Widgets, filters); + } + return widget; + }, + ); + } - return updatedDashboard; - }, [dashboard, filters, widgetIds]); + return updatedDashboard; +}; diff --git a/frontend/src/container/NewDashboard/DashboardSettings/Variables/index.tsx b/frontend/src/container/NewDashboard/DashboardSettings/Variables/index.tsx index 19c79868d397..4a4686175b4d 100644 --- a/frontend/src/container/NewDashboard/DashboardSettings/Variables/index.tsx +++ b/frontend/src/container/NewDashboard/DashboardSettings/Variables/index.tsx @@ -15,6 +15,7 @@ import { CSS } from '@dnd-kit/utilities'; import { Button, Modal, Row, Space, Table, Typography } from 'antd'; import { RowProps } from 'antd/lib'; import { convertVariablesToDbFormat } from 'container/NewDashboard/DashboardVariablesSelection/util'; +import { useAddDynamicVariableToPanels } from 'hooks/dashboard/useAddDynamicVariableToPanels'; import { useUpdateDashboard } from 'hooks/dashboard/useUpdateDashboard'; import { useNotifications } from 'hooks/useNotifications'; import { PenLine, Trash2 } from 'lucide-react'; @@ -162,19 +163,30 @@ function VariablesSetting({ setExistingVariableNamesMap(variableNamesMap); }, [variables]); + const addDynamicVariableToPanels = useAddDynamicVariableToPanels(); + const updateVariables = ( updatedVariablesData: Dashboard['data']['variables'], + currentRequestedId?: string, ): void => { if (!selectedDashboard) { return; } + const newDashboard = + (currentRequestedId && + addDynamicVariableToPanels( + selectedDashboard, + updatedVariablesData[currentRequestedId || ''], + )) || + selectedDashboard; + updateMutation.mutateAsync( { id: selectedDashboard.id, data: { - ...selectedDashboard.data, + ...newDashboard.data, variables: updatedVariablesData, }, }, @@ -225,7 +237,7 @@ function VariablesSetting({ const variables = convertVariablesToDbFormat(newVariablesArr); setVariablesTableData(newVariablesArr); - updateVariables(variables); + updateVariables(variables, variableData?.id); onDoneVariableViewMode(); }; diff --git a/frontend/src/hooks/dashboard/useAddDynamicVariableToPanels.tsx b/frontend/src/hooks/dashboard/useAddDynamicVariableToPanels.tsx index 9092bfe312a4..3f9086e69b81 100644 --- a/frontend/src/hooks/dashboard/useAddDynamicVariableToPanels.tsx +++ b/frontend/src/hooks/dashboard/useAddDynamicVariableToPanels.tsx @@ -1,57 +1,43 @@ -import { useMemo } from 'react'; -import { Dashboard } from 'types/api/dashboard/getAll'; -import { BaseAutocompleteData } from 'types/api/queryBuilder/queryAutocompleteResponse'; +import { getFiltersFromKeyValue } from 'pages/Celery/CeleryOverview/CeleryOverviewUtils'; +import { useCallback } from 'react'; +import { Dashboard, IDashboardVariable } from 'types/api/dashboard/getAll'; import { TagFilterItem } from 'types/api/queryBuilder/queryBuilderData'; -import { v4 as uuid } from 'uuid'; -import { useAddTagFiltersToDashboard } from './useAddTagFiltersToDashboard'; - -interface DynamicVariableConfig { - variableName: string; - tagKey: { - key: string; - dataType: string; - isColumn: boolean; - isJSON?: boolean; - type: string; - }; - operator: string; -} +import { addTagFiltersToDashboard } from '../../container/NewDashboard/DashboardSettings/Variables/addTagFiltersToDashboard'; /** - * A hook that adds dynamic variables to dashboard panels as tag filters + * A hook that returns a function to add dynamic variables to dashboard panels as tag filters. * - * @param dashboard The dashboard configuration - * @param variableConfig Configuration for the dynamic variable to add - * @param widgetIds Optional array of widget IDs to target specific widgets - * @returns Updated dashboard with dynamic variables added as filters + * @returns A function that, when given a dashboard and variable config, returns the updated dashboard. */ -export const useAddDynamicVariableToPanels = ( +export const useAddDynamicVariableToPanels = (): (( dashboard: Dashboard | undefined, - variableConfig: DynamicVariableConfig, - widgetIds?: string[], -): Dashboard | undefined => { - // Create the tag filter based on the variable configuration - const tagFilters = useMemo((): TagFilterItem[] => { - if (!variableConfig) { - return []; - } + variableConfig: IDashboardVariable, +) => Dashboard | undefined) => + useCallback( + ( + dashboard: Dashboard | undefined, + variableConfig: IDashboardVariable, + ): Dashboard | undefined => { + if (!variableConfig) return dashboard; - const { variableName, tagKey, operator } = variableConfig; + const { + dynamicVariablesAttribute, + name, + dynamicVariablesWidgetIds, + } = variableConfig; - // Create a TagFilterItem that uses the variable as the value - const filter: TagFilterItem = { - id: uuid().slice(0, 8), - key: tagKey as BaseAutocompleteData, - op: operator, - value: `$${variableName}`, - }; + const tagFilters: TagFilterItem[] = [ + getFiltersFromKeyValue(dynamicVariablesAttribute || '', `$${name}`), + ]; - return [filter]; - }, [variableConfig]); - - // Use the existing hook to add these filters to the dashboard - return useAddTagFiltersToDashboard(dashboard, tagFilters, widgetIds); -}; + return addTagFiltersToDashboard( + dashboard, + tagFilters, + dynamicVariablesWidgetIds, + ); + }, + [], + ); export default useAddDynamicVariableToPanels; diff --git a/frontend/src/types/api/dashboard/getAll.ts b/frontend/src/types/api/dashboard/getAll.ts index 0318ecf60f9c..d34db2457f56 100644 --- a/frontend/src/types/api/dashboard/getAll.ts +++ b/frontend/src/types/api/dashboard/getAll.ts @@ -55,6 +55,7 @@ export interface IDashboardVariable { dynamicVariablesAttribute?: string; dynamicVariablesSource?: string; haveCustomValuesSelected?: boolean; + dynamicVariablesWidgetIds?: string[]; } export interface Dashboard { id: string;