From abb54f4dbb6d2d86115ab0ed884eb60dbb470e16 Mon Sep 17 00:00:00 2001 From: Nate Kelley Date: Thu, 20 Mar 2025 14:13:21 -0600 Subject: [PATCH] add metrics to dashboard tests --- .../helpers/addMetricToDashboard.ts | 51 +++++ .../addMetricToDashboardConfig.test.ts | 174 ++++++++++++++++++ .../buster_rest/dashboards/queryRequests.ts | 108 ++++++++--- .../buster_rest/datasource/queryRequests.ts | 2 +- .../api/buster_rest/metrics/queryRequests.ts | 79 -------- .../PermissionDatasetGroupSelectedPopup.tsx | 9 +- .../[datasourceId]/_forms/PostgresForm.tsx | 10 +- .../SaveDashboardToCollectionButton.tsx | 4 +- .../buttons/SaveMetricToCollectionButton.tsx | 2 +- .../ChatItemsSelectedPopup.tsx | 10 +- .../DashboardContentController.tsx | 2 +- .../DashboardSelectedPopup.tsx | 4 +- .../MetricItemsSelectedPopup.tsx | 10 +- .../DashboardThreeDotMenu.tsx | 4 +- 14 files changed, 327 insertions(+), 142 deletions(-) create mode 100644 web/src/api/buster_rest/dashboards/helpers/addMetricToDashboard.ts create mode 100644 web/src/api/buster_rest/dashboards/helpers/addMetricToDashboardConfig.test.ts diff --git a/web/src/api/buster_rest/dashboards/helpers/addMetricToDashboard.ts b/web/src/api/buster_rest/dashboards/helpers/addMetricToDashboard.ts new file mode 100644 index 000000000..f46be2959 --- /dev/null +++ b/web/src/api/buster_rest/dashboards/helpers/addMetricToDashboard.ts @@ -0,0 +1,51 @@ +import type { BusterDashboard } from '@/api/asset_interfaces/dashboard'; +import { MAX_NUMBER_OF_ITEMS, NUMBER_OF_COLUMNS } from '@/components/ui/grid/helpers'; +import { v4 as uuidv4 } from 'uuid'; + +export const addMetricToDashboardConfig = ( + metricIds: string[], + existingConfig: BusterDashboard['config'] +) => { + // Create a new config object to avoid mutating the original + const newConfig = { + ...existingConfig, + rows: [...(existingConfig.rows || [])] + }; + + // Filter out metrics that are already in the dashboard + const newMetricIds = metricIds.filter((metricId) => { + return !newConfig.rows?.some((row) => row.items.some((item) => item.id === metricId)); + }); + + if (newMetricIds.length === 0) { + return existingConfig; + } + + // Calculate how many rows we need + const totalNewMetrics = newMetricIds.length; + const metricsPerRow = MAX_NUMBER_OF_ITEMS; + const numRowsNeeded = Math.ceil(totalNewMetrics / metricsPerRow); + + // Create new rows for the metrics + for (let i = 0; i < numRowsNeeded; i++) { + const startIdx = i * metricsPerRow; + const endIdx = Math.min(startIdx + metricsPerRow, totalNewMetrics); + const metricsInThisRow = newMetricIds.slice(startIdx, endIdx); + + // Calculate column sizes for this row - each metric gets equal width + const columnSize = NUMBER_OF_COLUMNS / metricsInThisRow.length; + const columnSizes = Array(metricsInThisRow.length).fill(columnSize); + + // Create the new row + const newRow = { + id: uuidv4(), + items: metricsInThisRow.map((id) => ({ id })), + columnSizes, + rowHeight: 320 // Default row height + }; + + newConfig.rows.push(newRow); + } + + return newConfig; +}; diff --git a/web/src/api/buster_rest/dashboards/helpers/addMetricToDashboardConfig.test.ts b/web/src/api/buster_rest/dashboards/helpers/addMetricToDashboardConfig.test.ts new file mode 100644 index 000000000..f67ff233e --- /dev/null +++ b/web/src/api/buster_rest/dashboards/helpers/addMetricToDashboardConfig.test.ts @@ -0,0 +1,174 @@ +import { addMetricToDashboardConfig } from './addMetricToDashboard'; +import type { BusterDashboard } from '@/api/asset_interfaces/dashboard'; + +describe('addMetricToDashboardConfig', () => { + const createEmptyConfig = (): BusterDashboard['config'] => ({ + rows: [] + }); + + const createConfigWithRows = ( + rows: BusterDashboard['config']['rows'] + ): BusterDashboard['config'] => ({ + rows + }); + + it('should return the same config if no new metrics are provided', () => { + const config = createEmptyConfig(); + const result = addMetricToDashboardConfig([], config); + expect(result).toBe(config); + }); + + it('should add a single metric to an empty config', () => { + const config = createEmptyConfig(); + const result = addMetricToDashboardConfig(['metric1'], config); + + expect(result.rows!).toHaveLength(1); + expect(result.rows![0].items).toHaveLength(1); + expect(result.rows![0].items[0].id).toBe('metric1'); + expect(result.rows![0].columnSizes).toEqual([12]); // Single column takes full width + }); + + it('should add multiple metrics up to MAX_NUMBER_OF_ITEMS in a single row', () => { + const config = createEmptyConfig(); + const result = addMetricToDashboardConfig(['metric1', 'metric2', 'metric3', 'metric4'], config); + + expect(result.rows!).toHaveLength(1); + expect(result.rows![0].items).toHaveLength(4); + expect(result.rows![0].columnSizes).toEqual([3, 3, 3, 3]); // 4 equal columns + }); + + it('should create multiple rows when metrics exceed MAX_NUMBER_OF_ITEMS', () => { + const config = createEmptyConfig(); + const result = addMetricToDashboardConfig( + ['metric1', 'metric2', 'metric3', 'metric4', 'metric5', 'metric6'], + config + ); + + expect(result.rows!).toHaveLength(2); + expect(result.rows![0].items).toHaveLength(4); + expect(result.rows![1].items).toHaveLength(2); + expect(result.rows![0].columnSizes).toEqual([3, 3, 3, 3]); + expect(result.rows![1].columnSizes).toEqual([6, 6]); // 2 equal columns + }); + + it('should not add duplicate metrics', () => { + const config = createConfigWithRows([ + { + id: 'row1', + items: [{ id: 'metric1' }], + columnSizes: [12], + rowHeight: 320 + } + ]); + + const result = addMetricToDashboardConfig(['metric1', 'metric2'], config); + + expect(result.rows!).toHaveLength(2); + expect(result.rows![0].items).toHaveLength(1); + expect(result.rows![1].items).toHaveLength(1); + expect(result.rows![1].items[0].id).toBe('metric2'); + }); + + it('should respect MIN_NUMBER_OF_COLUMNS when adding fewer metrics', () => { + const config = createEmptyConfig(); + const result = addMetricToDashboardConfig(['metric1', 'metric2'], config); + + expect(result.rows!).toHaveLength(1); + expect(result.rows![0].items).toHaveLength(2); + expect(result.rows![0].columnSizes).toEqual([6, 6]); // 2 equal columns + }); + + it('should respect MAX_NUMBER_OF_COLUMNS when adding many metrics', () => { + const config = createEmptyConfig(); + const result = addMetricToDashboardConfig( + [ + 'metric1', + 'metric2', + 'metric3', + 'metric4', + 'metric5', + 'metric6', + 'metric7', + 'metric8', + 'metric9', + 'metric10', + 'metric11', + 'metric12', + 'metric13' + ], + config + ); + + expect(result.rows!).toHaveLength(4); + expect(result.rows![0].items).toHaveLength(4); + expect(result.rows![1].items).toHaveLength(4); + expect(result.rows![2].items).toHaveLength(4); + expect(result.rows![3].items).toHaveLength(1); + expect(result.rows![0].columnSizes).toEqual([3, 3, 3, 3]); + expect(result.rows![3].columnSizes).toEqual([12]); // Single column for last row + }); + + it('should preserve existing rows when adding new metrics', () => { + const config = createConfigWithRows([ + { + id: 'row1', + items: [{ id: 'existing1' }], + columnSizes: [12], + rowHeight: 320 + } + ]); + + const result = addMetricToDashboardConfig(['metric1', 'metric2'], config); + + expect(result.rows!).toHaveLength(2); + expect(result.rows![0].items[0].id).toBe('existing1'); + expect(result.rows![1].items[0].id).toBe('metric1'); + expect(result.rows![1].items[1].id).toBe('metric2'); + }); + + it('should correctly distribute columns for exactly 2 metrics', () => { + const config = createEmptyConfig(); + const result = addMetricToDashboardConfig(['metric1', 'metric2'], config); + + expect(result.rows!).toHaveLength(1); + expect(result.rows![0].items).toHaveLength(2); + expect(result.rows![0].items.map((item) => item.id)).toEqual(['metric1', 'metric2']); + expect(result.rows![0].columnSizes).toEqual([6, 6]); // Two equal columns of 6 + expect(result.rows![0].columnSizes!.reduce((a, b) => a + b)).toBe(12); // Sum should be 12 + }); + + it('should correctly distribute columns for exactly 3 metrics', () => { + const config = createEmptyConfig(); + const result = addMetricToDashboardConfig(['metric1', 'metric2', 'metric3'], config); + + expect(result.rows!).toHaveLength(1); + expect(result.rows![0].items).toHaveLength(3); + expect(result.rows![0].items.map((item) => item.id)).toEqual(['metric1', 'metric2', 'metric3']); + expect(result.rows![0].columnSizes).toEqual([4, 4, 4]); // Three equal columns of 4 + expect(result.rows![0].columnSizes!.reduce((a, b) => a + b)).toBe(12); // Sum should be 12 + }); + + it('should correctly distribute columns for exactly 4 metrics', () => { + const config = createEmptyConfig(); + const result = addMetricToDashboardConfig(['metric1', 'metric2', 'metric3', 'metric4'], config); + + expect(result.rows!).toHaveLength(1); + expect(result.rows![0].items).toHaveLength(4); + expect(result.rows![0].items.map((item) => item.id)).toEqual([ + 'metric1', + 'metric2', + 'metric3', + 'metric4' + ]); + expect(result.rows![0].columnSizes).toEqual([3, 3, 3, 3]); // Four equal columns of 3 + expect(result.rows![0].columnSizes!.reduce((a, b) => a + b)).toBe(12); // Sum should be 12 + }); + + it('should correctly distribute columns for exactly 0 metrics', () => { + const config = createEmptyConfig(); + const result = addMetricToDashboardConfig([], config); + + expect(result.rows!).toHaveLength(0); + expect(result.rows).toBe(config.rows); + }); +}); diff --git a/web/src/api/buster_rest/dashboards/queryRequests.ts b/web/src/api/buster_rest/dashboards/queryRequests.ts index 4e4bd1e4f..696bb753c 100644 --- a/web/src/api/buster_rest/dashboards/queryRequests.ts +++ b/web/src/api/buster_rest/dashboards/queryRequests.ts @@ -183,8 +183,7 @@ export const useAddDashboardToCollection = () => { const mutationFn = useMemoizedFn( async (variables: { dashboardId: string; collectionIds: string[] }) => { const { dashboardId, collectionIds } = variables; - - await Promise.all( + return await Promise.all( collectionIds.map((collectionId) => addAssetToCollection({ id: dashboardId, @@ -194,7 +193,6 @@ export const useAddDashboardToCollection = () => { ); } ); - return useMutation({ mutationFn, onSuccess: (_, { collectionIds }) => { @@ -214,7 +212,7 @@ export const useRemoveDashboardFromCollection = () => { const mutationFn = useMemoizedFn( async (variables: { dashboardId: string; collectionIds: string[] }) => { const { dashboardId, collectionIds } = variables; - await Promise.all( + return await Promise.all( collectionIds.map((collectionId) => removeAssetFromCollection({ id: dashboardId, @@ -245,30 +243,6 @@ export const useRemoveDashboardFromCollection = () => { }); }; -export const useRemoveItemFromDashboard = () => { - const { mutateAsync: updateDashboardMutation } = useUpdateDashboard(); - const queryClient = useQueryClient(); - const mutationFn = useMemoizedFn( - async (variables: { dashboardId: string; metricId: string | string[] }) => { - const { dashboardId, metricId } = variables; - const options = dashboardQueryKeys.dashboardGetDashboard(dashboardId); - const prevDashboard = queryClient.getQueryData(options.queryKey); - - if (prevDashboard) { - const prevMetricsIds = Object.keys(prevDashboard?.metrics); - const newMetricsIds = prevMetricsIds?.filter((t) => !metricId.includes(t)); - console.log('TODO: remove metrics from dashboard', dashboardId, metricId); - return updateDashboardMutation({ - id: dashboardId - }); - } - } - ); - return useMutation({ - mutationFn - }); -}; - export const useShareDashboard = () => { const queryClient = useQueryClient(); return useMutation({ @@ -341,3 +315,81 @@ export const useUpdateDashboardShare = () => { } }); }; + +export const useSaveMetricsToDashboard = () => { + const queryClient = useQueryClient(); + + const saveMetricToDashboard = useMemoizedFn( + async ({ metricIds, dashboardId }: { metricIds: string[]; dashboardId: string }) => { + // await saveMetric({ + // id: metricId, + // save_to_dashboard: dashboardIds + // }); + } + ); + + return useMutation({ + mutationFn: saveMetricToDashboard, + onSuccess: (data, variables) => { + // queryClient.invalidateQueries({ + // queryKey: variables.dashboardIds.map( + // (id) => dashboardQueryKeys.dashboardGetDashboard(id).queryKey + // ) + // }); + } + }); +}; + +export const useRemoveMetricFromDashboard = () => { + const { openConfirmModal } = useBusterNotifications(); + const queryClient = useQueryClient(); + const removeMetricFromDashboard = useMemoizedFn( + async ({ + metricId, + dashboardId, + useConfirmModal = true + }: { + metricId: string; + dashboardId: string; + useConfirmModal?: boolean; + }) => { + const method = async () => { + // await saveMetric({ + // id: metricId, + // remove_from_dashboard: [dashboardId] + // }); + }; + + if (!useConfirmModal) return await method(); + + return await openConfirmModal({ + title: 'Remove from dashboard', + content: 'Are you sure you want to remove this metric from this dashboard?', + onOk: method + }); + } + ); + + return useMutation({ + mutationFn: removeMetricFromDashboard, + onMutate: async (variables) => { + const currentDashboard = queryClient.getQueryData( + dashboardQueryKeys.dashboardGetDashboard(variables.dashboardId).queryKey + ); + if (currentDashboard) { + queryClient.setQueryData( + dashboardQueryKeys.dashboardGetDashboard(variables.dashboardId).queryKey, + (currentDashboard) => { + if (currentDashboard?.dashboard.config.rows) { + currentDashboard.dashboard.config.rows.forEach((row) => { + row.items = row.items.filter((item) => item.id !== variables.metricId); + }); + } + delete currentDashboard!.metrics[variables.metricId]; + return currentDashboard; + } + ); + } + } + }); +}; diff --git a/web/src/api/buster_rest/datasource/queryRequests.ts b/web/src/api/buster_rest/datasource/queryRequests.ts index 85c3116f6..8f148ccb8 100644 --- a/web/src/api/buster_rest/datasource/queryRequests.ts +++ b/web/src/api/buster_rest/datasource/queryRequests.ts @@ -9,7 +9,7 @@ import { import { queryKeys } from '@/api/query_keys'; import { useBusterNotifications } from '@/context/BusterNotifications'; -export const useListDatasources = (enabled: boolean) => { +export const useListDatasources = (enabled: boolean = true) => { return useQuery({ ...queryKeys.datasourceGetList, queryFn: listDatasources, diff --git a/web/src/api/buster_rest/metrics/queryRequests.ts b/web/src/api/buster_rest/metrics/queryRequests.ts index df3cba3b1..38a09c4ec 100644 --- a/web/src/api/buster_rest/metrics/queryRequests.ts +++ b/web/src/api/buster_rest/metrics/queryRequests.ts @@ -247,85 +247,6 @@ export const useRemoveMetricFromCollection = () => { }); }; -export const useSaveMetricToDashboard = () => { - const queryClient = useQueryClient(); - - const saveMetricToDashboard = useMemoizedFn( - async ({ metricId, dashboardIds }: { metricId: string; dashboardIds: string[] }) => { - // await saveMetric({ - // id: metricId, - // save_to_dashboard: dashboardIds - // }); - } - ); - - return useMutation({ - mutationFn: saveMetricToDashboard, - onSuccess: (data, variables) => { - queryClient.invalidateQueries({ - queryKey: variables.dashboardIds.map( - (id) => dashboardQueryKeys.dashboardGetDashboard(id).queryKey - ) - }); - } - }); -}; - -export const useRemoveMetricFromDashboard = () => { - const { openConfirmModal } = useBusterNotifications(); - const { mutateAsync: saveMetric } = useSaveMetric(); - const queryClient = useQueryClient(); - const removeMetricFromDashboard = useMemoizedFn( - async ({ - metricId, - dashboardId, - useConfirmModal = true - }: { - metricId: string; - dashboardId: string; - useConfirmModal?: boolean; - }) => { - const method = async () => { - // await saveMetric({ - // id: metricId, - // remove_from_dashboard: [dashboardId] - // }); - }; - - if (!useConfirmModal) return await method(); - - return await openConfirmModal({ - title: 'Remove from dashboard', - content: 'Are you sure you want to remove this metric from this dashboard?', - onOk: method - }); - } - ); - - return useMutation({ - mutationFn: removeMetricFromDashboard, - onMutate: async (variables) => { - const currentDashboard = queryClient.getQueryData( - dashboardQueryKeys.dashboardGetDashboard(variables.dashboardId).queryKey - ); - if (currentDashboard) { - queryClient.setQueryData( - dashboardQueryKeys.dashboardGetDashboard(variables.dashboardId).queryKey, - (currentDashboard) => { - if (currentDashboard?.dashboard.config.rows) { - currentDashboard.dashboard.config.rows.forEach((row) => { - row.items = row.items.filter((item) => item.id !== variables.metricId); - }); - } - delete currentDashboard!.metrics[variables.metricId]; - return currentDashboard; - } - ); - } - } - }); -}; - export const useDuplicateMetric = () => { return useMutation({ mutationFn: duplicateMetric diff --git a/web/src/app/app/(primary_layout)/datasets/[datasetId]/permissions/dataset-groups/_PermissionDatasetGroups/PermissionDatasetGroupSelectedPopup.tsx b/web/src/app/app/(primary_layout)/datasets/[datasetId]/permissions/dataset-groups/_PermissionDatasetGroups/PermissionDatasetGroupSelectedPopup.tsx index 62448169c..9696018d8 100644 --- a/web/src/app/app/(primary_layout)/datasets/[datasetId]/permissions/dataset-groups/_PermissionDatasetGroups/PermissionDatasetGroupSelectedPopup.tsx +++ b/web/src/app/app/(primary_layout)/datasets/[datasetId]/permissions/dataset-groups/_PermissionDatasetGroups/PermissionDatasetGroupSelectedPopup.tsx @@ -2,13 +2,18 @@ import { BusterListSelectedOptionPopupContainer } from '@/components/ui/list'; import React from 'react'; import { PermissionAssignedButton } from '@/components/features/PermissionComponents'; import { useDatasetUpdateDatasetGroups } from '@/api/buster_rest'; +import { useMemoizedFn } from '@/hooks'; export const PermissionDatasetGroupSelectedPopup: React.FC<{ selectedRowKeys: string[]; onSelectChange: (selectedRowKeys: string[]) => void; datasetId: string; }> = React.memo(({ selectedRowKeys, onSelectChange, datasetId }) => { - const { mutateAsync: updateDatasetGroups } = useDatasetUpdateDatasetGroups(datasetId); + const { mutateAsync: updateDatasetGroups } = useDatasetUpdateDatasetGroups(); + + const onUpdate = useMemoizedFn(async (groups: { id: string; assigned: boolean }[]) => { + return updateDatasetGroups({ dataset_id: datasetId, groups }); + }); return ( ]} /> diff --git a/web/src/app/app/(settings_layout)/settings/(restricted-width)/datasources/(admin-restricted-space)/[datasourceId]/_forms/PostgresForm.tsx b/web/src/app/app/(settings_layout)/settings/(restricted-width)/datasources/(admin-restricted-space)/[datasourceId]/_forms/PostgresForm.tsx index dacad784f..fb372eb5f 100644 --- a/web/src/app/app/(settings_layout)/settings/(restricted-width)/datasources/(admin-restricted-space)/[datasourceId]/_forms/PostgresForm.tsx +++ b/web/src/app/app/(settings_layout)/settings/(restricted-width)/datasources/(admin-restricted-space)/[datasourceId]/_forms/PostgresForm.tsx @@ -1,15 +1,9 @@ import { DataSource } from '@/api/asset_interfaces'; import React, { useRef } from 'react'; -import { Input } from '@/components/ui/inputs'; -import { Select } from '@/components/ui/select'; -import { FormWrapper, FormWrapperHandle } from './FormWrapper'; +import { FormWrapperHandle } from './FormWrapper'; import { formatDate } from '@/lib'; -import { - DatasourceCreateCredentials, - PostgresCreateCredentials -} from '@/api/request_interfaces/datasources'; +import { DatasourceCreateCredentials } from '@/api/request_interfaces/datasources'; import { useHotkeys } from 'react-hotkeys-hook'; -import { TagInput } from '@/components/ui/inputs/InputTagInput'; const sshModeOptions = ['Do not use SSH credentials', 'Use SSH credentials'].map((item, index) => ({ label: item, diff --git a/web/src/components/features/buttons/SaveDashboardToCollectionButton.tsx b/web/src/components/features/buttons/SaveDashboardToCollectionButton.tsx index becd68e02..33b50ab33 100644 --- a/web/src/components/features/buttons/SaveDashboardToCollectionButton.tsx +++ b/web/src/components/features/buttons/SaveDashboardToCollectionButton.tsx @@ -27,7 +27,7 @@ export const SaveDashboardToCollectionButton: React.FC<{ dashboardIds.map((dashboardId) => { return addDashboardToCollection({ dashboardId, - collectionId: collectionIds + collectionIds: collectionIds }); }) ); @@ -40,7 +40,7 @@ export const SaveDashboardToCollectionButton: React.FC<{ dashboardIds.map((dashboardId) => { return removeDashboardFromCollection({ dashboardId, - collectionId + collectionIds: [collectionId] }); }) ); diff --git a/web/src/components/features/buttons/SaveMetricToCollectionButton.tsx b/web/src/components/features/buttons/SaveMetricToCollectionButton.tsx index 61fd6e42c..a32a593d4 100644 --- a/web/src/components/features/buttons/SaveMetricToCollectionButton.tsx +++ b/web/src/components/features/buttons/SaveMetricToCollectionButton.tsx @@ -38,7 +38,7 @@ export const SaveMetricToCollectionButton: React.FC<{ const allSelectedButLast = selectedCollections.slice(0, -1); await Promise.all( allSelectedButLast.map((metricId) => { - return removeMetricFromCollection({ metricId, collectionId }); + return removeMetricFromCollection({ metricId, collectionIds: [collectionId] }); }) ); openInfoMessage('Metrics removed from collections'); diff --git a/web/src/controllers/ChatsListController/ChatItemsSelectedPopup.tsx b/web/src/controllers/ChatsListController/ChatItemsSelectedPopup.tsx index 1370815bb..b147a91f2 100644 --- a/web/src/controllers/ChatsListController/ChatItemsSelectedPopup.tsx +++ b/web/src/controllers/ChatsListController/ChatItemsSelectedPopup.tsx @@ -85,14 +85,8 @@ const CollectionsButton: React.FC<{ const onRemoveFromCollection = useMemoizedFn(async (collectionId: string) => { setSelectedCollections((prev) => prev.filter((id) => id !== collectionId)); - const allSelectedButLast = selectedRowKeys.slice(0, -1); - const lastMetricId = selectedRowKeys[selectedRowKeys.length - 1]; - const allRemoves: Promise[] = allSelectedButLast.map((metricId) => { - return removeMetricFromCollection({ metricId, collectionId }); - }); - await removeMetricFromCollection({ - metricId: lastMetricId, - collectionId + const allRemoves: Promise[] = selectedRowKeys.map((metricId) => { + return removeMetricFromCollection({ metricId, collectionIds: [collectionId] }); }); await Promise.all(allRemoves); openInfoMessage('Metrics removed from collections'); diff --git a/web/src/controllers/DashboardController/DashboardViewDashboardController/DashboardContentController/DashboardContentController.tsx b/web/src/controllers/DashboardController/DashboardViewDashboardController/DashboardContentController/DashboardContentController.tsx index 21aa40616..fbce8cb93 100644 --- a/web/src/controllers/DashboardController/DashboardViewDashboardController/DashboardContentController/DashboardContentController.tsx +++ b/web/src/controllers/DashboardController/DashboardViewDashboardController/DashboardContentController/DashboardContentController.tsx @@ -77,7 +77,7 @@ export const DashboardContentController: React.FC<{ ); const onRowLayoutChange = useMemoizedFn((rows: BusterResizeableGridRow[]) => { - onUpdateDashboardConfig({ rows, id: dashboard!.id }); + if (dashboard) onUpdateDashboardConfig({ rows, id: dashboard.id }); }); const onDragEnd = useMemoizedFn(() => { diff --git a/web/src/controllers/DashboardListController/DashboardSelectedPopup.tsx b/web/src/controllers/DashboardListController/DashboardSelectedPopup.tsx index d12dde06f..dc272149f 100644 --- a/web/src/controllers/DashboardListController/DashboardSelectedPopup.tsx +++ b/web/src/controllers/DashboardListController/DashboardSelectedPopup.tsx @@ -67,7 +67,7 @@ const CollectionsButton: React.FC<{ selectedRowKeys.map((dashboardId) => { return onAddDashboardToCollection({ dashboardId, - collectionId: collectionIds + collectionIds }); }) ); @@ -81,7 +81,7 @@ const CollectionsButton: React.FC<{ selectedRowKeys.map((dashboardId) => { return onRemoveDashboardFromCollection({ dashboardId, - collectionId + collectionIds: [collectionId] }); }) ); diff --git a/web/src/controllers/MetricListContainer/MetricItemsSelectedPopup.tsx b/web/src/controllers/MetricListContainer/MetricItemsSelectedPopup.tsx index fada2b7f4..583b6900f 100644 --- a/web/src/controllers/MetricListContainer/MetricItemsSelectedPopup.tsx +++ b/web/src/controllers/MetricListContainer/MetricItemsSelectedPopup.tsx @@ -90,14 +90,8 @@ const CollectionsButton: React.FC<{ const onRemoveFromCollection = useMemoizedFn(async (collectionId: string) => { setSelectedCollections((prev) => prev.filter((id) => id !== collectionId)); - const allSelectedButLast = selectedRowKeys.slice(0, -1); - const lastMetricId = selectedRowKeys[selectedRowKeys.length - 1]; - const allRemoves: Promise[] = allSelectedButLast.map((metricId) => { - return removeMetricFromCollection({ metricId, collectionId }); - }); - await removeMetricFromCollection({ - metricId: lastMetricId, - collectionId + const allRemoves: Promise[] = selectedRowKeys.map((metricId) => { + return removeMetricFromCollection({ metricId, collectionIds: [collectionId] }); }); await Promise.all(allRemoves); openInfoMessage('Metrics removed from collections'); diff --git a/web/src/layouts/ChatLayout/FileContainer/FileContainerHeader/DashboardContainerHeaderButtons/DashboardThreeDotMenu.tsx b/web/src/layouts/ChatLayout/FileContainer/FileContainerHeader/DashboardContainerHeaderButtons/DashboardThreeDotMenu.tsx index 3083355a6..e5cdf170f 100644 --- a/web/src/layouts/ChatLayout/FileContainer/FileContainerHeader/DashboardContainerHeaderButtons/DashboardThreeDotMenu.tsx +++ b/web/src/layouts/ChatLayout/FileContainer/FileContainerHeader/DashboardContainerHeaderButtons/DashboardThreeDotMenu.tsx @@ -122,12 +122,12 @@ const useCollectionSelectMenu = ({ dashboardId }: { dashboardId: string }) => { }, [collections]); const onSaveToCollection = useMemoizedFn(async (collectionIds: string[]) => { - await saveDashboardToCollection({ dashboardId, collectionId: collectionIds[0] }); + await saveDashboardToCollection({ dashboardId, collectionIds }); openInfoMessage('Dashboard saved to collections'); }); const onRemoveFromCollection = useMemoizedFn(async (collectionId: string) => { - await removeDashboardFromCollection({ dashboardId, collectionId }); + await removeDashboardFromCollection({ dashboardId, collectionIds: [collectionId] }); openInfoMessage('Dashboard removed from collections'); });