diff --git a/apps/web/src/api/buster_rest/dashboards/dashboardQueryHelpers.ts b/apps/web/src/api/buster_rest/dashboards/dashboardQueryHelpers.ts index 216fc5837..317de0ce2 100644 --- a/apps/web/src/api/buster_rest/dashboards/dashboardQueryHelpers.ts +++ b/apps/web/src/api/buster_rest/dashboards/dashboardQueryHelpers.ts @@ -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; + }); + } + ); }; diff --git a/apps/web/src/api/buster_rest/dashboards/queryRequests.ts b/apps/web/src/api/buster_rest/dashboards/queryRequests.ts index d46727a79..0be2be6b5 100644 --- a/apps/web/src/api/buster_rest/dashboards/queryRequests.ts +++ b/apps/web/src/api/buster_rest/dashboards/queryRequests.ts @@ -86,7 +86,7 @@ export const useGetDashboard = ( 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' + }); + }); } }); }; diff --git a/apps/web/src/api/buster_rest/metrics/updateMetricQueryRequests.ts b/apps/web/src/api/buster_rest/metrics/updateMetricQueryRequests.ts index be877a536..312356413 100644 --- a/apps/web/src/api/buster_rest/metrics/updateMetricQueryRequests.ts +++ b/apps/web/src/api/buster_rest/metrics/updateMetricQueryRequests.ts @@ -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 + }); }); } }); diff --git a/apps/web/src/components/features/buttons/SaveMetricToCollectionButton.tsx b/apps/web/src/components/features/buttons/SaveMetricToCollectionButton.tsx index c7dc35f01..072507554 100644 --- a/apps/web/src/components/features/buttons/SaveMetricToCollectionButton.tsx +++ b/apps/web/src/components/features/buttons/SaveMetricToCollectionButton.tsx @@ -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[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<{ + selectedCollections={selectedCollections || []}> ); diff --git a/apps/web/src/components/features/buttons/SaveMetricToDashboardButton.tsx b/apps/web/src/components/features/buttons/SaveMetricToDashboardButton.tsx index a3e5ebeee..f694c10ec 100644 --- a/apps/web/src/components/features/buttons/SaveMetricToDashboardButton.tsx +++ b/apps/web/src/components/features/buttons/SaveMetricToDashboardButton.tsx @@ -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[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 ( diff --git a/apps/web/src/components/features/dropdowns/SaveToCollectionsDropdown.tsx b/apps/web/src/components/features/dropdowns/SaveToCollectionsDropdown.tsx index 6afec38f4..b90bbad6a 100644 --- a/apps/web/src/components/features/dropdowns/SaveToCollectionsDropdown.tsx +++ b/apps/web/src/components/features/dropdowns/SaveToCollectionsDropdown.tsx @@ -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]); diff --git a/apps/web/src/components/features/dropdowns/SaveToDashboardDropdown.tsx b/apps/web/src/components/features/dropdowns/SaveToDashboardDropdown.tsx index ee7b83b0e..702acf5c4 100644 --- a/apps/web/src/components/features/dropdowns/SaveToDashboardDropdown.tsx +++ b/apps/web/src/components/features/dropdowns/SaveToDashboardDropdown.tsx @@ -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 diff --git a/apps/web/src/components/features/list/FavoriteStar.tsx b/apps/web/src/components/features/list/FavoriteStar.tsx index 15d6e14f3..e01600996 100644 --- a/apps/web/src/components/features/list/FavoriteStar.tsx +++ b/apps/web/src/components/features/list/FavoriteStar.tsx @@ -81,21 +81,23 @@ export const useFavoriteStar = ({ return userFavorites?.some((favorite) => favorite.id === id); }, [userFavorites, id]); - const onFavoriteClick = useMemoizedFn(async (e?: React.MouseEvent) => { - e?.stopPropagation(); - e?.preventDefault(); - if (!isFavorited) { - return await addItemToFavorite([ - { - asset_type: type, - id, - name - } - ]); - } + const onFavoriteClick = useMemoizedFn( + async (e?: React.MouseEvent | React.MouseEvent) => { + e?.stopPropagation(); + e?.preventDefault(); + if (!isFavorited) { + return await addItemToFavorite([ + { + asset_type: type, + id, + name + } + ]); + } - return await removeItemFromFavorite([id]); - }); + return await removeItemFromFavorite([id]); + } + ); return useMemo( () => ({ diff --git a/apps/web/src/components/features/metrics/ThreeDotMenu.tsx b/apps/web/src/components/features/metrics/ThreeDotMenu.tsx index 0f4db94c2..2de112bd5 100644 --- a/apps/web/src/components/features/metrics/ThreeDotMenu.tsx +++ b/apps/web/src/components/features/metrics/ThreeDotMenu.tsx @@ -63,7 +63,8 @@ export const useFavoriteMetricSelectMenu = ({ metricId }: { metricId: string }) label: isFavorited ? 'Remove from favorites' : 'Add to favorites', value: 'add-to-favorites', icon: isFavorited ? : , - onClick: onFavoriteClick + onClick: onFavoriteClick, + closeOnSelect: false }), [isFavorited, onFavoriteClick] ); diff --git a/apps/web/src/components/ui/dropdown/Dropdown.stories.tsx b/apps/web/src/components/ui/dropdown/Dropdown.stories.tsx index 68d34f5ab..a259468b3 100644 --- a/apps/web/src/components/ui/dropdown/Dropdown.stories.tsx +++ b/apps/web/src/components/ui/dropdown/Dropdown.stories.tsx @@ -15,10 +15,6 @@ const meta: Meta = { layout: 'centered' }, argTypes: { - closeOnSelect: { - control: 'boolean', - defaultValue: true - }, align: { control: 'select', options: ['start', 'center', 'end'], diff --git a/apps/web/src/components/ui/dropdown/Dropdown.tsx b/apps/web/src/components/ui/dropdown/Dropdown.tsx index 47d73380d..5fe45151c 100644 --- a/apps/web/src/components/ui/dropdown/Dropdown.tsx +++ b/apps/web/src/components/ui/dropdown/Dropdown.tsx @@ -31,7 +31,8 @@ export interface DropdownItem { secondaryLabel?: string; value: T; shortcut?: string; - onClick?: () => void; + onClick?: (e: React.MouseEvent) => void; + closeOnSelect?: boolean; //default is true icon?: React.ReactNode; disabled?: boolean; loading?: boolean; @@ -52,7 +53,6 @@ export interface DropdownProps extends DropdownMenuProps { items: DropdownItems; 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 = ({ selectType = 'none', menuHeader, contentClassName = '', - closeOnSelect = true, onSelect, children, align = 'start', @@ -114,7 +113,6 @@ export const DropdownBase = ({ items={items} selectType={selectType} menuHeader={menuHeader} - closeOnSelect={closeOnSelect} onSelect={onSelect} showIndex={showIndex} emptyStateText={emptyStateText} @@ -135,7 +133,6 @@ export const DropdownContent = ({ items, selectType, menuHeader, - closeOnSelect = true, showIndex = false, emptyStateText = 'No items found', footerContent, @@ -198,7 +195,7 @@ export const DropdownContent = ({ 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 = ({ selectType={selectType} onSelect={onSelect} onSelectItem={onSelectItem} - closeOnSelect={closeOnSelect} + closeOnSelect={(item as DropdownItem).closeOnSelect !== false} showIndex={showIndex} /> ); @@ -270,7 +267,7 @@ export const DropdownContent = ({ selectType={selectType} onSelect={onSelect} onSelectItem={onSelectItem} - closeOnSelect={closeOnSelect} + closeOnSelect={(item as DropdownItem).closeOnSelect !== false} showIndex={showIndex} /> ); @@ -365,7 +362,7 @@ const DropdownItem = ({ selectType: DropdownProps['selectType']; }) => { const onClickItem = useMemoizedFn((e: React.MouseEvent) => { - if (onClick) onClick(); + if (onClick) onClick(e); if (onSelect) onSelect(value as T); }); const enabledHotKeys = showIndex && !disabled && !!onSelectItem; diff --git a/apps/web/src/components/ui/dropdown/DropdownBase.tsx b/apps/web/src/components/ui/dropdown/DropdownBase.tsx index f1b7b3e7d..57b40450e 100644 --- a/apps/web/src/components/ui/dropdown/DropdownBase.tsx +++ b/apps/web/src/components/ui/dropdown/DropdownBase.tsx @@ -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' )}> - + {children} diff --git a/apps/web/src/controllers/ChatsListController/ChatItemsSelectedPopup.tsx b/apps/web/src/controllers/ChatsListController/ChatItemsSelectedPopup.tsx index b3ef206b2..c7d7aa7b9 100644 --- a/apps/web/src/controllers/ChatsListController/ChatItemsSelectedPopup.tsx +++ b/apps/web/src/controllers/ChatsListController/ChatItemsSelectedPopup.tsx @@ -1,7 +1,6 @@ 'use client'; import React, { useState } from 'react'; -import { ShareAssetType } from '@buster/server-shared/share'; import { useDeleteChat, useRemoveChatFromCollections, diff --git a/apps/web/src/layouts/ChatLayout/FileContainer/FileContainerHeader/DashboardContainerHeaderButtons/DashboardThreeDotMenu.tsx b/apps/web/src/layouts/ChatLayout/FileContainer/FileContainerHeader/DashboardContainerHeaderButtons/DashboardThreeDotMenu.tsx index 2cf390850..0fefce21d 100644 --- a/apps/web/src/layouts/ChatLayout/FileContainer/FileContainerHeader/DashboardContainerHeaderButtons/DashboardThreeDotMenu.tsx +++ b/apps/web/src/layouts/ChatLayout/FileContainer/FileContainerHeader/DashboardContainerHeaderButtons/DashboardThreeDotMenu.tsx @@ -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(() => { diff --git a/apps/web/src/layouts/ChatLayout/FileContainer/FileContainerHeader/MetricContainerHeaderButtons/MetricContainerHeaderButtons.tsx b/apps/web/src/layouts/ChatLayout/FileContainer/FileContainerHeader/MetricContainerHeaderButtons/MetricContainerHeaderButtons.tsx index 4bd342067..17d9247ec 100644 --- a/apps/web/src/layouts/ChatLayout/FileContainer/FileContainerHeader/MetricContainerHeaderButtons/MetricContainerHeaderButtons.tsx +++ b/apps/web/src/layouts/ChatLayout/FileContainer/FileContainerHeader/MetricContainerHeaderButtons/MetricContainerHeaderButtons.tsx @@ -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 ( - - ); + return ; }); SaveToCollectionButton.displayName = 'SaveToCollectionButton'; diff --git a/apps/web/src/layouts/ChatLayout/FileContainer/FileContainerHeader/MetricContainerHeaderButtons/MetricThreeDotMenu.tsx b/apps/web/src/layouts/ChatLayout/FileContainer/FileContainerHeader/MetricContainerHeaderButtons/MetricThreeDotMenu.tsx index 894b57fe5..18c8524ac 100644 --- a/apps/web/src/layouts/ChatLayout/FileContainer/FileContainerHeader/MetricContainerHeaderButtons/MetricThreeDotMenu.tsx +++ b/apps/web/src/layouts/ChatLayout/FileContainer/FileContainerHeader/MetricContainerHeaderButtons/MetricThreeDotMenu.tsx @@ -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: , items: [{dashboardSubMenu}] }), @@ -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(() => {