mirror of https://github.com/buster-so/buster.git
versions for dashboard
This commit is contained in:
parent
99b6436418
commit
ba16fcac4a
|
@ -0,0 +1,183 @@
|
|||
import { BusterDashboardResponse } from '@/api/asset_interfaces/dashboard';
|
||||
import { queryKeys } from '@/api/query_keys';
|
||||
import { dashboardQueryKeys } from '@/api/query_keys/dashboard';
|
||||
import { useBusterAssetsContextSelector } from '@/context/Assets/BusterAssetsProvider';
|
||||
import { useBusterNotifications } from '@/context/BusterNotifications';
|
||||
import { useOriginalDashboardStore } from '@/context/Dashboards';
|
||||
import { useMemoizedFn } from '@/hooks/useMemoizedFn';
|
||||
import { upgradeMetricToIMetric } from '@/lib/metrics/upgradeToIMetric';
|
||||
import { Query, useQueryClient } from '@tanstack/react-query';
|
||||
import { prefetchGetMetricDataClient } from '../metrics/queryRequests';
|
||||
import { dashboardsGetDashboard } from './requests';
|
||||
import { useParams, useSearchParams } from 'next/navigation';
|
||||
import { useMemo } from 'react';
|
||||
import { RustApiError } from '../errors';
|
||||
import last from 'lodash/last';
|
||||
|
||||
export const useEnsureDashboardConfig = (prefetchData: boolean = true) => {
|
||||
const queryClient = useQueryClient();
|
||||
const prefetchDashboard = useGetDashboardAndInitializeMetrics(prefetchData);
|
||||
const { openErrorMessage } = useBusterNotifications();
|
||||
const getLatestDashboardVersion = useGetLatestDashboardVersionNumber();
|
||||
|
||||
const method = useMemoizedFn(async (dashboardId: string) => {
|
||||
const latestVersion = getLatestDashboardVersion(dashboardId);
|
||||
const options = dashboardQueryKeys.dashboardGetDashboard(dashboardId, latestVersion);
|
||||
let dashboardResponse = queryClient.getQueryData(options.queryKey);
|
||||
if (!dashboardResponse) {
|
||||
const res = await prefetchDashboard(dashboardId, latestVersion || undefined).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 useGetDashboardAndInitializeMetrics = (prefetchData: boolean = true) => {
|
||||
const queryClient = useQueryClient();
|
||||
const setOriginalDashboards = useOriginalDashboardStore((x) => x.setOriginalDashboard);
|
||||
const getAssetPassword = useBusterAssetsContextSelector((state) => state.getAssetPassword);
|
||||
|
||||
const initializeMetrics = useMemoizedFn((metrics: BusterDashboardResponse['metrics']) => {
|
||||
for (const metric of Object.values(metrics)) {
|
||||
const prevMetric = queryClient.getQueryData(
|
||||
queryKeys.metricsGetMetric(metric.id, metric.version_number).queryKey
|
||||
);
|
||||
const upgradedMetric = upgradeMetricToIMetric(metric, prevMetric);
|
||||
queryClient.setQueryData(
|
||||
queryKeys.metricsGetMetric(metric.id, metric.version_number).queryKey,
|
||||
upgradedMetric
|
||||
);
|
||||
if (prefetchData) {
|
||||
prefetchGetMetricDataClient(
|
||||
{ id: metric.id, version_number: metric.version_number },
|
||||
queryClient
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return useMemoizedFn(async (id: string, version_number: number | null | undefined) => {
|
||||
const { password } = getAssetPassword?.(id) || {};
|
||||
|
||||
return dashboardsGetDashboard({
|
||||
id: id!,
|
||||
password,
|
||||
version_number: version_number || undefined
|
||||
}).then((data) => {
|
||||
initializeMetrics(data.metrics);
|
||||
setOriginalDashboards(data.dashboard);
|
||||
|
||||
if (!version_number && data.dashboard.version_number) {
|
||||
queryClient.setQueryData(
|
||||
dashboardQueryKeys.dashboardGetDashboard(id, data.dashboard.version_number).queryKey,
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
return data;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetDashboardVersionNumber = (props?: {
|
||||
versionNumber?: number | null; //if null it will not use a params from the query params
|
||||
}) => {
|
||||
const { versionNumber: versionNumberProp } = props || {};
|
||||
const { dashboardId: dashboardIdPathParam } = useParams() as {
|
||||
dashboardId: string | undefined;
|
||||
};
|
||||
const versionNumberQueryParam = useSearchParams().get('dashboard_version_number');
|
||||
const versionNumberFromParams = dashboardIdPathParam ? versionNumberQueryParam : undefined;
|
||||
|
||||
const paramVersionNumber = useMemo(() => {
|
||||
return (
|
||||
versionNumberProp ??
|
||||
(versionNumberFromParams ? parseInt(versionNumberFromParams!) : undefined)
|
||||
);
|
||||
}, [versionNumberProp, versionNumberFromParams]);
|
||||
|
||||
const latestVersionNumber = useGetLatestDashboardVersion({ dashboardId: dashboardIdPathParam! });
|
||||
|
||||
const selectedVersionNumber: number | null = useMemo(() => {
|
||||
if (versionNumberProp === null) return null;
|
||||
return paramVersionNumber || latestVersionNumber || 0;
|
||||
}, [paramVersionNumber, latestVersionNumber]);
|
||||
|
||||
return useMemo(() => {
|
||||
return { selectedVersionNumber, paramVersionNumber, latestVersionNumber };
|
||||
}, [selectedVersionNumber, selectedVersionNumber, latestVersionNumber]);
|
||||
};
|
||||
|
||||
type PredicateType = (query: Query<BusterDashboardResponse, RustApiError>) => boolean;
|
||||
const filterMetricPredicate = <PredicateType>((query) => {
|
||||
const lastKey = last(query.queryKey);
|
||||
return (
|
||||
typeof lastKey === 'number' &&
|
||||
!!lastKey &&
|
||||
query.state.data !== undefined &&
|
||||
typeof query.state.data === 'object' &&
|
||||
query.state.data !== null &&
|
||||
'versions' in query.state.data
|
||||
);
|
||||
});
|
||||
|
||||
const getLatestVersionNumber = (
|
||||
queries: [readonly unknown[], BusterDashboardResponse | undefined][]
|
||||
) => {
|
||||
let maxVersion = -Infinity;
|
||||
|
||||
// Single pass: filter and find max version
|
||||
for (const [queryKey, data] of queries) {
|
||||
if (data && typeof data === 'object' && 'versions' in data) {
|
||||
const lastVersion = last(data.versions);
|
||||
const version = Number(lastVersion?.version_number);
|
||||
maxVersion = Math.max(maxVersion, version, data.dashboard.version_number);
|
||||
}
|
||||
}
|
||||
|
||||
return maxVersion === -Infinity ? null : maxVersion;
|
||||
};
|
||||
|
||||
const useGetLatestDashboardVersion = ({ dashboardId }: { dashboardId: string }) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const memoizedKey = useMemo(() => {
|
||||
return dashboardQueryKeys.dashboardGetDashboard(dashboardId, null).queryKey.slice(0, -1);
|
||||
}, [dashboardId]);
|
||||
|
||||
const queries = queryClient.getQueriesData<BusterDashboardResponse, any>({
|
||||
queryKey: memoizedKey,
|
||||
predicate: filterMetricPredicate
|
||||
});
|
||||
|
||||
const latestVersion = useMemo(() => {
|
||||
return getLatestVersionNumber(queries);
|
||||
}, [queries.length]);
|
||||
|
||||
return latestVersion;
|
||||
};
|
||||
|
||||
//This is a helper function that returns the latest version number for a metric
|
||||
export const useGetLatestDashboardVersionNumber = () => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const method = useMemoizedFn((dashboardId: string) => {
|
||||
const queries = queryClient.getQueriesData<BusterDashboardResponse, any>({
|
||||
queryKey: dashboardQueryKeys.dashboardGetDashboard(dashboardId, null).queryKey.slice(0, -1),
|
||||
predicate: filterMetricPredicate
|
||||
});
|
||||
|
||||
return getLatestVersionNumber(queries);
|
||||
});
|
||||
|
||||
return method;
|
||||
};
|
|
@ -1,109 +0,0 @@
|
|||
import { BusterDashboardResponse } from '@/api/asset_interfaces/dashboard';
|
||||
import { IBusterMetric } from '@/api/asset_interfaces/metric';
|
||||
import { queryKeys } from '@/api/query_keys';
|
||||
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/useMemoizedFn';
|
||||
import { upgradeMetricToIMetric } from '@/lib/metrics/upgradeToIMetric';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { prefetchGetMetricDataClient } from '../metrics/queryRequests';
|
||||
import { dashboardsGetDashboard } from './requests';
|
||||
import { useParams, useSearchParams } from 'next/navigation';
|
||||
import { useMemo } from 'react';
|
||||
|
||||
export const useEnsureDashboardConfig = (prefetchData: boolean = true) => {
|
||||
const queryClient = useQueryClient();
|
||||
const versionNumber = useGetDashboardVersionNumber();
|
||||
const prefetchDashboard = useGetDashboardAndInitializeMetrics(prefetchData);
|
||||
const { openErrorMessage } = useBusterNotifications();
|
||||
|
||||
const method = useMemoizedFn(async (dashboardId: string) => {
|
||||
const options = dashboardQueryKeys.dashboardGetDashboard(dashboardId, versionNumber);
|
||||
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 useGetDashboardAndInitializeMetrics = (prefetchData: boolean = true) => {
|
||||
const queryClient = useQueryClient();
|
||||
const setOriginalDashboards = useOriginalDashboardStore((x) => x.setOriginalDashboard);
|
||||
const getAssetPassword = useBusterAssetsContextSelector((state) => state.getAssetPassword);
|
||||
|
||||
const initializeMetrics = useMemoizedFn((metrics: BusterDashboardResponse['metrics']) => {
|
||||
for (const metric of Object.values(metrics)) {
|
||||
const prevMetric = queryClient.getQueryData(
|
||||
queryKeys.metricsGetMetric(metric.id, metric.version_number).queryKey
|
||||
);
|
||||
const upgradedMetric = upgradeMetricToIMetric(metric, prevMetric);
|
||||
queryClient.setQueryData(
|
||||
queryKeys.metricsGetMetric(metric.id, metric.version_number).queryKey,
|
||||
upgradedMetric
|
||||
);
|
||||
if (prefetchData) {
|
||||
prefetchGetMetricDataClient(
|
||||
{ id: metric.id, version_number: metric.version_number },
|
||||
queryClient
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
return useMemoizedFn(async (id: string, version_number?: number) => {
|
||||
const { password } = getAssetPassword?.(id) || {};
|
||||
|
||||
return dashboardsGetDashboard({ id: id!, password, version_number }).then((data) => {
|
||||
initializeMetrics(data.metrics);
|
||||
setOriginalDashboards(data.dashboard);
|
||||
|
||||
if (!version_number && data.dashboard.version_number) {
|
||||
queryClient.setQueryData(
|
||||
dashboardQueryKeys.dashboardGetDashboard(id, data.dashboard.version_number).queryKey,
|
||||
data
|
||||
);
|
||||
}
|
||||
|
||||
return data;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
export const useGetDashboardVersionNumber = (props?: {
|
||||
versionNumber?: number | null; //if null it will not use a params from the query params
|
||||
}) => {
|
||||
const { versionNumber: versionNumberProp } = props || {};
|
||||
const { versionNumber: versionNumberPathParam, dashboardId: dashboardIdPathParam } =
|
||||
useParams() as {
|
||||
versionNumber: string | undefined;
|
||||
dashboardId: string | undefined;
|
||||
};
|
||||
const versionNumberQueryParam = useSearchParams().get('dashboard_version_number');
|
||||
const versionNumberFromParams = dashboardIdPathParam
|
||||
? versionNumberQueryParam || versionNumberPathParam
|
||||
: undefined;
|
||||
|
||||
const versionNumber = useMemo(() => {
|
||||
if (versionNumberProp === null) return undefined;
|
||||
return (
|
||||
versionNumberProp ??
|
||||
(versionNumberFromParams ? parseInt(versionNumberFromParams!) : undefined)
|
||||
);
|
||||
}, [versionNumberProp, versionNumberFromParams]);
|
||||
|
||||
return versionNumber;
|
||||
};
|
|
@ -32,8 +32,9 @@ import {
|
|||
useGetDashboardAndInitializeMetrics,
|
||||
useGetDashboardVersionNumber,
|
||||
useEnsureDashboardConfig
|
||||
} from './queryHelpers';
|
||||
} from './dashboardQueryHelpers';
|
||||
import { useGetLatestMetricVersionNumber } from '../metrics';
|
||||
import { useBusterAssetsContextSelector } from '@/context/Assets/BusterAssetsProvider';
|
||||
|
||||
export const useGetDashboard = <TData = BusterDashboardResponse>(
|
||||
{
|
||||
|
@ -46,14 +47,29 @@ export const useGetDashboard = <TData = BusterDashboardResponse>(
|
|||
>
|
||||
) => {
|
||||
const queryFn = useGetDashboardAndInitializeMetrics();
|
||||
const versionNumber = useGetDashboardVersionNumber({ versionNumber: versionNumberProp });
|
||||
const setAssetPasswordError = useBusterAssetsContextSelector((x) => x.setAssetPasswordError);
|
||||
const { selectedVersionNumber, latestVersionNumber, paramVersionNumber } =
|
||||
useGetDashboardVersionNumber({ versionNumber: versionNumberProp });
|
||||
|
||||
const { isFetched: isFetchedInitial, isError: isErrorInitial } = useQuery({
|
||||
...dashboardQueryKeys.dashboardGetDashboard(id!, null),
|
||||
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!, versionNumber),
|
||||
queryFn: () => queryFn(id!, versionNumber),
|
||||
enabled: false, //we made this false because we want to be explicit about the fact that we fetch the dashboard server side
|
||||
select: params?.select,
|
||||
...params
|
||||
...dashboardQueryKeys.dashboardGetDashboard(id!, selectedVersionNumber),
|
||||
queryFn: () => queryFn(id!, selectedVersionNumber),
|
||||
enabled: !!latestVersionNumber && isFetchedInitial && !isErrorInitial,
|
||||
select: params?.select
|
||||
});
|
||||
};
|
||||
|
||||
|
@ -72,7 +88,6 @@ export const useSaveDashboard = (params?: { updateOnSave?: boolean }) => {
|
|||
const updateOnSave = params?.updateOnSave || false;
|
||||
const queryClient = useQueryClient();
|
||||
const setOriginalDashboard = useOriginalDashboardStore((x) => x.setOriginalDashboard);
|
||||
const versionNumber = useGetDashboardVersionNumber();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: dashboardsUpdateDashboard,
|
||||
|
@ -89,13 +104,6 @@ export const useSaveDashboard = (params?: { updateOnSave?: boolean }) => {
|
|||
.queryKey,
|
||||
data
|
||||
);
|
||||
//We need to update BOTH the versioned and the non-versioned metric for version updates to keep the latest up to date
|
||||
if (variables.update_version || variables.restore_to_version) {
|
||||
queryClient.setQueryData(
|
||||
dashboardQueryKeys.dashboardGetDashboard(data.dashboard.id, undefined).queryKey,
|
||||
data
|
||||
);
|
||||
}
|
||||
setOriginalDashboard(data.dashboard);
|
||||
}
|
||||
}
|
||||
|
@ -110,7 +118,7 @@ export const useUpdateDashboard = (params?: {
|
|||
const { updateOnSave = false, updateVersion = false, saveToServer = false } = params || {};
|
||||
const queryClient = useQueryClient();
|
||||
const { mutateAsync: saveDashboard } = useSaveDashboard({ updateOnSave });
|
||||
const versionNumber = useGetDashboardVersionNumber();
|
||||
const { latestVersionNumber } = useGetDashboardVersionNumber();
|
||||
const getOriginalDashboard = useOriginalDashboardStore((x) => x.getOriginalDashboard);
|
||||
|
||||
const mutationFn = useMemoizedFn(
|
||||
|
@ -133,8 +141,7 @@ export const useUpdateDashboard = (params?: {
|
|||
});
|
||||
const queryKey = dashboardQueryKeys.dashboardGetDashboard(
|
||||
variables.id,
|
||||
versionNumber
|
||||
// updatedDashboard.version_number
|
||||
latestVersionNumber
|
||||
).queryKey;
|
||||
|
||||
queryClient.setQueryData(queryKey, (previousData) => {
|
||||
|
@ -152,7 +159,7 @@ export const useUpdateDashboardConfig = () => {
|
|||
updateVersion: false
|
||||
});
|
||||
const queryClient = useQueryClient();
|
||||
const versionNumber = useGetDashboardVersionNumber();
|
||||
const { latestVersionNumber } = useGetDashboardVersionNumber();
|
||||
|
||||
const method = useMemoizedFn(
|
||||
async ({
|
||||
|
@ -161,7 +168,7 @@ export const useUpdateDashboardConfig = () => {
|
|||
}: Partial<BusterDashboard['config']> & {
|
||||
dashboardId: string;
|
||||
}) => {
|
||||
const options = dashboardQueryKeys.dashboardGetDashboard(dashboardId, versionNumber);
|
||||
const options = dashboardQueryKeys.dashboardGetDashboard(dashboardId, latestVersionNumber);
|
||||
const previousDashboard = queryClient.getQueryData(options.queryKey);
|
||||
const previousConfig = previousDashboard?.dashboard?.config;
|
||||
if (previousConfig) {
|
||||
|
@ -304,13 +311,13 @@ export const useRemoveDashboardFromCollection = () => {
|
|||
|
||||
export const useShareDashboard = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const versionNumber = useGetDashboardVersionNumber();
|
||||
const { latestVersionNumber } = useGetDashboardVersionNumber();
|
||||
return useMutation({
|
||||
mutationFn: shareDashboard,
|
||||
onMutate: (variables) => {
|
||||
const queryKey = dashboardQueryKeys.dashboardGetDashboard(
|
||||
variables.id,
|
||||
versionNumber
|
||||
latestVersionNumber
|
||||
).queryKey;
|
||||
queryClient.setQueryData(queryKey, (previousData) => {
|
||||
return create(previousData!, (draft) => {
|
||||
|
@ -323,7 +330,8 @@ export const useShareDashboard = () => {
|
|||
},
|
||||
onSuccess: (data) => {
|
||||
queryClient.setQueryData(
|
||||
dashboardQueryKeys.dashboardGetDashboard(data.dashboard.id, versionNumber).queryKey,
|
||||
dashboardQueryKeys.dashboardGetDashboard(data.dashboard.id, data.dashboard.version_number)
|
||||
.queryKey,
|
||||
data
|
||||
);
|
||||
}
|
||||
|
@ -332,13 +340,13 @@ export const useShareDashboard = () => {
|
|||
|
||||
export const useUnshareDashboard = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const versionNumber = useGetDashboardVersionNumber();
|
||||
const { latestVersionNumber } = useGetDashboardVersionNumber();
|
||||
return useMutation({
|
||||
mutationFn: unshareDashboard,
|
||||
onMutate: (variables) => {
|
||||
const queryKey = dashboardQueryKeys.dashboardGetDashboard(
|
||||
variables.id,
|
||||
versionNumber
|
||||
latestVersionNumber
|
||||
).queryKey;
|
||||
queryClient.setQueryData(queryKey, (previousData) => {
|
||||
return create(previousData!, (draft) => {
|
||||
|
@ -349,7 +357,8 @@ export const useUnshareDashboard = () => {
|
|||
},
|
||||
onSuccess: (data) => {
|
||||
queryClient.setQueryData(
|
||||
dashboardQueryKeys.dashboardGetDashboard(data.dashboard.id, versionNumber).queryKey,
|
||||
dashboardQueryKeys.dashboardGetDashboard(data.dashboard.id, data.dashboard.version_number)
|
||||
.queryKey,
|
||||
data
|
||||
);
|
||||
}
|
||||
|
@ -358,11 +367,11 @@ export const useUnshareDashboard = () => {
|
|||
|
||||
export const useUpdateDashboardShare = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const versionNumber = useGetDashboardVersionNumber();
|
||||
const { latestVersionNumber } = useGetDashboardVersionNumber();
|
||||
return useMutation({
|
||||
mutationFn: updateDashboardShare,
|
||||
onMutate: ({ id, params }) => {
|
||||
const queryKey = dashboardQueryKeys.dashboardGetDashboard(id, versionNumber).queryKey;
|
||||
const queryKey = dashboardQueryKeys.dashboardGetDashboard(id, latestVersionNumber).queryKey;
|
||||
queryClient.setQueryData(queryKey, (previousData) => {
|
||||
return create(previousData!, (draft) => {
|
||||
draft.individual_permissions =
|
||||
|
@ -429,10 +438,6 @@ export const useAddAndRemoveMetricsFromDashboard = () => {
|
|||
mutationFn: addAndRemoveMetrics,
|
||||
onSuccess: (data, variables) => {
|
||||
if (data) {
|
||||
queryClient.setQueryData(
|
||||
dashboardQueryKeys.dashboardGetDashboard(data.dashboard.id, undefined).queryKey,
|
||||
data
|
||||
);
|
||||
queryClient.setQueryData(
|
||||
dashboardQueryKeys.dashboardGetDashboard(data.dashboard.id, data.dashboard.version_number)
|
||||
.queryKey,
|
||||
|
@ -486,10 +491,6 @@ export const useAddMetricsToDashboard = () => {
|
|||
},
|
||||
onSuccess: (data) => {
|
||||
if (data) {
|
||||
queryClient.setQueryData(
|
||||
dashboardQueryKeys.dashboardGetDashboard(data.dashboard.id, undefined).queryKey,
|
||||
data
|
||||
);
|
||||
queryClient.setQueryData(
|
||||
dashboardQueryKeys.dashboardGetDashboard(data.dashboard.id, data.dashboard.version_number)
|
||||
.queryKey,
|
||||
|
@ -554,10 +555,7 @@ export const useRemoveMetricsFromDashboard = () => {
|
|||
dashboardResponse.dashboard.id,
|
||||
dashboardResponse.dashboard.version_number
|
||||
);
|
||||
const nonVersionedOptions = dashboardQueryKeys.dashboardGetDashboard(
|
||||
dashboardResponse.dashboard.id,
|
||||
undefined
|
||||
);
|
||||
|
||||
const newConfig = removeMetricFromDashboardConfig(
|
||||
metricIds,
|
||||
dashboardResponse.dashboard.config
|
||||
|
@ -568,11 +566,6 @@ export const useRemoveMetricsFromDashboard = () => {
|
|||
draft.dashboard.config = newConfig;
|
||||
});
|
||||
});
|
||||
queryClient.setQueryData(nonVersionedOptions.queryKey, (currentDashboard) => {
|
||||
return create(currentDashboard!, (draft) => {
|
||||
draft.dashboard.config = newConfig;
|
||||
});
|
||||
});
|
||||
|
||||
const data = await dashboardsUpdateDashboard({
|
||||
id: dashboardId,
|
||||
|
@ -580,7 +573,10 @@ export const useRemoveMetricsFromDashboard = () => {
|
|||
});
|
||||
|
||||
queryClient.setQueryData(
|
||||
dashboardQueryKeys.dashboardGetDashboard(data.dashboard.id, undefined).queryKey,
|
||||
dashboardQueryKeys.dashboardGetDashboard(
|
||||
data.dashboard.id,
|
||||
data.dashboard.version_number
|
||||
).queryKey,
|
||||
data
|
||||
);
|
||||
queryClient.setQueryData(
|
||||
|
|
|
@ -9,7 +9,7 @@ export const prefetchGetDashboard = async (
|
|||
const queryClient = queryClientProp || new QueryClient();
|
||||
|
||||
await queryClient.prefetchQuery({
|
||||
...dashboardQueryKeys.dashboardGetDashboard(params.id, params.version_number),
|
||||
...dashboardQueryKeys.dashboardGetDashboard(params.id, params.version_number || null),
|
||||
queryFn: async () => {
|
||||
return await getDashboard_server(params);
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import { Query, useQueryClient } from '@tanstack/react-query';
|
|||
import { IBusterMetric } from '@/api/asset_interfaces/metric';
|
||||
import { metricsQueryKeys } from '@/api/query_keys/metric';
|
||||
import last from 'lodash/last';
|
||||
import { INFINITY } from 'chart.js/helpers';
|
||||
import { useMemoizedFn } from '@/hooks';
|
||||
import { RustApiError } from '../errors';
|
||||
|
||||
|
@ -48,6 +47,22 @@ const filterMetricPredicate = <PredicateType>((query) => {
|
|||
);
|
||||
});
|
||||
|
||||
const getLatestVersionNumber = (queries: [readonly unknown[], IBusterMetric | undefined][]) => {
|
||||
let latestVersion = -Infinity;
|
||||
|
||||
for (const [queryKey, data] of queries) {
|
||||
if (data && typeof data === 'object' && 'versions' in data) {
|
||||
const lastVersion = last(data.versions);
|
||||
const version = Number(lastVersion?.version_number);
|
||||
if (!isNaN(version)) {
|
||||
latestVersion = Math.max(latestVersion, version, data.version_number);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return latestVersion;
|
||||
};
|
||||
|
||||
const useGetLatestMetricVersion = ({ metricId }: { metricId: string }) => {
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
|
@ -61,18 +76,7 @@ const useGetLatestMetricVersion = ({ metricId }: { metricId: string }) => {
|
|||
});
|
||||
|
||||
const latestVersion = useMemo(() => {
|
||||
let maxVersion = -Infinity;
|
||||
|
||||
// Single pass: filter and find max version
|
||||
for (const [queryKey, data] of queries) {
|
||||
if (data && typeof data === 'object' && 'versions' in data) {
|
||||
const lastVersion = last(data.versions);
|
||||
const version = Number(lastVersion?.version_number);
|
||||
maxVersion = Math.max(maxVersion, version, data.version_number);
|
||||
}
|
||||
}
|
||||
|
||||
return maxVersion === -Infinity ? null : maxVersion;
|
||||
return getLatestVersionNumber(queries);
|
||||
}, [queries.length]);
|
||||
|
||||
return latestVersion;
|
||||
|
@ -88,19 +92,7 @@ export const useGetLatestMetricVersionNumber = () => {
|
|||
predicate: filterMetricPredicate
|
||||
});
|
||||
|
||||
let latestVersion = -INFINITY;
|
||||
|
||||
for (const [queryKey, data] of queries) {
|
||||
if (data && typeof data === 'object' && 'versions' in data) {
|
||||
const lastVersion = last(data.versions);
|
||||
const version = Number(lastVersion?.version_number);
|
||||
if (!isNaN(version)) {
|
||||
latestVersion = Math.max(latestVersion, version, data.version_number);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return latestVersion;
|
||||
return getLatestVersionNumber(queries);
|
||||
});
|
||||
|
||||
return method;
|
||||
|
|
|
@ -13,7 +13,7 @@ export const prefetchGetMetric = async (
|
|||
const queryClient = queryClientProp || new QueryClient();
|
||||
|
||||
await queryClient.prefetchQuery({
|
||||
...metricsQueryKeys.metricsGetMetric(params.id, params.version_number),
|
||||
...metricsQueryKeys.metricsGetMetric(params.id, params.version_number || null),
|
||||
queryFn: async () => {
|
||||
const result = await getMetric_server(params);
|
||||
return upgradeMetricToIMetric(result, null);
|
||||
|
|
|
@ -27,7 +27,7 @@ export const useSaveMetric = (params?: { updateOnSave?: boolean }) => {
|
|||
const updateOnSave = params?.updateOnSave || false;
|
||||
const queryClient = useQueryClient();
|
||||
const setOriginalMetric = useOriginalMetricStore((x) => x.setOriginalMetric);
|
||||
const { latestVersionNumber, selectedVersionNumber } = useGetMetricVersionNumber();
|
||||
const { latestVersionNumber } = useGetMetricVersionNumber();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: updateMetric,
|
||||
|
@ -37,7 +37,7 @@ export const useSaveMetric = (params?: { updateOnSave?: boolean }) => {
|
|||
//set the current metric to the previous it is being restored to
|
||||
if (isRestoringVersion) {
|
||||
const oldMetric = queryClient.getQueryData(
|
||||
metricsQueryKeys.metricsGetMetric(id, selectedVersionNumber).queryKey
|
||||
metricsQueryKeys.metricsGetMetric(id, latestVersionNumber).queryKey
|
||||
);
|
||||
const newMetric = queryClient.getQueryData(
|
||||
metricsQueryKeys.metricsGetMetric(id, restore_to_version).queryKey
|
||||
|
@ -60,7 +60,7 @@ export const useSaveMetric = (params?: { updateOnSave?: boolean }) => {
|
|||
|
||||
if (isUpdatingVersion) {
|
||||
const metric = queryClient.getQueryData(
|
||||
metricsQueryKeys.metricsGetMetric(id, selectedVersionNumber).queryKey
|
||||
metricsQueryKeys.metricsGetMetric(id, latestVersionNumber).queryKey
|
||||
);
|
||||
if (!metric) return;
|
||||
const metricVersionNumber = metric?.version_number;
|
||||
|
|
|
@ -15,9 +15,9 @@ const dashboardGetList = (
|
|||
initialDataUpdatedAt: 0
|
||||
});
|
||||
|
||||
const dashboardGetDashboard = (dashboardId: string, version_number: number | undefined) =>
|
||||
const dashboardGetDashboard = (dashboardId: string, version_number: number | null) =>
|
||||
queryOptions<BusterDashboardResponse>({
|
||||
queryKey: ['dashboard', 'get', dashboardId, version_number || 'latest'] as const,
|
||||
queryKey: ['dashboard', 'get', dashboardId, version_number || 'INITIAL'] as const,
|
||||
staleTime: 10 * 1000
|
||||
});
|
||||
|
||||
|
|
|
@ -82,12 +82,12 @@ const adminTools: ISidebarGroup = {
|
|||
route: BusterRoutes.APP_LOGS,
|
||||
id: BusterRoutes.APP_LOGS
|
||||
},
|
||||
{
|
||||
label: 'Terms & Definitions',
|
||||
icon: <BookOpen4 />,
|
||||
route: BusterRoutes.APP_TERMS,
|
||||
id: BusterRoutes.APP_TERMS
|
||||
},
|
||||
// {
|
||||
// label: 'Terms & Definitions',
|
||||
// icon: <BookOpen4 />,
|
||||
// route: BusterRoutes.APP_TERMS,
|
||||
// id: BusterRoutes.APP_TERMS
|
||||
// },
|
||||
{
|
||||
label: 'Datasets',
|
||||
icon: <Table />,
|
||||
|
|
|
@ -11,7 +11,10 @@ export const PopupContainer: React.FC<{
|
|||
<AnimatePresence mode="wait">
|
||||
{show && (
|
||||
<motion.div
|
||||
className={cn('absolute bottom-10 z-50 flex w-full justify-center', className)}
|
||||
className={cn(
|
||||
'absolute right-0 bottom-10 left-0 z-50 flex w-full justify-center',
|
||||
className
|
||||
)}
|
||||
initial={{ opacity: 0, y: -3 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -3 }}
|
||||
|
|
|
@ -30,7 +30,7 @@ const useBusterAssets = () => {
|
|||
});
|
||||
} else if (type === 'dashboard') {
|
||||
await queryClient.invalidateQueries({
|
||||
queryKey: queryKeys.dashboardGetDashboard(assetId, undefined).queryKey
|
||||
queryKey: queryKeys.dashboardGetDashboard(assetId, null).queryKey
|
||||
});
|
||||
} else if (type === 'collection') {
|
||||
await queryClient.invalidateQueries({
|
||||
|
|
|
@ -5,7 +5,7 @@ import { useQueryClient } from '@tanstack/react-query';
|
|||
export const useGetDashboardMemoized = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const getDashboardMemoized = useMemoizedFn((dashboardId: string, versionNumber?: number) => {
|
||||
const options = dashboardQueryKeys.dashboardGetDashboard(dashboardId, versionNumber);
|
||||
const options = dashboardQueryKeys.dashboardGetDashboard(dashboardId, versionNumber || null);
|
||||
const data = queryClient.getQueryData(options.queryKey);
|
||||
return data;
|
||||
});
|
||||
|
|
|
@ -7,16 +7,11 @@ import { dashboardQueryKeys } from '@/api/query_keys/dashboard';
|
|||
import { compareObjectsByKeys } from '@/lib/objects';
|
||||
import { useMemo } from 'react';
|
||||
import { create } from 'mutative';
|
||||
import last from 'lodash/last';
|
||||
|
||||
export const useIsDashboardChanged = ({ dashboardId }: { dashboardId: string }) => {
|
||||
const queryClient = useQueryClient();
|
||||
const originalDashboard = useOriginalDashboardStore((x) => x.getOriginalDashboard(dashboardId));
|
||||
|
||||
// const { data: latestVersionNumber } = useGetDashboard(
|
||||
// { id: dashboardId },
|
||||
// { select: (x) => last(x.versions)?.version_number }
|
||||
// );
|
||||
|
||||
const { data: currentDashboard, refetch: refetchCurrentDashboard } = useGetDashboard(
|
||||
{ id: dashboardId, versionNumber: undefined },
|
||||
{
|
||||
|
@ -32,7 +27,7 @@ export const useIsDashboardChanged = ({ dashboardId }: { dashboardId: string })
|
|||
const onResetDashboardToOriginal = useMemoizedFn(() => {
|
||||
const options = dashboardQueryKeys.dashboardGetDashboard(
|
||||
dashboardId,
|
||||
originalDashboard?.version_number
|
||||
originalDashboard?.version_number || null
|
||||
);
|
||||
const currentDashboard = queryClient.getQueryData<BusterDashboardResponse>(options.queryKey);
|
||||
if (originalDashboard && currentDashboard) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import type { IBusterMetric } from '@/api/asset_interfaces/metric';
|
||||
import type { IBusterMetric, IBusterMetricData } from '@/api/asset_interfaces/metric';
|
||||
import { useGetMetricVersionNumber } from '@/api/buster_rest/metrics';
|
||||
import { queryKeys } from '@/api/query_keys';
|
||||
import { useMemoizedFn } from '@/hooks';
|
||||
|
@ -7,7 +7,7 @@ import { useQueryClient } from '@tanstack/react-query';
|
|||
|
||||
export const useGetMetricMemoized = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const { selectedVersionNumber } = useGetMetricVersionNumber({});
|
||||
const { selectedVersionNumber } = useGetMetricVersionNumber();
|
||||
const getMetricMemoized = useMemoizedFn(
|
||||
(metricId: string, versionNumberProp?: number): IBusterMetric => {
|
||||
const options = queryKeys.metricsGetMetric(
|
||||
|
@ -20,3 +20,19 @@ export const useGetMetricMemoized = () => {
|
|||
);
|
||||
return getMetricMemoized;
|
||||
};
|
||||
|
||||
export const useGetMetricDataMemoized = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const { selectedVersionNumber, latestVersionNumber } = useGetMetricVersionNumber();
|
||||
const getMetricDataMemoized = useMemoizedFn(
|
||||
(metricId: string, versionNumberProp?: number): IBusterMetricData | undefined => {
|
||||
const options = queryKeys.metricsGetData(
|
||||
metricId,
|
||||
versionNumberProp || selectedVersionNumber || latestVersionNumber!
|
||||
);
|
||||
const data = queryClient.getQueryData(options.queryKey);
|
||||
return data;
|
||||
}
|
||||
);
|
||||
return getMetricDataMemoized;
|
||||
};
|
||||
|
|
|
@ -30,7 +30,10 @@ export const useIsMetricChanged = ({ metricId }: { metricId: string }) => {
|
|||
);
|
||||
|
||||
const onResetMetricToOriginal = useMemoizedFn(() => {
|
||||
const options = metricsQueryKeys.metricsGetMetric(metricId, originalMetric?.version_number);
|
||||
const options = metricsQueryKeys.metricsGetMetric(
|
||||
metricId,
|
||||
originalMetric?.version_number || null
|
||||
);
|
||||
if (originalMetric) {
|
||||
queryClient.setQueryData(options.queryKey, originalMetric);
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@ import type {
|
|||
BusterDashboardResponse,
|
||||
DashboardConfig
|
||||
} from '@/api/asset_interfaces';
|
||||
import { DashboardEmptyState } from './DashboardEmptyState';
|
||||
import { DashboardEmptyState, DashboardNoContentReadOnly } from './DashboardEmptyState';
|
||||
import { type useUpdateDashboardConfig } from '@/api/buster_rest/dashboards';
|
||||
import last from 'lodash/last';
|
||||
|
||||
|
@ -136,7 +136,9 @@ export const DashboardContentController: React.FC<{
|
|||
</DashboardContentControllerProvider>
|
||||
) : !readOnly ? (
|
||||
<DashboardEmptyState onOpenAddContentModal={onOpenAddContentModal} />
|
||||
) : null}
|
||||
) : (
|
||||
<DashboardNoContentReadOnly />
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import { Button } from '@/components/ui/buttons';
|
||||
import { Plus } from '@/components/ui/icons';
|
||||
import React from 'react';
|
||||
import { Text } from '@/components/ui/typography';
|
||||
|
||||
export const DashboardEmptyState: React.FC<{
|
||||
onOpenAddContentModal: () => void;
|
||||
|
@ -14,3 +15,12 @@ export const DashboardEmptyState: React.FC<{
|
|||
);
|
||||
});
|
||||
DashboardEmptyState.displayName = 'DashboardEmptyState';
|
||||
|
||||
export const DashboardNoContentReadOnly: React.FC = React.memo(() => {
|
||||
return (
|
||||
<div className="border-border bg-background flex min-h-56 flex-col items-center justify-center rounded-md border border-dashed shadow">
|
||||
<Text variant="secondary">No items added to dashboard</Text>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
DashboardNoContentReadOnly.displayName = 'DashboardNoContentReadOnly';
|
||||
|
|
|
@ -28,6 +28,7 @@ export const MetricViewChartHeader: React.FC<{
|
|||
level={4}
|
||||
readOnly={readOnly}
|
||||
inputClassName="h-auto!"
|
||||
placeholder="New chart"
|
||||
onChange={onSetTitle}>
|
||||
{name}
|
||||
</EditableTitle>
|
||||
|
|
|
@ -11,13 +11,14 @@ import { useQueryClient } from '@tanstack/react-query';
|
|||
import { useRef } from 'react';
|
||||
import { didColumnDataChange, simplifyChatConfigForSQLChange } from './helpers';
|
||||
import { useRunSQL as useRunSQLQuery } from '@/api/buster_rest';
|
||||
import { useUpdateMetric } from '@/api/buster_rest/metrics';
|
||||
import { useGetMetricMemoized } from '@/context/Metrics';
|
||||
import { useGetLatestMetricVersionNumber, useUpdateMetric } from '@/api/buster_rest/metrics';
|
||||
import { useGetMetricDataMemoized, useGetMetricMemoized } from '@/context/Metrics';
|
||||
import { timeout } from '@/lib';
|
||||
|
||||
export const useMetricRunSQL = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const getMetricMemoized = useGetMetricMemoized();
|
||||
const getMetricDataMemoized = useGetMetricDataMemoized();
|
||||
const { mutateAsync: stageMetric } = useUpdateMetric({
|
||||
updateVersion: false,
|
||||
saveToServer: false,
|
||||
|
@ -38,13 +39,7 @@ export const useMetricRunSQL = () => {
|
|||
isPending: isRunningSQL
|
||||
} = useRunSQLQuery();
|
||||
const { openSuccessNotification } = useBusterNotifications();
|
||||
|
||||
const getDataByMetricIdMemoized = useMemoizedFn(
|
||||
(metricId: string): IBusterMetricData | undefined => {
|
||||
const options = queryKeys.metricsGetData(metricId);
|
||||
return queryClient.getQueryData(options.queryKey);
|
||||
}
|
||||
);
|
||||
const getLatestMetricVersion = useGetLatestMetricVersionNumber();
|
||||
|
||||
const originalConfigs = useRef<{
|
||||
chartConfig: IBusterMetricChartConfig;
|
||||
|
@ -64,8 +59,9 @@ export const useMetricRunSQL = () => {
|
|||
data_metadata: BusterMetricData['data_metadata'];
|
||||
isDataFromRerun: boolean;
|
||||
}) => {
|
||||
const options = queryKeys.metricsGetData(metricId);
|
||||
const currentData = getDataByMetricIdMemoized(metricId);
|
||||
const latestVersionNumber = getLatestMetricVersion(metricId);
|
||||
const options = queryKeys.metricsGetData(metricId, latestVersionNumber);
|
||||
const currentData = getMetricDataMemoized(metricId, latestVersionNumber);
|
||||
if (!currentData) return;
|
||||
const setter = isDataFromRerun ? 'dataFromRerun' : 'data';
|
||||
|
||||
|
@ -82,7 +78,7 @@ export const useMetricRunSQL = () => {
|
|||
if (metricId) {
|
||||
const { data, data_metadata } = d;
|
||||
const metricMessage = getMetricMemoized(metricId);
|
||||
const currentMessageData = getDataByMetricIdMemoized(metricId);
|
||||
const currentMessageData = getMetricDataMemoized(metricId);
|
||||
if (!originalConfigs.current) {
|
||||
originalConfigs.current = {
|
||||
chartConfig: metricMessage?.chart_config!,
|
||||
|
@ -185,7 +181,8 @@ export const useMetricRunSQL = () => {
|
|||
});
|
||||
await timeout(50);
|
||||
|
||||
const currentData = getDataByMetricIdMemoized(metricId);
|
||||
const latestVersionNumber = getLatestMetricVersion(metricId);
|
||||
const currentData = getMetricDataMemoized(metricId, latestVersionNumber);
|
||||
|
||||
if (currentData?.data_metadata && currentData?.dataFromRerun) {
|
||||
onSetDataForMetric({
|
||||
|
|
|
@ -21,6 +21,7 @@ interface AssetAccess {
|
|||
hasAccess: boolean;
|
||||
passwordRequired: boolean;
|
||||
isPublic: boolean;
|
||||
isDeleted: boolean;
|
||||
}
|
||||
|
||||
interface AssetQueryResult {
|
||||
|
@ -32,14 +33,18 @@ interface AssetQueryResult {
|
|||
|
||||
const getAssetAccess = (error: RustApiError | null): AssetAccess => {
|
||||
if (!error) {
|
||||
return { hasAccess: true, passwordRequired: false, isPublic: false };
|
||||
return { hasAccess: true, passwordRequired: false, isPublic: false, isDeleted: false };
|
||||
}
|
||||
|
||||
if (error.status === 418) {
|
||||
return { hasAccess: false, passwordRequired: true, isPublic: true };
|
||||
return { hasAccess: false, passwordRequired: true, isPublic: true, isDeleted: false };
|
||||
}
|
||||
|
||||
return { hasAccess: false, passwordRequired: false, isPublic: false };
|
||||
if (error.status === 410) {
|
||||
return { hasAccess: false, passwordRequired: false, isPublic: false, isDeleted: true };
|
||||
}
|
||||
|
||||
return { hasAccess: false, passwordRequired: false, isPublic: false, isDeleted: false };
|
||||
};
|
||||
|
||||
const useVersionNumber = (props: UseGetAssetProps) => {
|
||||
|
|
|
@ -31,6 +31,7 @@ export const ChatHeaderTitle: React.FC<{
|
|||
className="flex w-full items-center overflow-hidden">
|
||||
<EditableTitle
|
||||
className="w-full"
|
||||
placeholder="New chat"
|
||||
level={5}
|
||||
id={CHAT_HEADER_TITLE_ID}
|
||||
onChange={(value) =>
|
||||
|
|
Loading…
Reference in New Issue