mirror of https://github.com/buster-so/buster.git
Merge pull request #631 from buster-so/big-nate/bus-1521-couple-bugz-with-three-dot-menu
Fix save to collection dropdown
This commit is contained in:
commit
7b8cbc6213
|
@ -15,18 +15,25 @@ import {
|
|||
import { dashboardsGetDashboard } from './requests';
|
||||
import { metricsQueryKeys } from '@/api/query_keys/metric';
|
||||
|
||||
export const useEnsureDashboardConfig = (prefetchData = true) => {
|
||||
export const useEnsureDashboardConfig = (params?: { prefetchData?: boolean }) => {
|
||||
const { prefetchData = true } = params || {};
|
||||
const queryClient = useQueryClient();
|
||||
const prefetchDashboard = useGetDashboardAndInitializeMetrics(prefetchData);
|
||||
const prefetchDashboard = useGetDashboardAndInitializeMetrics({
|
||||
prefetchData
|
||||
});
|
||||
const { openErrorMessage } = useBusterNotifications();
|
||||
const getLatestDashboardVersion = useGetLatestDashboardVersionMemoized();
|
||||
|
||||
const method = useMemoizedFn(async (dashboardId: string) => {
|
||||
const method = useMemoizedFn(async (dashboardId: string, initializeMetrics = true) => {
|
||||
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) => {
|
||||
const res = await prefetchDashboard(
|
||||
dashboardId,
|
||||
latestVersion || undefined,
|
||||
initializeMetrics
|
||||
).catch((e) => {
|
||||
openErrorMessage('Failed to save metrics to dashboard. Dashboard not found');
|
||||
return null;
|
||||
});
|
||||
|
@ -46,7 +53,8 @@ export const useEnsureDashboardConfig = (prefetchData = true) => {
|
|||
return method;
|
||||
};
|
||||
|
||||
export const useGetDashboardAndInitializeMetrics = (prefetchData = true) => {
|
||||
export const useGetDashboardAndInitializeMetrics = (params?: { prefetchData?: boolean }) => {
|
||||
const { prefetchData = true } = params || {};
|
||||
const queryClient = useQueryClient();
|
||||
const setOriginalDashboard = useOriginalDashboardStore((x) => x.setOriginalDashboard);
|
||||
const onSetLatestDashboardVersion = useDashboardQueryStore((x) => x.onSetLatestDashboardVersion);
|
||||
|
@ -72,32 +80,40 @@ export const useGetDashboardAndInitializeMetrics = (prefetchData = true) => {
|
|||
}
|
||||
});
|
||||
|
||||
return useMemoizedFn(async (id: string, version_number: number | null | undefined) => {
|
||||
const { password } = getAssetPassword?.(id) || {};
|
||||
return useMemoizedFn(
|
||||
async (
|
||||
id: string,
|
||||
version_number: number | null | undefined,
|
||||
shouldInitializeMetrics = true
|
||||
) => {
|
||||
const { password } = getAssetPassword?.(id) || {};
|
||||
|
||||
return dashboardsGetDashboard({
|
||||
id: id || '',
|
||||
password,
|
||||
version_number: version_number || undefined
|
||||
}).then((data) => {
|
||||
initializeMetrics(data.metrics);
|
||||
const latestVersion = last(data.versions)?.version_number || 1;
|
||||
const isLatestVersion = data.dashboard.version_number === latestVersion;
|
||||
return dashboardsGetDashboard({
|
||||
id: id || '',
|
||||
password,
|
||||
version_number: version_number || undefined
|
||||
}).then((data) => {
|
||||
if (shouldInitializeMetrics) initializeMetrics(data.metrics);
|
||||
const latestVersion = last(data.versions)?.version_number || 1;
|
||||
const isLatestVersion = data.dashboard.version_number === latestVersion;
|
||||
|
||||
if (isLatestVersion) {
|
||||
setOriginalDashboard(data.dashboard);
|
||||
}
|
||||
if (isLatestVersion) {
|
||||
setOriginalDashboard(data.dashboard);
|
||||
}
|
||||
|
||||
if (data.dashboard.version_number) {
|
||||
queryClient.setQueryData(
|
||||
dashboardQueryKeys.dashboardGetDashboard(data.dashboard.id, data.dashboard.version_number)
|
||||
.queryKey,
|
||||
data
|
||||
);
|
||||
onSetLatestDashboardVersion(data.dashboard.id, latestVersion);
|
||||
}
|
||||
if (data.dashboard.version_number) {
|
||||
queryClient.setQueryData(
|
||||
dashboardQueryKeys.dashboardGetDashboard(
|
||||
data.dashboard.id,
|
||||
data.dashboard.version_number
|
||||
).queryKey,
|
||||
data
|
||||
);
|
||||
onSetLatestDashboardVersion(data.dashboard.id, latestVersion);
|
||||
}
|
||||
|
||||
return data;
|
||||
});
|
||||
});
|
||||
return data;
|
||||
});
|
||||
}
|
||||
);
|
||||
};
|
||||
|
|
|
@ -86,7 +86,7 @@ export const useGetDashboard = <TData = BusterDashboardResponse>(
|
|||
|
||||
export const usePrefetchGetDashboardClient = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const queryFn = useGetDashboardAndInitializeMetrics(false);
|
||||
const queryFn = useGetDashboardAndInitializeMetrics({ prefetchData: false });
|
||||
return useMemoizedFn((id: string, versionNumber: number) => {
|
||||
const getDashboardQueryKey = dashboardQueryKeys.dashboardGetDashboard(id, versionNumber);
|
||||
const isStale = isQueryStale(getDashboardQueryKey, queryClient);
|
||||
|
@ -447,7 +447,7 @@ export const useUpdateDashboardShare = () => {
|
|||
export const useAddAndRemoveMetricsFromDashboard = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const { openErrorMessage, openConfirmModal } = useBusterNotifications();
|
||||
const ensureDashboardConfig = useEnsureDashboardConfig(false);
|
||||
const ensureDashboardConfig = useEnsureDashboardConfig({ prefetchData: false });
|
||||
const setOriginalDashboard = useOriginalDashboardStore((x) => x.setOriginalDashboard);
|
||||
const setLatestDashboardVersion = useDashboardQueryStore((x) => x.onSetLatestDashboardVersion);
|
||||
|
||||
|
@ -560,7 +560,7 @@ export const useAddAndRemoveMetricsFromDashboard = () => {
|
|||
export const useAddMetricsToDashboard = () => {
|
||||
const queryClient = useQueryClient();
|
||||
const { openErrorMessage, openConfirmModal } = useBusterNotifications();
|
||||
const ensureDashboardConfig = useEnsureDashboardConfig(false);
|
||||
const ensureDashboardConfig = useEnsureDashboardConfig({ prefetchData: false });
|
||||
const setOriginalDashboard = useOriginalDashboardStore((x) => x.setOriginalDashboard);
|
||||
const getLatestMetricVersion = useGetLatestMetricVersionMemoized();
|
||||
const setLatestDashboardVersion = useDashboardQueryStore((x) => x.onSetLatestDashboardVersion);
|
||||
|
@ -653,7 +653,7 @@ export const useAddMetricsToDashboard = () => {
|
|||
export const useRemoveMetricsFromDashboard = () => {
|
||||
const { openConfirmModal, openErrorMessage } = useBusterNotifications();
|
||||
const queryClient = useQueryClient();
|
||||
const ensureDashboardConfig = useEnsureDashboardConfig(false);
|
||||
const ensureDashboardConfig = useEnsureDashboardConfig({ prefetchData: false });
|
||||
const setOriginalDashboard = useOriginalDashboardStore((x) => x.setOriginalDashboard);
|
||||
const getLatestMetricVersion = useGetLatestMetricVersionMemoized();
|
||||
const setLatestDashboardVersion = useDashboardQueryStore((x) => x.onSetLatestDashboardVersion);
|
||||
|
@ -683,7 +683,7 @@ export const useRemoveMetricsFromDashboard = () => {
|
|||
}
|
||||
}
|
||||
|
||||
const dashboardResponse = await ensureDashboardConfig(dashboardId);
|
||||
const dashboardResponse = await ensureDashboardConfig(dashboardId, false);
|
||||
|
||||
if (dashboardResponse) {
|
||||
const versionedOptions = dashboardQueryKeys.dashboardGetDashboard(
|
||||
|
@ -715,13 +715,7 @@ export const useRemoveMetricsFromDashboard = () => {
|
|||
).queryKey,
|
||||
data
|
||||
);
|
||||
queryClient.setQueryData(
|
||||
dashboardQueryKeys.dashboardGetDashboard(
|
||||
data.dashboard.id,
|
||||
data.dashboard.version_number
|
||||
).queryKey,
|
||||
data
|
||||
);
|
||||
|
||||
setOriginalDashboard(data.dashboard);
|
||||
|
||||
return data;
|
||||
|
@ -745,10 +739,17 @@ export const useRemoveMetricsFromDashboard = () => {
|
|||
|
||||
return useMutation({
|
||||
mutationFn: removeMetricFromDashboard,
|
||||
onSuccess: (data) => {
|
||||
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,
|
||||
refetchType: 'all'
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
|
@ -22,6 +22,7 @@ import {
|
|||
updateMetric,
|
||||
updateMetricShare
|
||||
} from './requests';
|
||||
import type { BusterCollection } from '@/api/asset_interfaces/collection';
|
||||
|
||||
/**
|
||||
* This is a mutation that saves a metric to the server.
|
||||
|
@ -157,16 +158,39 @@ export const useSaveMetricToCollections = () => {
|
|||
|
||||
return useMutation({
|
||||
mutationFn: saveMetricToCollection,
|
||||
onSuccess: (_, { collectionIds }) => {
|
||||
onMutate: ({ metricIds, collectionIds }) => {
|
||||
metricIds.forEach((id) => {
|
||||
queryClient.setQueryData(
|
||||
metricsQueryKeys.metricsGetMetric(id, null).queryKey,
|
||||
(oldData) => {
|
||||
if (!oldData) return oldData;
|
||||
const newData: BusterMetric = create(oldData, (draft) => {
|
||||
draft.collections = [
|
||||
...(draft.collections || []),
|
||||
...collectionIds.map((id) => ({ id, name: '' }))
|
||||
];
|
||||
});
|
||||
return newData;
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
onSuccess: (_, { collectionIds, metricIds }) => {
|
||||
const collectionIsInFavorites = userFavorites.some((f) => {
|
||||
return collectionIds.includes(f.id);
|
||||
});
|
||||
if (collectionIsInFavorites) refreshFavoritesList();
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: collectionIds.map(
|
||||
(id) => collectionQueryKeys.collectionsGetCollection(id).queryKey
|
||||
),
|
||||
refetchType: 'all'
|
||||
|
||||
collectionIds.forEach((id) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: collectionQueryKeys.collectionsGetCollection(id).queryKey
|
||||
});
|
||||
});
|
||||
|
||||
metricIds.forEach((id) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: metricsQueryKeys.metricsGetMetric(id, null).queryKey
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -192,17 +216,48 @@ export const useRemoveMetricFromCollection = () => {
|
|||
|
||||
return useMutation({
|
||||
mutationFn: removeMetricFromCollection,
|
||||
onMutate: ({ metricIds, collectionIds }) => {
|
||||
metricIds.forEach((id) => {
|
||||
queryClient.setQueryData(
|
||||
metricsQueryKeys.metricsGetMetric(id, null).queryKey,
|
||||
(oldData) => {
|
||||
if (!oldData) return oldData;
|
||||
const newData: BusterMetric = create(oldData, (draft) => {
|
||||
draft.collections = draft.collections?.filter((c) => !collectionIds.includes(c.id));
|
||||
});
|
||||
return newData;
|
||||
}
|
||||
);
|
||||
});
|
||||
collectionIds.forEach((id) => {
|
||||
queryClient.setQueryData(
|
||||
collectionQueryKeys.collectionsGetCollection(id).queryKey,
|
||||
(oldData) => {
|
||||
if (!oldData) return oldData;
|
||||
const newData: BusterCollection = create(oldData, (draft) => {
|
||||
draft.assets = draft.assets?.filter((a) => !metricIds.includes(a.id)) || [];
|
||||
});
|
||||
return newData;
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
onSuccess: (_, { collectionIds, metricIds }) => {
|
||||
const collectionIsInFavorites = userFavorites.some((f) => {
|
||||
return collectionIds.includes(f.id);
|
||||
});
|
||||
if (collectionIsInFavorites) refreshFavoritesList();
|
||||
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: collectionIds.map(
|
||||
(id) => collectionQueryKeys.collectionsGetCollection(id).queryKey
|
||||
),
|
||||
refetchType: 'all'
|
||||
collectionIds.forEach((id) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: collectionQueryKeys.collectionsGetCollection(id).queryKey
|
||||
});
|
||||
});
|
||||
|
||||
metricIds.forEach((id) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: metricsQueryKeys.metricsGetMetric(id, null).queryKey
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import uniq from 'lodash/uniq';
|
||||
import type React from 'react';
|
||||
import { useState } from 'react';
|
||||
import {
|
||||
useGetMetric,
|
||||
useRemoveMetricFromCollection,
|
||||
useSaveMetricToCollections
|
||||
} from '@/api/buster_rest/metrics';
|
||||
|
@ -11,38 +10,29 @@ import { SaveToCollectionsDropdown } from '../dropdowns/SaveToCollectionsDropdow
|
|||
import { CollectionButton } from './CollectionsButton';
|
||||
|
||||
export const SaveMetricToCollectionButton: React.FC<{
|
||||
metricIds: string[];
|
||||
selectedCollections: string[];
|
||||
metricId: string;
|
||||
buttonType?: 'ghost' | 'default';
|
||||
useText?: boolean;
|
||||
}> = ({
|
||||
metricIds,
|
||||
selectedCollections: selectedCollectionsProp,
|
||||
buttonType = 'ghost',
|
||||
useText = false
|
||||
}) => {
|
||||
}> = ({ metricId, buttonType = 'ghost', useText = false }) => {
|
||||
const { openInfoMessage } = useBusterNotifications();
|
||||
const { mutateAsync: saveMetricToCollection } = useSaveMetricToCollections();
|
||||
const { mutateAsync: removeMetricFromCollection } = useRemoveMetricFromCollection();
|
||||
|
||||
const [selectedCollections, setSelectedCollections] =
|
||||
useState<Parameters<typeof SaveToCollectionsDropdown>[0]['selectedCollections']>(
|
||||
selectedCollectionsProp
|
||||
);
|
||||
const { data: selectedCollections } = useGetMetric(
|
||||
{ id: metricId },
|
||||
{ select: (x) => x.collections?.map((x) => x.id) }
|
||||
);
|
||||
|
||||
const onSaveToCollection = useMemoizedFn(async (collectionIds: string[]) => {
|
||||
setSelectedCollections((prev) => uniq([...prev, ...collectionIds]));
|
||||
await saveMetricToCollection({
|
||||
metricIds,
|
||||
metricIds: [metricId],
|
||||
collectionIds
|
||||
});
|
||||
openInfoMessage('Metrics saved to collections');
|
||||
});
|
||||
|
||||
const onRemoveFromCollection = useMemoizedFn(async (collectionId: string) => {
|
||||
setSelectedCollections((prev) => prev.filter((x) => x !== collectionId));
|
||||
await removeMetricFromCollection({
|
||||
metricIds,
|
||||
metricIds: [metricId],
|
||||
collectionIds: [collectionId]
|
||||
});
|
||||
openInfoMessage('Metrics removed from collections');
|
||||
|
@ -52,7 +42,7 @@ export const SaveMetricToCollectionButton: React.FC<{
|
|||
<SaveToCollectionsDropdown
|
||||
onSaveToCollection={onSaveToCollection}
|
||||
onRemoveFromCollection={onRemoveFromCollection}
|
||||
selectedCollections={selectedCollections}>
|
||||
selectedCollections={selectedCollections || []}>
|
||||
<CollectionButton buttonType={buttonType} useText={useText} />
|
||||
</SaveToCollectionsDropdown>
|
||||
);
|
||||
|
|
|
@ -5,7 +5,6 @@ import {
|
|||
} from '@/api/buster_rest/dashboards';
|
||||
import { Button } from '@/components/ui/buttons';
|
||||
import { AppTooltip } from '@/components/ui/tooltip';
|
||||
import { useBusterNotifications } from '@/context/BusterNotifications';
|
||||
import { useMemoizedFn } from '@/hooks';
|
||||
import { ASSET_ICONS } from '../config/assetIcons';
|
||||
import { SaveToDashboardDropdown } from '../dropdowns/SaveToDashboardDropdown';
|
||||
|
@ -14,37 +13,22 @@ export const SaveMetricToDashboardButton: React.FC<{
|
|||
metricIds: string[];
|
||||
disabled?: boolean;
|
||||
selectedDashboards: string[];
|
||||
}> = React.memo(({ metricIds, disabled = false, selectedDashboards: selectedDashboardsProp }) => {
|
||||
}> = React.memo(({ metricIds, disabled = false, selectedDashboards }) => {
|
||||
const { mutateAsync: saveMetricsToDashboard } = useAddMetricsToDashboard();
|
||||
const { mutateAsync: removeMetricsFromDashboard } = useRemoveMetricsFromDashboard();
|
||||
const { openConfirmModal } = useBusterNotifications();
|
||||
|
||||
const [selectedDashboards, setSelectedDashboards] =
|
||||
useState<Parameters<typeof SaveToDashboardDropdown>[0]['selectedDashboards']>(
|
||||
selectedDashboardsProp
|
||||
);
|
||||
|
||||
const onSaveToDashboard = useMemoizedFn(async (dashboardIds: string[]) => {
|
||||
setSelectedDashboards((prev) => [...prev, ...dashboardIds]);
|
||||
await Promise.all(
|
||||
dashboardIds.map((dashboardId) => saveMetricsToDashboard({ metricIds, dashboardId }))
|
||||
);
|
||||
});
|
||||
|
||||
const onRemoveFromDashboard = useMemoizedFn(async (dashboardIds: string[]) => {
|
||||
const method = async () => {
|
||||
setSelectedDashboards((prev) => prev.filter((x) => !dashboardIds.includes(x)));
|
||||
await Promise.all(
|
||||
dashboardIds.map((dashboardId) =>
|
||||
removeMetricsFromDashboard({ useConfirmModal: false, metricIds, dashboardId })
|
||||
)
|
||||
);
|
||||
};
|
||||
return await openConfirmModal({
|
||||
title: 'Remove from dashboard',
|
||||
content: 'Are you sure you want to remove this from the dashboard?',
|
||||
onOk: method
|
||||
});
|
||||
await Promise.all(
|
||||
dashboardIds.map((dashboardId) =>
|
||||
removeMetricsFromDashboard({ useConfirmModal: false, metricIds, dashboardId })
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
return (
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import React, { useMemo } from 'react';
|
||||
import React, { useMemo, useState } from 'react';
|
||||
import type { BusterCollectionListItem } from '@/api/asset_interfaces/collection';
|
||||
import { useGetCollectionsList } from '@/api/buster_rest/collections';
|
||||
import { Button } from '@/components/ui/buttons';
|
||||
|
@ -56,6 +56,8 @@ export const useSaveToCollectionsDropdownContent = ({
|
|||
> & {
|
||||
ModalComponent: React.ReactNode;
|
||||
} => {
|
||||
const [openCollectionModal, setOpenCollectionModal] = useState(false);
|
||||
|
||||
const { data: collectionsList, isPending: isCreatingCollection } = useGetCollectionsList({});
|
||||
const onChangePage = useAppLayoutContextSelector((s) => s.onChangePage);
|
||||
|
||||
|
@ -75,8 +77,6 @@ export const useSaveToCollectionsDropdownContent = ({
|
|||
return collectionsItems;
|
||||
}, [collectionsList, selectedCollections]);
|
||||
|
||||
const [openCollectionModal, setOpenCollectionModal] = React.useState(false);
|
||||
|
||||
const menuHeader = useMemo(() => {
|
||||
return items.length > 0 ? 'Save to a collection' : undefined;
|
||||
}, [items.length]);
|
||||
|
|
|
@ -89,7 +89,12 @@ export const useSaveToDashboardDropdownContent = ({
|
|||
value: dashboard.id,
|
||||
label: dashboard.name || 'New dashboard',
|
||||
selected: selectedDashboards.some((d) => d === dashboard.id),
|
||||
onClick: () => onClickItem(dashboard),
|
||||
closeOnSelect: false,
|
||||
onClick: (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
onClickItem(dashboard);
|
||||
},
|
||||
link: createBusterRoute({
|
||||
route: BusterRoutes.APP_DASHBOARD_ID,
|
||||
dashboardId: dashboard.id
|
||||
|
|
|
@ -81,21 +81,23 @@ export const useFavoriteStar = ({
|
|||
return userFavorites?.some((favorite) => favorite.id === id);
|
||||
}, [userFavorites, id]);
|
||||
|
||||
const onFavoriteClick = useMemoizedFn(async (e?: React.MouseEvent<HTMLButtonElement>) => {
|
||||
e?.stopPropagation();
|
||||
e?.preventDefault();
|
||||
if (!isFavorited) {
|
||||
return await addItemToFavorite([
|
||||
{
|
||||
asset_type: type,
|
||||
id,
|
||||
name
|
||||
}
|
||||
]);
|
||||
}
|
||||
const onFavoriteClick = useMemoizedFn(
|
||||
async (e?: React.MouseEvent<HTMLButtonElement> | React.MouseEvent<HTMLDivElement>) => {
|
||||
e?.stopPropagation();
|
||||
e?.preventDefault();
|
||||
if (!isFavorited) {
|
||||
return await addItemToFavorite([
|
||||
{
|
||||
asset_type: type,
|
||||
id,
|
||||
name
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
return await removeItemFromFavorite([id]);
|
||||
});
|
||||
return await removeItemFromFavorite([id]);
|
||||
}
|
||||
);
|
||||
|
||||
return useMemo(
|
||||
() => ({
|
||||
|
|
|
@ -63,7 +63,8 @@ export const useFavoriteMetricSelectMenu = ({ metricId }: { metricId: string })
|
|||
label: isFavorited ? 'Remove from favorites' : 'Add to favorites',
|
||||
value: 'add-to-favorites',
|
||||
icon: isFavorited ? <StarFilled /> : <Star />,
|
||||
onClick: onFavoriteClick
|
||||
onClick: onFavoriteClick,
|
||||
closeOnSelect: false
|
||||
}),
|
||||
[isFavorited, onFavoriteClick]
|
||||
);
|
||||
|
|
|
@ -15,10 +15,6 @@ const meta: Meta<typeof Dropdown> = {
|
|||
layout: 'centered'
|
||||
},
|
||||
argTypes: {
|
||||
closeOnSelect: {
|
||||
control: 'boolean',
|
||||
defaultValue: true
|
||||
},
|
||||
align: {
|
||||
control: 'select',
|
||||
options: ['start', 'center', 'end'],
|
||||
|
|
|
@ -31,7 +31,8 @@ export interface DropdownItem<T = string> {
|
|||
secondaryLabel?: string;
|
||||
value: T;
|
||||
shortcut?: string;
|
||||
onClick?: () => void;
|
||||
onClick?: (e: React.MouseEvent<HTMLDivElement>) => void;
|
||||
closeOnSelect?: boolean; //default is true
|
||||
icon?: React.ReactNode;
|
||||
disabled?: boolean;
|
||||
loading?: boolean;
|
||||
|
@ -52,7 +53,6 @@ export interface DropdownProps<T = string> extends DropdownMenuProps {
|
|||
items: DropdownItems<T>;
|
||||
selectType?: 'single' | 'multiple' | 'none';
|
||||
menuHeader?: string | React.ReactNode; //if string it will render a search box
|
||||
closeOnSelect?: boolean;
|
||||
onSelect?: (value: T) => void;
|
||||
align?: 'start' | 'center' | 'end';
|
||||
side?: 'top' | 'right' | 'bottom' | 'left';
|
||||
|
@ -81,7 +81,6 @@ export const DropdownBase = <T,>({
|
|||
selectType = 'none',
|
||||
menuHeader,
|
||||
contentClassName = '',
|
||||
closeOnSelect = true,
|
||||
onSelect,
|
||||
children,
|
||||
align = 'start',
|
||||
|
@ -114,7 +113,6 @@ export const DropdownBase = <T,>({
|
|||
items={items}
|
||||
selectType={selectType}
|
||||
menuHeader={menuHeader}
|
||||
closeOnSelect={closeOnSelect}
|
||||
onSelect={onSelect}
|
||||
showIndex={showIndex}
|
||||
emptyStateText={emptyStateText}
|
||||
|
@ -135,7 +133,6 @@ export const DropdownContent = <T,>({
|
|||
items,
|
||||
selectType,
|
||||
menuHeader,
|
||||
closeOnSelect = true,
|
||||
showIndex = false,
|
||||
emptyStateText = 'No items found',
|
||||
footerContent,
|
||||
|
@ -198,7 +195,7 @@ export const DropdownContent = <T,>({
|
|||
if (!disabled && onSelect) {
|
||||
onSelect(item.value);
|
||||
// Close the dropdown if closeOnSelect is true
|
||||
if (closeOnSelect) {
|
||||
if (item.closeOnSelect !== false) {
|
||||
const dropdownTrigger = document.querySelector('[data-state="open"][role="menu"]');
|
||||
if (dropdownTrigger) {
|
||||
const closeEvent = new KeyboardEvent('keydown', { key: 'Escape', bubbles: true });
|
||||
|
@ -248,7 +245,7 @@ export const DropdownContent = <T,>({
|
|||
selectType={selectType}
|
||||
onSelect={onSelect}
|
||||
onSelectItem={onSelectItem}
|
||||
closeOnSelect={closeOnSelect}
|
||||
closeOnSelect={(item as DropdownItem).closeOnSelect !== false}
|
||||
showIndex={showIndex}
|
||||
/>
|
||||
);
|
||||
|
@ -270,7 +267,7 @@ export const DropdownContent = <T,>({
|
|||
selectType={selectType}
|
||||
onSelect={onSelect}
|
||||
onSelectItem={onSelectItem}
|
||||
closeOnSelect={closeOnSelect}
|
||||
closeOnSelect={(item as DropdownItem).closeOnSelect !== false}
|
||||
showIndex={showIndex}
|
||||
/>
|
||||
);
|
||||
|
@ -365,7 +362,7 @@ const DropdownItem = <T,>({
|
|||
selectType: DropdownProps<T>['selectType'];
|
||||
}) => {
|
||||
const onClickItem = useMemoizedFn((e: React.MouseEvent<HTMLDivElement>) => {
|
||||
if (onClick) onClick();
|
||||
if (onClick) onClick(e);
|
||||
if (onSelect) onSelect(value as T);
|
||||
});
|
||||
const enabledHotKeys = showIndex && !disabled && !!onSelectItem;
|
||||
|
|
|
@ -198,7 +198,7 @@ const DropdownMenuCheckboxItemMultiple = React.forwardRef<
|
|||
'absolute left-2 flex h-3.5 w-3.5 items-center justify-center opacity-0 group-hover:opacity-100',
|
||||
checked && 'opacity-100'
|
||||
)}>
|
||||
<Checkbox size="sm" checked={checked} />
|
||||
<Checkbox size="default" checked={checked} />
|
||||
</span>
|
||||
{children}
|
||||
</DropdownMenuPrimitive.CheckboxItem>
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { ShareAssetType } from '@buster/server-shared/share';
|
||||
import {
|
||||
useDeleteChat,
|
||||
useRemoveChatFromCollections,
|
||||
|
|
|
@ -145,16 +145,12 @@ const useVersionHistorySelectMenu = ({ dashboardId }: { dashboardId: string }) =
|
|||
const useCollectionSelectMenu = ({ dashboardId }: { dashboardId: string }) => {
|
||||
const { mutateAsync: saveDashboardToCollection } = useAddDashboardToCollection();
|
||||
const { mutateAsync: removeDashboardFromCollection } = useRemoveDashboardFromCollection();
|
||||
const { data: collections } = useGetDashboard(
|
||||
const { data: selectedCollections } = useGetDashboard(
|
||||
{ id: dashboardId },
|
||||
{ select: (x) => x.collections }
|
||||
{ select: (x) => x.collections?.map((collection) => collection.id) }
|
||||
);
|
||||
const { openInfoMessage } = useBusterNotifications();
|
||||
|
||||
const selectedCollections = useMemo(() => {
|
||||
return collections?.map((x) => x.id) || [];
|
||||
}, [collections]);
|
||||
|
||||
const onSaveToCollection = useMemoizedFn(async (collectionIds: string[]) => {
|
||||
await saveDashboardToCollection({ dashboardIds: [dashboardId], collectionIds });
|
||||
openInfoMessage('Dashboard saved to collections');
|
||||
|
@ -171,7 +167,7 @@ const useCollectionSelectMenu = ({ dashboardId }: { dashboardId: string }) => {
|
|||
const { ModalComponent, ...dropdownProps } = useSaveToCollectionsDropdownContent({
|
||||
onSaveToCollection,
|
||||
onRemoveFromCollection,
|
||||
selectedCollections
|
||||
selectedCollections: selectedCollections || []
|
||||
});
|
||||
|
||||
const collectionSubMenu = useMemo(() => {
|
||||
|
|
|
@ -126,14 +126,7 @@ const EditChartButton = React.memo(({ metricId }: { metricId: string }) => {
|
|||
EditChartButton.displayName = 'EditChartButton';
|
||||
|
||||
const SaveToCollectionButton = React.memo(({ metricId }: { metricId: string }) => {
|
||||
const { data: collections } = useGetMetric(
|
||||
{ id: metricId },
|
||||
{ select: (x) => x.collections?.map((x) => x.id) }
|
||||
);
|
||||
|
||||
return (
|
||||
<SaveMetricToCollectionButton metricIds={[metricId]} selectedCollections={collections || []} />
|
||||
);
|
||||
return <SaveMetricToCollectionButton metricId={metricId} />;
|
||||
});
|
||||
SaveToCollectionButton.displayName = 'SaveToCollectionButton';
|
||||
|
||||
|
|
|
@ -152,6 +152,7 @@ const useDashboardSelectMenu = ({ metricId }: { metricId: string }) => {
|
|||
const { mutateAsync: saveMetricsToDashboard } = useAddMetricsToDashboard();
|
||||
const { mutateAsync: removeMetricsFromDashboard } = useRemoveMetricsFromDashboard();
|
||||
const { data: dashboards } = useGetMetric({ id: metricId }, { select: (x) => x.dashboards });
|
||||
const { openInfoMessage } = useBusterNotifications();
|
||||
|
||||
const onSaveToDashboard = useMemoizedFn(async (dashboardIds: string[]) => {
|
||||
await Promise.all(
|
||||
|
@ -159,14 +160,16 @@ const useDashboardSelectMenu = ({ metricId }: { metricId: string }) => {
|
|||
saveMetricsToDashboard({ metricIds: [metricId], dashboardId })
|
||||
)
|
||||
);
|
||||
openInfoMessage('Metric added to dashboard');
|
||||
});
|
||||
|
||||
const onRemoveFromDashboard = useMemoizedFn(async (dashboardIds: string[]) => {
|
||||
await Promise.all(
|
||||
dashboardIds.map((dashboardId) =>
|
||||
removeMetricsFromDashboard({ metricIds: [metricId], dashboardId })
|
||||
removeMetricsFromDashboard({ metricIds: [metricId], dashboardId, useConfirmModal: false })
|
||||
)
|
||||
);
|
||||
openInfoMessage('Metric removed from dashboard');
|
||||
});
|
||||
|
||||
const { items, footerContent, selectType, menuHeader } = useSaveToDashboardDropdownContent({
|
||||
|
@ -190,6 +193,7 @@ const useDashboardSelectMenu = ({ metricId }: { metricId: string }) => {
|
|||
() => ({
|
||||
label: 'Add to dashboard',
|
||||
value: 'add-to-dashboard',
|
||||
closeOnSelect: false,
|
||||
icon: <ASSET_ICONS.dashboardAdd />,
|
||||
items: [<React.Fragment key="dashboard-sub-menu">{dashboardSubMenu}</React.Fragment>]
|
||||
}),
|
||||
|
@ -202,13 +206,12 @@ const useDashboardSelectMenu = ({ metricId }: { metricId: string }) => {
|
|||
const useCollectionSelectMenu = ({ metricId }: { metricId: string }) => {
|
||||
const { mutateAsync: saveMetricToCollection } = useSaveMetricToCollections();
|
||||
const { mutateAsync: removeMetricFromCollection } = useRemoveMetricFromCollection();
|
||||
const { data: collections } = useGetMetric({ id: metricId }, { select: (x) => x.collections });
|
||||
const { data: selectedCollections } = useGetMetric(
|
||||
{ id: metricId },
|
||||
{ select: (x) => x.collections?.map((x) => x.id) }
|
||||
);
|
||||
const { openInfoMessage } = useBusterNotifications();
|
||||
|
||||
const selectedCollections = useMemo(() => {
|
||||
return collections?.map((x) => x.id) || [];
|
||||
}, [collections]);
|
||||
|
||||
const onSaveToCollection = useMemoizedFn(async (collectionIds: string[]) => {
|
||||
await saveMetricToCollection({
|
||||
metricIds: [metricId],
|
||||
|
@ -228,7 +231,7 @@ const useCollectionSelectMenu = ({ metricId }: { metricId: string }) => {
|
|||
const { ModalComponent, ...dropdownProps } = useSaveToCollectionsDropdownContent({
|
||||
onSaveToCollection,
|
||||
onRemoveFromCollection,
|
||||
selectedCollections
|
||||
selectedCollections: selectedCollections || []
|
||||
});
|
||||
|
||||
const CollectionSubMenu = useMemo(() => {
|
||||
|
|
Loading…
Reference in New Issue