mirror of https://github.com/buster-so/buster.git
update update functions
This commit is contained in:
parent
b7db8dc1ee
commit
9bc97db50c
|
@ -0,0 +1,118 @@
|
|||
import { addAndRemoveMetricsToDashboard } from './addAndRemoveMetricsToDashboard';
|
||||
import type { BusterDashboard } from '@/api/asset_interfaces/dashboard';
|
||||
|
||||
describe('addAndRemoveMetricsToDashboard', () => {
|
||||
const createMockConfig = (metricIds: string[]): BusterDashboard['config'] => ({
|
||||
rows: [
|
||||
{
|
||||
id: 'row-1',
|
||||
items: metricIds.map((id) => ({ id })),
|
||||
columnSizes: Array(metricIds.length).fill(12 / metricIds.length),
|
||||
rowHeight: 320
|
||||
}
|
||||
]
|
||||
});
|
||||
|
||||
it('should return existing config when no changes are needed', () => {
|
||||
const existingConfig = createMockConfig(['metric-1', 'metric-2']);
|
||||
const result = addAndRemoveMetricsToDashboard(['metric-1', 'metric-2'], existingConfig);
|
||||
expect(result).toEqual(existingConfig);
|
||||
// Verify order is maintained
|
||||
expect(result.rows?.[0].items.map((item) => item.id)).toEqual(['metric-1', 'metric-2']);
|
||||
});
|
||||
|
||||
it('should add new metrics when they dont exist in the dashboard', () => {
|
||||
const existingConfig = createMockConfig(['metric-1']);
|
||||
const result = addAndRemoveMetricsToDashboard(
|
||||
['metric-1', 'metric-2', 'metric-3'],
|
||||
existingConfig
|
||||
);
|
||||
|
||||
// Verify metrics were added in the correct order
|
||||
const resultMetricIds = result.rows?.[0].items.map((item) => item.id);
|
||||
expect(result.rows?.[0].items.map((item) => item.id)).toEqual(['metric-1']);
|
||||
expect(result.rows?.[1].items.map((item) => item.id)).toEqual(['metric-2', 'metric-3']);
|
||||
});
|
||||
|
||||
it('should remove metrics that are not in the provided array while maintaining order', () => {
|
||||
const existingConfig = createMockConfig(['metric-1', 'metric-2', 'metric-3']);
|
||||
const result = addAndRemoveMetricsToDashboard(['metric-1', 'metric-3'], existingConfig);
|
||||
|
||||
// Verify metric was removed while maintaining order of remaining metrics
|
||||
const resultMetricIds = result.rows?.[0].items.map((item) => item.id);
|
||||
expect(resultMetricIds).toEqual(['metric-1', 'metric-3']);
|
||||
expect(resultMetricIds?.length).toBe(2);
|
||||
});
|
||||
|
||||
it('should handle both adding and removing metrics simultaneously while maintaining order', () => {
|
||||
const existingConfig = createMockConfig(['metric-1', 'metric-2', 'metric-3']);
|
||||
const result = addAndRemoveMetricsToDashboard(
|
||||
['metric-1', 'metric-4', 'metric-5'],
|
||||
existingConfig
|
||||
);
|
||||
|
||||
// Verify correct metrics were added and removed in the right order
|
||||
expect(result.rows?.[0].items.map((item) => item.id)).toEqual(['metric-1']); // Order should match input array
|
||||
expect(result.rows?.[1].items.map((item) => item.id)).toEqual(['metric-4', 'metric-5']); // Order should match input array
|
||||
});
|
||||
|
||||
it('should handle empty input array by removing all metrics', () => {
|
||||
const existingConfig = createMockConfig(['metric-1', 'metric-2']);
|
||||
const result = addAndRemoveMetricsToDashboard([], existingConfig);
|
||||
|
||||
// Verify all metrics were removed
|
||||
expect(result.rows?.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should handle empty existing config by adding all metrics in order', () => {
|
||||
const emptyConfig: BusterDashboard['config'] = { rows: [] };
|
||||
const result = addAndRemoveMetricsToDashboard(['metric-1', 'metric-2'], emptyConfig);
|
||||
|
||||
// Verify all metrics were added in the correct order
|
||||
const resultMetricIds = result.rows?.[0].items.map((item) => item.id);
|
||||
expect(resultMetricIds).toEqual(['metric-1', 'metric-2']);
|
||||
});
|
||||
|
||||
it('should maintain correct column sizes and order when removing metrics', () => {
|
||||
const existingConfig = createMockConfig(['metric-1', 'metric-2', 'metric-3']);
|
||||
const result = addAndRemoveMetricsToDashboard(['metric-1', 'metric-3'], existingConfig);
|
||||
|
||||
// Verify column sizes are updated correctly while maintaining order
|
||||
expect(result.rows?.[0].columnSizes).toEqual([6, 6]); // 12/2 = 6 for each column
|
||||
expect(result.rows?.[0].items.map((item) => item.id)).toEqual(['metric-1', 'metric-3']);
|
||||
});
|
||||
|
||||
it('should handle multiple rows and maintain order within each row', () => {
|
||||
const existingConfig: BusterDashboard['config'] = {
|
||||
rows: [
|
||||
{
|
||||
id: 'row-1',
|
||||
items: [{ id: 'metric-1' }, { id: 'metric-2' }],
|
||||
columnSizes: [6, 6],
|
||||
rowHeight: 320
|
||||
},
|
||||
{
|
||||
id: 'row-2',
|
||||
items: [{ id: 'metric-3' }, { id: 'metric-4' }],
|
||||
columnSizes: [6, 6],
|
||||
rowHeight: 320
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const result = addAndRemoveMetricsToDashboard(
|
||||
['metric-1', 'metric-3', 'metric-5'],
|
||||
existingConfig
|
||||
);
|
||||
|
||||
// Verify order is maintained in each row after modifications
|
||||
expect(result.rows?.[0].items.map((item) => item.id)).toEqual(['metric-1']);
|
||||
expect(result.rows?.[0].columnSizes).toEqual([12]);
|
||||
|
||||
expect(result.rows?.[1].items.map((item) => item.id)).toEqual(['metric-3']);
|
||||
expect(result.rows?.[1].columnSizes).toEqual([12]);
|
||||
|
||||
expect(result.rows?.[2].items.map((item) => item.id)).toEqual(['metric-5']);
|
||||
expect(result.rows?.[2].columnSizes).toEqual([12]);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,36 @@
|
|||
import type { BusterDashboard } from '@/api/asset_interfaces/dashboard';
|
||||
import { addMetricToDashboardConfig } from './addMetricToDashboard';
|
||||
import { removeMetricFromDashboardConfig } from './removeMetricFromDashboard';
|
||||
|
||||
export const addAndRemoveMetricsToDashboard = (
|
||||
metricIds: string[],
|
||||
existingConfig: BusterDashboard['config']
|
||||
): BusterDashboard['config'] => {
|
||||
// Get all existing metric IDs from the dashboard
|
||||
const existingMetricIds = new Set(
|
||||
existingConfig.rows?.flatMap((row) => row.items.map((item) => item.id)) || []
|
||||
);
|
||||
|
||||
// Determine which metrics to add and remove
|
||||
const metricsToAdd = metricIds.filter((id) => !existingMetricIds.has(id));
|
||||
const metricsToRemove = Array.from(existingMetricIds).filter((id) => !metricIds.includes(id));
|
||||
|
||||
// If no changes needed, return existing config
|
||||
if (metricsToAdd.length === 0 && metricsToRemove.length === 0) {
|
||||
return existingConfig;
|
||||
}
|
||||
|
||||
// First remove metrics if any
|
||||
const configAfterRemoval =
|
||||
metricsToRemove.length > 0
|
||||
? removeMetricFromDashboardConfig(metricsToRemove, existingConfig)
|
||||
: existingConfig;
|
||||
|
||||
// Then add new metrics if any
|
||||
const finalConfig =
|
||||
metricsToAdd.length > 0
|
||||
? addMetricToDashboardConfig(metricsToAdd, configAfterRemoval)
|
||||
: configAfterRemoval;
|
||||
|
||||
return finalConfig;
|
||||
};
|
|
@ -26,6 +26,7 @@ import {
|
|||
} from '../collections/queryRequests';
|
||||
import { collectionQueryKeys } from '@/api/query_keys/collection';
|
||||
import { addMetricToDashboardConfig, removeMetricFromDashboardConfig } from './helpers';
|
||||
import { addAndRemoveMetricsToDashboard } from './helpers/addAndRemoveMetricsToDashboard';
|
||||
|
||||
export const useGetDashboardsList = (
|
||||
params: Omit<DashboardsListRequest, 'page_token' | 'page_size'>
|
||||
|
@ -129,6 +130,7 @@ export const useUpdateDashboardConfig = () => {
|
|||
const newConfig = create(previousConfig!, (draft) => {
|
||||
Object.assign(draft, newDashboard);
|
||||
});
|
||||
console.log('update', newConfig);
|
||||
return mutateAsync({
|
||||
id: newDashboard.id,
|
||||
config: newConfig
|
||||
|
@ -330,29 +332,75 @@ export const useUpdateDashboardShare = () => {
|
|||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook for adding metrics to a dashboard. This function also supports removing metrics via the addMetricToDashboardConfig
|
||||
*/
|
||||
export const useAddMetricsToDashboard = () => {
|
||||
const useEnsureDashboardConfig = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const prefetchDashboard = useGetDashboardAndInitializeMetrics();
|
||||
const { openErrorMessage } = useBusterNotifications();
|
||||
|
||||
const method = useMemoizedFn(async (dashboardId: string) => {
|
||||
const options = dashboardQueryKeys.dashboardGetDashboard(dashboardId);
|
||||
let dashboardResponse = queryClient.getQueryData(options.queryKey);
|
||||
if (!dashboardResponse) {
|
||||
const res = await prefetchDashboard(dashboardId).catch((e) => {
|
||||
openErrorMessage('Failed to save metrics to dashboard. Dashboard not found');
|
||||
return null;
|
||||
});
|
||||
if (res) {
|
||||
queryClient.setQueryData(options.queryKey, res);
|
||||
dashboardResponse = res;
|
||||
}
|
||||
}
|
||||
|
||||
return dashboardResponse;
|
||||
});
|
||||
|
||||
return method;
|
||||
};
|
||||
|
||||
export const useAddAndRemoveMetricsFromDashboard = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const { openErrorMessage } = useBusterNotifications();
|
||||
const ensureDashboardConfig = useEnsureDashboardConfig();
|
||||
|
||||
const addMetricToDashboard = useMemoizedFn(
|
||||
async ({ metricIds, dashboardId }: { metricIds: string[]; dashboardId: string }) => {
|
||||
const options = dashboardQueryKeys.dashboardGetDashboard(dashboardId);
|
||||
let dashboardResponse = queryClient.getQueryData(options.queryKey);
|
||||
if (!dashboardResponse) {
|
||||
const res = await prefetchDashboard(dashboardId).catch((e) => {
|
||||
openErrorMessage('Failed to save metrics to dashboard. Dashboard not found');
|
||||
return null;
|
||||
const dashboardResponse = await ensureDashboardConfig(dashboardId);
|
||||
|
||||
if (dashboardResponse) {
|
||||
const newConfig = addAndRemoveMetricsToDashboard(
|
||||
metricIds,
|
||||
dashboardResponse.dashboard.config
|
||||
);
|
||||
console.log('add/remove', newConfig);
|
||||
return dashboardsUpdateDashboard({
|
||||
id: dashboardId,
|
||||
config: newConfig
|
||||
});
|
||||
if (res) {
|
||||
queryClient.setQueryData(options.queryKey, res);
|
||||
dashboardResponse = res;
|
||||
}
|
||||
}
|
||||
|
||||
openErrorMessage('Failed to save metrics to dashboard');
|
||||
}
|
||||
);
|
||||
|
||||
return useMutation({
|
||||
mutationFn: addMetricToDashboard,
|
||||
onSuccess: (data, variables) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: dashboardQueryKeys.dashboardGetDashboard(variables.dashboardId).queryKey
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
export const useAddMetricsToDashboard = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const { openErrorMessage } = useBusterNotifications();
|
||||
const ensureDashboardConfig = useEnsureDashboardConfig();
|
||||
|
||||
const addMetricToDashboard = useMemoizedFn(
|
||||
async ({ metricIds, dashboardId }: { metricIds: string[]; dashboardId: string }) => {
|
||||
const dashboardResponse = await ensureDashboardConfig(dashboardId);
|
||||
|
||||
if (dashboardResponse) {
|
||||
const newConfig = addMetricToDashboardConfig(metricIds, dashboardResponse.dashboard.config);
|
||||
return dashboardsUpdateDashboard({
|
||||
|
@ -378,7 +426,8 @@ export const useAddMetricsToDashboard = () => {
|
|||
export const useRemoveMetricsFromDashboard = () => {
|
||||
const { openConfirmModal, openErrorMessage } = useBusterNotifications();
|
||||
const queryClient = useQueryClient();
|
||||
const prefetchDashboard = useGetDashboardAndInitializeMetrics();
|
||||
const ensureDashboardConfig = useEnsureDashboardConfig();
|
||||
|
||||
const removeMetricFromDashboard = useMemoizedFn(
|
||||
async ({
|
||||
metricIds,
|
||||
|
@ -390,18 +439,7 @@ export const useRemoveMetricsFromDashboard = () => {
|
|||
useConfirmModal?: boolean;
|
||||
}) => {
|
||||
const method = async () => {
|
||||
const options = dashboardQueryKeys.dashboardGetDashboard(dashboardId);
|
||||
let dashboardResponse = queryClient.getQueryData(options.queryKey);
|
||||
if (!dashboardResponse) {
|
||||
const res = await prefetchDashboard(dashboardId).catch((e) => {
|
||||
openErrorMessage('Failed to remove metrics from dashboard. Dashboard not found');
|
||||
return null;
|
||||
});
|
||||
if (res) {
|
||||
queryClient.setQueryData(options.queryKey, res);
|
||||
dashboardResponse = res;
|
||||
}
|
||||
}
|
||||
const dashboardResponse = await ensureDashboardConfig(dashboardId);
|
||||
|
||||
if (dashboardResponse) {
|
||||
const newConfig = removeMetricFromDashboardConfig(
|
||||
|
|
|
@ -4,7 +4,11 @@ import React, { useLayoutEffect, useMemo, useState } from 'react';
|
|||
import { InputSelectModal, InputSelectModalProps } from '@/components/ui/modal/InputSelectModal';
|
||||
import { formatDate } from '@/lib';
|
||||
import { Button } from '@/components/ui/buttons';
|
||||
import { useAddMetricsToDashboard, useGetDashboard } from '@/api/buster_rest/dashboards';
|
||||
import {
|
||||
useAddAndRemoveMetricsFromDashboard,
|
||||
useAddMetricsToDashboard,
|
||||
useGetDashboard
|
||||
} from '@/api/buster_rest/dashboards';
|
||||
|
||||
export const AddToDashboardModal: React.FC<{
|
||||
open: boolean;
|
||||
|
@ -13,7 +17,7 @@ export const AddToDashboardModal: React.FC<{
|
|||
}> = React.memo(({ open, onClose, dashboardId }) => {
|
||||
const { data: dashboard, isFetched: isFetchedDashboard } = useGetDashboard(dashboardId);
|
||||
const { data: metrics, isFetched: isFetchedMetrics } = useGetMetricsList({});
|
||||
const { mutateAsync: addMetricsToDashboard } = useAddMetricsToDashboard();
|
||||
const { mutateAsync: addAndRemoveMetricsFromDashboard } = useAddAndRemoveMetricsFromDashboard();
|
||||
|
||||
const [selectedMetrics, setSelectedMetrics] = useState<string[]>([]);
|
||||
|
||||
|
@ -43,7 +47,7 @@ export const AddToDashboardModal: React.FC<{
|
|||
}, [metrics.length]);
|
||||
|
||||
const handleAddAndRemoveMetrics = useMemoizedFn(async () => {
|
||||
await addMetricsToDashboard({
|
||||
await addAndRemoveMetricsFromDashboard({
|
||||
dashboardId: dashboardId,
|
||||
metricIds: selectedMetrics
|
||||
});
|
||||
|
@ -102,7 +106,7 @@ export const AddToDashboardModal: React.FC<{
|
|||
|
||||
return (
|
||||
<InputSelectModal
|
||||
width={650}
|
||||
width={665}
|
||||
open={open}
|
||||
onClose={onClose}
|
||||
columns={columns}
|
||||
|
|
|
@ -23,7 +23,9 @@ import { v4 as uuidv4 } from 'uuid';
|
|||
import { useMemoizedFn } from '@/hooks';
|
||||
import isEqual from 'lodash/isEqual';
|
||||
import { BusterResizeRows } from './BusterResizeRows';
|
||||
import omit from 'lodash/omit';
|
||||
import { NUMBER_OF_COLUMNS, NEW_ROW_ID, MIN_ROW_HEIGHT, TOP_SASH_ID } from './helpers';
|
||||
import { create } from 'mutative';
|
||||
|
||||
const measuringConfig = {
|
||||
droppable: {
|
||||
|
@ -60,6 +62,8 @@ export const BusterResizeableGrid: React.FC<{
|
|||
const onRowLayoutChangePreflight = useMemoizedFn((newLayout: BusterResizeableGridRow[]) => {
|
||||
const filteredRows = newRowPreflight(newLayout);
|
||||
|
||||
console.log(filteredRows);
|
||||
|
||||
if (checkRowEquality(filteredRows, rows)) {
|
||||
return;
|
||||
}
|
||||
|
@ -399,9 +403,7 @@ const newRowPreflight = (newRows: BusterResizeableGridRow[]) => {
|
|||
columnSizes: newColumnSizes
|
||||
};
|
||||
}
|
||||
return {
|
||||
...row
|
||||
};
|
||||
return row;
|
||||
});
|
||||
|
||||
return newRowsCopy;
|
||||
|
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
|
||||
export type ResizeableGridDragItem = {
|
||||
id: string;
|
||||
children?: React.ReactNode;
|
||||
children: React.ReactNode;
|
||||
};
|
||||
|
||||
export type BusterResizeableGridRow = {
|
||||
|
|
|
@ -4,7 +4,12 @@ import React, { useEffect, useMemo, useState } from 'react';
|
|||
import isEmpty from 'lodash/isEmpty';
|
||||
import { BusterResizeableGrid, BusterResizeableGridRow } from '@/components/ui/grid';
|
||||
import { useDebounceFn, useMemoizedFn } from '@/hooks';
|
||||
import { hasRemovedMetrics, hasUnmappedMetrics, normalizeNewMetricsIntoGrid } from './helpers';
|
||||
import {
|
||||
hasRemovedMetrics,
|
||||
hasUnmappedMetrics,
|
||||
normalizeNewMetricsIntoGrid,
|
||||
removeChildrenFromItems
|
||||
} from './helpers';
|
||||
import { DashboardMetricItem } from './DashboardMetricItem';
|
||||
import { DashboardContentControllerProvider } from './DashboardContentControllerContext';
|
||||
import type {
|
||||
|
@ -14,6 +19,7 @@ import type {
|
|||
} from '@/api/asset_interfaces';
|
||||
import { DashboardEmptyState } from './DashboardEmptyState';
|
||||
import { type useUpdateDashboardConfig } from '@/api/buster_rest/dashboards';
|
||||
import omit from 'lodash/omit';
|
||||
|
||||
const DEFAULT_EMPTY_ROWS: DashboardConfig['rows'] = [];
|
||||
const DEFAULT_EMPTY_METRICS: Record<string, BusterMetric> = {};
|
||||
|
@ -77,7 +83,9 @@ export const DashboardContentController: React.FC<{
|
|||
);
|
||||
|
||||
const onRowLayoutChange = useMemoizedFn((rows: BusterResizeableGridRow[]) => {
|
||||
if (dashboard) onUpdateDashboardConfig({ rows, id: dashboard.id });
|
||||
if (dashboard) {
|
||||
onUpdateDashboardConfig({ rows: removeChildrenFromItems(rows), id: dashboard.id });
|
||||
}
|
||||
});
|
||||
|
||||
const onDragEnd = useMemoizedFn(() => {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { DashboardConfig } from '@/api/asset_interfaces/dashboard';
|
||||
import { BusterMetric } from '@/api/asset_interfaces/metric';
|
||||
import { BusterResizeableGridRow } from '@/components/ui/grid/interfaces';
|
||||
import type { DashboardConfig } from '@/api/asset_interfaces/dashboard';
|
||||
import type { BusterMetric } from '@/api/asset_interfaces/metric';
|
||||
import { BusterResizeableGridRow } from '@/components/ui/grid';
|
||||
import omit from 'lodash/omit';
|
||||
|
||||
export const hasUnmappedMetrics = (
|
||||
metrics: Record<string, BusterMetric>,
|
||||
|
@ -13,7 +14,7 @@ export const hasUnmappedMetrics = (
|
|||
|
||||
export const hasRemovedMetrics = (
|
||||
metrics: Record<string, BusterMetric>,
|
||||
configRows: BusterResizeableGridRow[]
|
||||
configRows: DashboardConfig['rows'] = []
|
||||
) => {
|
||||
const allGridItemsLength = configRows.flatMap((r) => r.items).length;
|
||||
|
||||
|
@ -25,3 +26,10 @@ export const hasRemovedMetrics = (
|
|||
r.items.some((t) => Object.values(metrics).some((m) => t.id === m.id))
|
||||
);
|
||||
};
|
||||
|
||||
export const removeChildrenFromItems = (row: BusterResizeableGridRow[]) => {
|
||||
return row.map((r) => ({
|
||||
...r,
|
||||
items: r.items.map((i) => omit(i, 'children'))
|
||||
}));
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue