mirror of https://github.com/buster-so/buster.git
809 lines
27 KiB
TypeScript
809 lines
27 KiB
TypeScript
import {
|
|
type QueryClient,
|
|
type UseQueryOptions,
|
|
useMutation,
|
|
useQuery,
|
|
useQueryClient
|
|
} from '@tanstack/react-query';
|
|
import last from 'lodash/last';
|
|
import { create } from 'mutative';
|
|
import { useMemo } from 'react';
|
|
import {
|
|
type BusterDashboard,
|
|
type BusterDashboardResponse,
|
|
MAX_NUMBER_OF_ITEMS_ON_DASHBOARD
|
|
} from '@/api/asset_interfaces/dashboard';
|
|
import { collectionQueryKeys } from '@/api/query_keys/collection';
|
|
import { dashboardQueryKeys } from '@/api/query_keys/dashboard';
|
|
import { metricsQueryKeys } from '@/api/query_keys/metric';
|
|
import { useBusterAssetsContextSelector } from '@/context/Assets/BusterAssetsProvider';
|
|
import { useBusterNotifications } from '@/context/BusterNotifications';
|
|
import { useOriginalDashboardStore } from '@/context/Dashboards';
|
|
import { useMemoizedFn } from '@/hooks';
|
|
import { hasOrganizationId, isQueryStale } from '@/lib';
|
|
import {
|
|
useAddAssetToCollection,
|
|
useRemoveAssetFromCollection
|
|
} from '../collections/queryRequests';
|
|
import type { RustApiError } from '../errors';
|
|
import { useGetLatestMetricVersionMemoized } from '../metrics';
|
|
import { createDashboardFullConfirmModal } from './confirmModals';
|
|
import {
|
|
useEnsureDashboardConfig,
|
|
useGetDashboardAndInitializeMetrics
|
|
} from './dashboardQueryHelpers';
|
|
import { useDashboardQueryStore, useGetDashboardVersionNumber } from './dashboardQueryStore';
|
|
import { addMetricToDashboardConfig, removeMetricFromDashboardConfig } from './helpers';
|
|
import { addAndRemoveMetricsToDashboard } from './helpers/addAndRemoveMetricsToDashboard';
|
|
import {
|
|
dashboardsCreateDashboard,
|
|
dashboardsDeleteDashboard,
|
|
dashboardsGetList,
|
|
dashboardsUpdateDashboard,
|
|
shareDashboard,
|
|
unshareDashboard,
|
|
updateDashboardShare
|
|
} from './requests';
|
|
|
|
export const useGetDashboard = <TData = BusterDashboardResponse>(
|
|
{
|
|
id: idProp,
|
|
versionNumber: versionNumberProp
|
|
}: { id: string | undefined; versionNumber?: number | null },
|
|
params?: Omit<
|
|
UseQueryOptions<BusterDashboardResponse, RustApiError, TData>,
|
|
'queryKey' | 'queryFn'
|
|
>
|
|
) => {
|
|
const id = idProp || '';
|
|
const queryFn = useGetDashboardAndInitializeMetrics();
|
|
const setAssetPasswordError = useBusterAssetsContextSelector((x) => x.setAssetPasswordError);
|
|
const { selectedVersionNumber, latestVersionNumber, paramVersionNumber } =
|
|
useGetDashboardVersionNumber({ versionNumber: versionNumberProp });
|
|
|
|
const { isFetched: isFetchedInitial, isError: isErrorInitial } = useQuery({
|
|
...dashboardQueryKeys.dashboardGetDashboard(id, null),
|
|
staleTime: Infinity,
|
|
queryFn: () => queryFn(id, paramVersionNumber),
|
|
enabled: false, //we made this false because we want to be explicit about the fact that we fetch the dashboard server side
|
|
retry(failureCount, error) {
|
|
if (error?.message !== undefined) {
|
|
setAssetPasswordError(id, error.message || 'An error occurred');
|
|
}
|
|
return false;
|
|
},
|
|
select: undefined,
|
|
...params
|
|
});
|
|
|
|
return useQuery({
|
|
...dashboardQueryKeys.dashboardGetDashboard(id, selectedVersionNumber),
|
|
queryFn: () => queryFn(id, selectedVersionNumber),
|
|
enabled: !!latestVersionNumber && isFetchedInitial && !isErrorInitial,
|
|
select: params?.select
|
|
});
|
|
};
|
|
|
|
export const usePrefetchGetDashboardClient = () => {
|
|
const queryClient = useQueryClient();
|
|
const queryFn = useGetDashboardAndInitializeMetrics(false);
|
|
return useMemoizedFn((id: string, versionNumber: number) => {
|
|
const getDashboardQueryKey = dashboardQueryKeys.dashboardGetDashboard(id, versionNumber);
|
|
const isStale = isQueryStale(getDashboardQueryKey, queryClient);
|
|
if (!isStale) return queryClient;
|
|
|
|
return queryClient.prefetchQuery({
|
|
...dashboardQueryKeys.dashboardGetDashboard(id, versionNumber),
|
|
queryFn: () => queryFn(id, versionNumber)
|
|
});
|
|
});
|
|
};
|
|
|
|
export const useSaveDashboard = (params?: { updateOnSave?: boolean }) => {
|
|
const updateOnSave = params?.updateOnSave || false;
|
|
const queryClient = useQueryClient();
|
|
const setOriginalDashboard = useOriginalDashboardStore((x) => x.setOriginalDashboard);
|
|
const onSetLatestDashboardVersion = useDashboardQueryStore((x) => x.onSetLatestDashboardVersion);
|
|
|
|
return useMutation({
|
|
mutationFn: dashboardsUpdateDashboard,
|
|
onSuccess: (data, variables) => {
|
|
if (updateOnSave && data) {
|
|
queryClient.setQueryData(
|
|
dashboardQueryKeys.dashboardGetDashboard(data.dashboard.id, data.dashboard.version_number)
|
|
.queryKey,
|
|
data
|
|
);
|
|
setOriginalDashboard(data.dashboard);
|
|
onSetLatestDashboardVersion(data.dashboard.id, last(data.versions)?.version_number || 1);
|
|
|
|
if (variables.restore_to_version) {
|
|
queryClient.invalidateQueries({
|
|
queryKey: dashboardQueryKeys.dashboardGetDashboard(data.dashboard.id, null).queryKey
|
|
});
|
|
}
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
export const useUpdateDashboard = (params?: {
|
|
updateOnSave?: boolean;
|
|
updateVersion?: boolean;
|
|
saveToServer?: boolean;
|
|
}) => {
|
|
const { updateOnSave = false, updateVersion = false, saveToServer = false } = params || {};
|
|
const queryClient = useQueryClient();
|
|
const { mutateAsync: saveDashboard } = useSaveDashboard({ updateOnSave });
|
|
const { latestVersionNumber } = useGetDashboardVersionNumber();
|
|
const getOriginalDashboard = useOriginalDashboardStore((x) => x.getOriginalDashboard);
|
|
|
|
const mutationFn = useMemoizedFn(
|
|
async (variables: Parameters<typeof dashboardsUpdateDashboard>[0]) => {
|
|
if (saveToServer) {
|
|
return await saveDashboard({
|
|
...variables,
|
|
update_version: updateVersion
|
|
});
|
|
}
|
|
}
|
|
);
|
|
|
|
return useMutation({
|
|
mutationFn,
|
|
onMutate: (variables) => {
|
|
const originalDashboard = getOriginalDashboard(variables.id);
|
|
if (!originalDashboard) return;
|
|
const updatedDashboard = create(originalDashboard, (draft) => {
|
|
Object.assign(draft, variables);
|
|
});
|
|
const queryKey = dashboardQueryKeys.dashboardGetDashboard(
|
|
variables.id,
|
|
latestVersionNumber
|
|
).queryKey;
|
|
|
|
queryClient.setQueryData(queryKey, (previousData) => {
|
|
if (!previousData) return previousData;
|
|
return create(previousData, (draft) => {
|
|
draft.dashboard = updatedDashboard;
|
|
});
|
|
});
|
|
}
|
|
});
|
|
};
|
|
|
|
export const useUpdateDashboardConfig = () => {
|
|
const { mutateAsync } = useUpdateDashboard({
|
|
saveToServer: false,
|
|
updateVersion: false,
|
|
updateOnSave: true
|
|
});
|
|
const queryClient = useQueryClient();
|
|
const { latestVersionNumber } = useGetDashboardVersionNumber();
|
|
|
|
const method = useMemoizedFn(
|
|
async ({
|
|
dashboardId,
|
|
...newDashboard
|
|
}: Partial<BusterDashboard['config']> & {
|
|
dashboardId: string;
|
|
}) => {
|
|
const options = dashboardQueryKeys.dashboardGetDashboard(dashboardId, latestVersionNumber);
|
|
const previousDashboard = queryClient.getQueryData(options.queryKey);
|
|
const previousConfig = previousDashboard?.dashboard?.config;
|
|
if (previousConfig) {
|
|
const newConfig = create(previousConfig, (draft) => {
|
|
Object.assign(draft, newDashboard);
|
|
});
|
|
return mutateAsync({
|
|
id: dashboardId,
|
|
config: newConfig
|
|
});
|
|
}
|
|
}
|
|
);
|
|
|
|
return useMutation({
|
|
mutationFn: method
|
|
});
|
|
};
|
|
|
|
export const useCreateDashboard = () => {
|
|
const queryClient = useQueryClient();
|
|
const setOriginalDashboard = useOriginalDashboardStore((x) => x.setOriginalDashboard);
|
|
const setLatestDashboardVersion = useDashboardQueryStore((x) => x.onSetLatestDashboardVersion);
|
|
return useMutation({
|
|
mutationFn: dashboardsCreateDashboard,
|
|
onSuccess: (originalData, variables) => {
|
|
const data = create(originalData, (draft) => {
|
|
draft.dashboard.name = variables.name || originalData.dashboard.name;
|
|
});
|
|
queryClient.setQueryData(
|
|
dashboardQueryKeys.dashboardGetDashboard(data.dashboard.id, data.dashboard.version_number)
|
|
.queryKey,
|
|
data
|
|
);
|
|
queryClient.setQueryData(
|
|
dashboardQueryKeys.dashboardGetDashboard(data.dashboard.id, null).queryKey,
|
|
data
|
|
);
|
|
setOriginalDashboard(data.dashboard);
|
|
setLatestDashboardVersion(data.dashboard.id, data.dashboard.version_number);
|
|
setTimeout(() => {
|
|
queryClient.invalidateQueries({
|
|
queryKey: dashboardQueryKeys.dashboardGetList().queryKey,
|
|
refetchType: 'all'
|
|
});
|
|
}, 550);
|
|
}
|
|
});
|
|
};
|
|
|
|
export const useDeleteDashboards = () => {
|
|
const queryClient = useQueryClient();
|
|
const { openConfirmModal } = useBusterNotifications();
|
|
|
|
const onDeleteDashboard = useMemoizedFn(
|
|
async ({
|
|
dashboardId,
|
|
ignoreConfirm
|
|
}: {
|
|
dashboardId: string | string[];
|
|
ignoreConfirm?: boolean;
|
|
}) => {
|
|
const onMutate = () => {
|
|
const queryKey = dashboardQueryKeys.dashboardGetList().queryKey;
|
|
queryClient.setQueryData(queryKey, (v) => {
|
|
const ids = typeof dashboardId === 'string' ? [dashboardId] : dashboardId;
|
|
return v?.filter((t) => !ids.includes(t.id)) || [];
|
|
});
|
|
};
|
|
|
|
const method = async () => {
|
|
const ids = typeof dashboardId === 'string' ? [dashboardId] : dashboardId;
|
|
onMutate();
|
|
await dashboardsDeleteDashboard({ ids });
|
|
};
|
|
if (ignoreConfirm) {
|
|
return method();
|
|
}
|
|
return await openConfirmModal({
|
|
title: 'Delete Dashboard',
|
|
content: 'Are you sure you want to delete this dashboard?',
|
|
primaryButtonProps: {
|
|
text: 'Delete'
|
|
},
|
|
onOk: method
|
|
});
|
|
}
|
|
);
|
|
|
|
return useMutation({
|
|
mutationFn: onDeleteDashboard,
|
|
onSuccess: (_, { dashboardId }) => {
|
|
queryClient.invalidateQueries({
|
|
queryKey: dashboardQueryKeys.dashboardGetList().queryKey,
|
|
refetchType: 'all'
|
|
});
|
|
}
|
|
});
|
|
};
|
|
|
|
export const useAddDashboardToCollection = () => {
|
|
const queryClient = useQueryClient();
|
|
const { mutateAsync: addAssetToCollection } = useAddAssetToCollection();
|
|
const mutationFn = useMemoizedFn(
|
|
async (variables: { dashboardIds: string[]; collectionIds: string[] }) => {
|
|
const { dashboardIds, collectionIds } = variables;
|
|
return await Promise.all(
|
|
collectionIds.map((collectionId) =>
|
|
addAssetToCollection({
|
|
id: collectionId,
|
|
assets: dashboardIds.map((dashboardId) => ({ id: dashboardId, type: 'dashboard' }))
|
|
})
|
|
)
|
|
);
|
|
}
|
|
);
|
|
return useMutation({
|
|
mutationFn,
|
|
onSuccess: (_, { collectionIds }) => {
|
|
queryClient.invalidateQueries({
|
|
queryKey: collectionIds.map(
|
|
(id) => collectionQueryKeys.collectionsGetCollection(id).queryKey
|
|
),
|
|
refetchType: 'all'
|
|
});
|
|
}
|
|
});
|
|
};
|
|
|
|
export const useRemoveDashboardFromCollection = () => {
|
|
const queryClient = useQueryClient();
|
|
const { mutateAsync: removeAssetFromCollection } = useRemoveAssetFromCollection();
|
|
|
|
const mutationFn = useMemoizedFn(
|
|
async (variables: { dashboardIds: string[]; collectionIds: string[] }) => {
|
|
const { dashboardIds, collectionIds } = variables;
|
|
|
|
return await Promise.all(
|
|
collectionIds.map((collectionId) =>
|
|
removeAssetFromCollection({
|
|
id: collectionId,
|
|
assets: dashboardIds.map((dashboardId) => ({ id: dashboardId, type: 'dashboard' }))
|
|
})
|
|
)
|
|
);
|
|
}
|
|
);
|
|
return useMutation({
|
|
mutationFn,
|
|
onSuccess: (_, { collectionIds }) => {
|
|
queryClient.invalidateQueries({
|
|
queryKey: collectionIds.map(
|
|
(id) => collectionQueryKeys.collectionsGetCollection(id).queryKey
|
|
),
|
|
refetchType: 'all'
|
|
});
|
|
}
|
|
});
|
|
};
|
|
|
|
export const useShareDashboard = () => {
|
|
const queryClient = useQueryClient();
|
|
const { latestVersionNumber } = useGetDashboardVersionNumber();
|
|
return useMutation({
|
|
mutationFn: shareDashboard,
|
|
onMutate: ({ id, params }) => {
|
|
const queryKey = dashboardQueryKeys.dashboardGetDashboard(id, latestVersionNumber).queryKey;
|
|
|
|
queryClient.setQueryData(queryKey, (previousData) => {
|
|
if (!previousData) return previousData;
|
|
return create(previousData, (draft) => {
|
|
draft.individual_permissions = [
|
|
...params.map((p) => ({
|
|
...p,
|
|
name: p.name,
|
|
avatar_url: p.avatar_url || null
|
|
})),
|
|
...(draft.individual_permissions || [])
|
|
].sort((a, b) => a.email.localeCompare(b.email));
|
|
});
|
|
});
|
|
},
|
|
onSuccess: (data, variables) => {
|
|
const partialMatchedKey = dashboardQueryKeys
|
|
.dashboardGetDashboard(variables.id, null)
|
|
.queryKey.slice(0, -1);
|
|
queryClient.invalidateQueries({
|
|
queryKey: partialMatchedKey,
|
|
refetchType: 'all'
|
|
});
|
|
}
|
|
});
|
|
};
|
|
|
|
export const useUnshareDashboard = () => {
|
|
const queryClient = useQueryClient();
|
|
const { latestVersionNumber } = useGetDashboardVersionNumber();
|
|
return useMutation({
|
|
mutationFn: unshareDashboard,
|
|
onMutate: (variables) => {
|
|
const queryKey = dashboardQueryKeys.dashboardGetDashboard(
|
|
variables.id,
|
|
latestVersionNumber
|
|
).queryKey;
|
|
queryClient.setQueryData(queryKey, (previousData) => {
|
|
if (!previousData) return previousData;
|
|
return create(previousData, (draft) => {
|
|
draft.individual_permissions = (
|
|
draft.individual_permissions?.filter((t) => !variables.data.includes(t.email)) || []
|
|
).sort((a, b) => a.email.localeCompare(b.email));
|
|
});
|
|
});
|
|
},
|
|
onSuccess: (data) => {
|
|
//
|
|
}
|
|
});
|
|
};
|
|
|
|
export const useUpdateDashboardShare = () => {
|
|
const queryClient = useQueryClient();
|
|
const { latestVersionNumber } = useGetDashboardVersionNumber();
|
|
return useMutation({
|
|
mutationFn: updateDashboardShare,
|
|
onMutate: ({ id, params }) => {
|
|
const queryKey = dashboardQueryKeys.dashboardGetDashboard(id, latestVersionNumber).queryKey;
|
|
queryClient.setQueryData(queryKey, (previousData) => {
|
|
if (!previousData) return previousData;
|
|
return create(previousData, (draft) => {
|
|
draft.individual_permissions = (
|
|
draft.individual_permissions?.map((t) => {
|
|
const found = params.users?.find((v) => v.email === t.email);
|
|
if (found) return { ...t, ...found };
|
|
return t;
|
|
}) || []
|
|
).sort((a, b) => a.email.localeCompare(b.email));
|
|
|
|
if (params.publicly_accessible !== undefined) {
|
|
draft.publicly_accessible = params.publicly_accessible;
|
|
}
|
|
if (params.public_password !== undefined) {
|
|
draft.public_password = params.public_password;
|
|
}
|
|
if (params.public_expiry_date !== undefined) {
|
|
draft.public_expiry_date = params.public_expiry_date;
|
|
}
|
|
if (params.workspace_sharing !== undefined) {
|
|
draft.workspace_sharing = params.workspace_sharing;
|
|
}
|
|
});
|
|
});
|
|
}
|
|
});
|
|
};
|
|
|
|
export const useAddAndRemoveMetricsFromDashboard = () => {
|
|
const queryClient = useQueryClient();
|
|
const { openErrorMessage, openConfirmModal } = useBusterNotifications();
|
|
const ensureDashboardConfig = useEnsureDashboardConfig(false);
|
|
const setOriginalDashboard = useOriginalDashboardStore((x) => x.setOriginalDashboard);
|
|
const setLatestDashboardVersion = useDashboardQueryStore((x) => x.onSetLatestDashboardVersion);
|
|
|
|
const addAndRemoveMetrics = useMemoizedFn(
|
|
async ({
|
|
metrics,
|
|
dashboardId
|
|
}: {
|
|
metrics: { id: string; name: string }[];
|
|
dashboardId: string;
|
|
}) => {
|
|
const dashboardResponse = await ensureDashboardConfig(dashboardId);
|
|
|
|
if (dashboardResponse) {
|
|
// Get all existing metric IDs from the dashboard
|
|
const existingMetricIds = new Set(
|
|
dashboardResponse.dashboard.config.rows?.flatMap((row) =>
|
|
row.items.map((item) => item.id)
|
|
) || []
|
|
);
|
|
|
|
// Determine which metrics to add and remove
|
|
const metricsToAdd = metrics.filter((metric) => !existingMetricIds.has(metric.id));
|
|
const metricsToRemove = Array.from(existingMetricIds).filter(
|
|
(id) => !metrics.some((metric) => metric.id === id)
|
|
);
|
|
|
|
// Calculate how many metrics we can add while staying under the limit
|
|
const currentMetricCount = existingMetricIds.size - metricsToRemove.length;
|
|
const availableSlots = MAX_NUMBER_OF_ITEMS_ON_DASHBOARD - currentMetricCount;
|
|
const metricsToActuallyAdd = metricsToAdd.slice(0, availableSlots);
|
|
|
|
const addMethod = async () => {
|
|
// Create the final list of metrics to include
|
|
const finalMetricIds = [
|
|
...Array.from(existingMetricIds).filter((id) => !metricsToRemove.includes(id)),
|
|
...metricsToActuallyAdd.map((metric) => metric.id)
|
|
];
|
|
|
|
const newConfig = addAndRemoveMetricsToDashboard(
|
|
finalMetricIds,
|
|
dashboardResponse.dashboard.config
|
|
);
|
|
|
|
const data = await dashboardsUpdateDashboard({
|
|
id: dashboardId,
|
|
config: newConfig
|
|
});
|
|
|
|
return data;
|
|
};
|
|
|
|
if (metricsToAdd.length > availableSlots) {
|
|
if (availableSlots === 0) {
|
|
return openConfirmModal({
|
|
title: 'Dashboard is full',
|
|
content: `The dashboard is full, please remove some metrics before adding more. You can only have ${MAX_NUMBER_OF_ITEMS_ON_DASHBOARD} metrics on a dashboard at a time.`,
|
|
primaryButtonProps: {
|
|
text: 'Okay'
|
|
},
|
|
cancelButtonProps: {
|
|
hide: true
|
|
},
|
|
onOk: () => {}
|
|
});
|
|
}
|
|
|
|
const content = createDashboardFullConfirmModal({
|
|
availableSlots,
|
|
metricsToActuallyAdd: metricsToActuallyAdd,
|
|
metricsToAdd: metricsToAdd
|
|
});
|
|
|
|
return openConfirmModal<BusterDashboardResponse>({
|
|
title: 'Dashboard is full',
|
|
content,
|
|
primaryButtonProps: {
|
|
text: 'Okay'
|
|
},
|
|
onOk: async () => {
|
|
return await addMethod();
|
|
}
|
|
});
|
|
}
|
|
|
|
return await addMethod();
|
|
}
|
|
|
|
openErrorMessage('Failed to save metrics to dashboard');
|
|
return;
|
|
}
|
|
);
|
|
|
|
return useMutation({
|
|
mutationFn: addAndRemoveMetrics,
|
|
onSuccess: (data, variables) => {
|
|
if (data) {
|
|
queryClient.setQueryData(
|
|
dashboardQueryKeys.dashboardGetDashboard(data.dashboard.id, data.dashboard.version_number)
|
|
.queryKey,
|
|
data
|
|
);
|
|
setOriginalDashboard(data.dashboard);
|
|
setLatestDashboardVersion(data.dashboard.id, data.dashboard.version_number);
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
export const useAddMetricsToDashboard = () => {
|
|
const queryClient = useQueryClient();
|
|
const { openErrorMessage, openConfirmModal } = useBusterNotifications();
|
|
const ensureDashboardConfig = useEnsureDashboardConfig(false);
|
|
const setOriginalDashboard = useOriginalDashboardStore((x) => x.setOriginalDashboard);
|
|
const getLatestMetricVersion = useGetLatestMetricVersionMemoized();
|
|
const setLatestDashboardVersion = useDashboardQueryStore((x) => x.onSetLatestDashboardVersion);
|
|
|
|
const addMetricToDashboard = useMemoizedFn(
|
|
async ({ metricIds, dashboardId }: { metricIds: string[]; dashboardId: string }) => {
|
|
const dashboardResponse = await ensureDashboardConfig(dashboardId);
|
|
|
|
const existingMetricIds = new Set(
|
|
dashboardResponse?.dashboard.config.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 currentMetricCount = existingMetricIds.size;
|
|
const availableSlots = MAX_NUMBER_OF_ITEMS_ON_DASHBOARD - currentMetricCount;
|
|
|
|
if (metricsToAdd.length > availableSlots) {
|
|
return openConfirmModal({
|
|
title: 'Dashboard is full',
|
|
content: `The dashboard is full, please remove some metrics before adding more. You can only have ${MAX_NUMBER_OF_ITEMS_ON_DASHBOARD} metrics on a dashboard at a time.`,
|
|
primaryButtonProps: {
|
|
text: 'Okay'
|
|
},
|
|
onOk: () => {}
|
|
});
|
|
}
|
|
|
|
if (dashboardResponse) {
|
|
const newConfig = addMetricToDashboardConfig(metricIds, dashboardResponse.dashboard.config);
|
|
return dashboardsUpdateDashboard({
|
|
id: dashboardId,
|
|
config: newConfig
|
|
});
|
|
}
|
|
|
|
openErrorMessage('Failed to save metrics to dashboard');
|
|
}
|
|
);
|
|
|
|
return useMutation({
|
|
mutationFn: addMetricToDashboard,
|
|
onMutate: ({ metricIds, dashboardId }) => {
|
|
for (const metricId of metricIds) {
|
|
const highestVersion = getLatestMetricVersion(metricId);
|
|
|
|
// Update the dashboards array for the highest version metric
|
|
if (highestVersion) {
|
|
const options = metricsQueryKeys.metricsGetMetric(metricId, highestVersion);
|
|
queryClient.setQueryData(options.queryKey, (old) => {
|
|
if (!old) return old;
|
|
return create(old, (draft) => {
|
|
draft.dashboards = [...(draft.dashboards || []), { id: dashboardId, name: '' }];
|
|
});
|
|
});
|
|
}
|
|
}
|
|
},
|
|
onSuccess: (data) => {
|
|
if (data) {
|
|
queryClient.setQueryData(
|
|
dashboardQueryKeys.dashboardGetDashboard(data.dashboard.id, data.dashboard.version_number)
|
|
.queryKey,
|
|
data
|
|
);
|
|
for (const metric of Object.values(data.metrics)) {
|
|
const dashboardId = data.dashboard.id;
|
|
const dashboardName = data.dashboard.name;
|
|
const options = metricsQueryKeys.metricsGetMetric(metric.id, metric.version_number);
|
|
queryClient.setQueryData(options.queryKey, (old) => {
|
|
if (!old) return old;
|
|
return create(old, (draft) => {
|
|
draft.dashboards = [
|
|
...(draft.dashboards || []),
|
|
{ id: dashboardId, name: dashboardName }
|
|
];
|
|
});
|
|
});
|
|
}
|
|
|
|
setOriginalDashboard(data.dashboard);
|
|
setLatestDashboardVersion(data.dashboard.id, data.dashboard.version_number);
|
|
}
|
|
}
|
|
});
|
|
};
|
|
|
|
export const useRemoveMetricsFromDashboard = () => {
|
|
const { openConfirmModal, openErrorMessage } = useBusterNotifications();
|
|
const queryClient = useQueryClient();
|
|
const ensureDashboardConfig = useEnsureDashboardConfig(false);
|
|
const setOriginalDashboard = useOriginalDashboardStore((x) => x.setOriginalDashboard);
|
|
const getLatestMetricVersion = useGetLatestMetricVersionMemoized();
|
|
const setLatestDashboardVersion = useDashboardQueryStore((x) => x.onSetLatestDashboardVersion);
|
|
|
|
const removeMetricFromDashboard = useMemoizedFn(
|
|
async ({
|
|
metricIds,
|
|
dashboardId,
|
|
useConfirmModal = true
|
|
}: {
|
|
metricIds: string[];
|
|
dashboardId: string;
|
|
useConfirmModal?: boolean;
|
|
}) => {
|
|
const method = async () => {
|
|
for (const metricId of metricIds) {
|
|
const highestVersion = getLatestMetricVersion(metricId);
|
|
// Update the dashboards array for the highest version metric
|
|
if (highestVersion) {
|
|
const options = metricsQueryKeys.metricsGetMetric(metricId, highestVersion);
|
|
queryClient.setQueryData(options.queryKey, (old) => {
|
|
if (!old) return old;
|
|
return create(old, (draft) => {
|
|
draft.dashboards = old?.dashboards?.filter((d) => d.id !== dashboardId) || [];
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
const dashboardResponse = await ensureDashboardConfig(dashboardId);
|
|
|
|
if (dashboardResponse) {
|
|
const versionedOptions = dashboardQueryKeys.dashboardGetDashboard(
|
|
dashboardResponse.dashboard.id,
|
|
dashboardResponse.dashboard.version_number
|
|
);
|
|
|
|
const newConfig = removeMetricFromDashboardConfig(
|
|
metricIds,
|
|
dashboardResponse.dashboard.config
|
|
);
|
|
|
|
queryClient.setQueryData(versionedOptions.queryKey, (currentDashboard) => {
|
|
if (!currentDashboard) return currentDashboard;
|
|
return create(currentDashboard, (draft) => {
|
|
draft.dashboard.config = newConfig;
|
|
});
|
|
});
|
|
|
|
const data = await dashboardsUpdateDashboard({
|
|
id: dashboardId,
|
|
config: newConfig
|
|
});
|
|
|
|
queryClient.setQueryData(
|
|
dashboardQueryKeys.dashboardGetDashboard(
|
|
data.dashboard.id,
|
|
data.dashboard.version_number
|
|
).queryKey,
|
|
data
|
|
);
|
|
queryClient.setQueryData(
|
|
dashboardQueryKeys.dashboardGetDashboard(
|
|
data.dashboard.id,
|
|
data.dashboard.version_number
|
|
).queryKey,
|
|
data
|
|
);
|
|
setOriginalDashboard(data.dashboard);
|
|
|
|
return data;
|
|
}
|
|
|
|
openErrorMessage('Failed to remove metrics from dashboard');
|
|
};
|
|
|
|
if (!useConfirmModal) return await method();
|
|
|
|
return await openConfirmModal({
|
|
title: 'Remove from dashboard',
|
|
content:
|
|
metricIds.length > 1
|
|
? 'Are you sure you want to remove these metrics from the dashboard?'
|
|
: 'Are you sure you want to remove this metric from the dashboard?',
|
|
onOk: method
|
|
});
|
|
}
|
|
);
|
|
|
|
return useMutation({
|
|
mutationFn: removeMetricFromDashboard,
|
|
onMutate: (variables) => {
|
|
variables.metricIds.forEach((id) => {
|
|
queryClient.setQueryData(metricsQueryKeys.metricsGetMetric(id, null).queryKey, (old) => {
|
|
if (!old) return old;
|
|
return create(old, (draft) => {
|
|
draft.dashboards = old?.dashboards?.filter((d) => d.id !== variables.dashboardId) || [];
|
|
});
|
|
});
|
|
});
|
|
},
|
|
onSuccess: (data, variables) => {
|
|
if (data) {
|
|
setLatestDashboardVersion(data.dashboard.id, data.dashboard.version_number);
|
|
}
|
|
|
|
variables.metricIds.forEach((id) => {
|
|
queryClient.invalidateQueries({
|
|
queryKey: metricsQueryKeys.metricsGetMetric(id, null).queryKey
|
|
});
|
|
});
|
|
}
|
|
});
|
|
};
|
|
|
|
export const useGetDashboardsList = (
|
|
params: Omit<Parameters<typeof dashboardsGetList>[0], 'page_token' | 'page_size'>,
|
|
options?: Omit<
|
|
UseQueryOptions<Awaited<ReturnType<typeof dashboardsGetList>>, RustApiError>,
|
|
'queryKey' | 'queryFn' | 'initialData'
|
|
>
|
|
) => {
|
|
const filters = useMemo(() => {
|
|
return {
|
|
...params,
|
|
page_token: 0,
|
|
page_size: 3500
|
|
};
|
|
}, [params]);
|
|
|
|
return useQuery({
|
|
...dashboardQueryKeys.dashboardGetList(filters),
|
|
queryFn: () => dashboardsGetList(filters),
|
|
...options
|
|
});
|
|
};
|
|
|
|
export const prefetchGetDashboardsList = async (
|
|
queryClient: QueryClient,
|
|
params?: Parameters<typeof dashboardsGetList>[0]
|
|
) => {
|
|
const options = dashboardQueryKeys.dashboardGetList(params);
|
|
const isStale = isQueryStale(options, queryClient);
|
|
if (!isStale || !hasOrganizationId(queryClient)) return queryClient;
|
|
|
|
const lastQueryKey = options.queryKey[options.queryKey.length - 1];
|
|
const compiledParams = lastQueryKey as Parameters<typeof dashboardsGetList>[0];
|
|
|
|
await queryClient.prefetchQuery({ ...options, queryFn: () => dashboardsGetList(compiledParams) });
|
|
|
|
return queryClient;
|
|
};
|