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');
});