favorites update

This commit is contained in:
Nate Kelley 2025-04-09 09:54:40 -06:00
parent 6ebb364101
commit 152bd1242f
No known key found for this signature in database
GPG Key ID: FD90372AB8D98B4F
12 changed files with 155 additions and 172 deletions

View File

@ -101,7 +101,9 @@ export const useAddUserFavorite = () => {
mutationFn: createUserFavorite,
onMutate: (params) => {
queryClient.setQueryData(queryKeys.favoritesGetList.queryKey, (prev) => {
return [params, ...(prev || [])];
const prevIds = prev?.map((p) => p.id) || [];
const dedupedAdd = params.filter((p) => !prevIds.includes(p.id));
return [...dedupedAdd, ...(prev || [])];
});
},
onSuccess: (data) => {
@ -116,7 +118,7 @@ export const useDeleteUserFavorite = () => {
mutationFn: deleteUserFavorite,
onMutate: (id) => {
queryClient.setQueryData(queryKeys.favoritesGetList.queryKey, (prev) => {
return prev?.filter((fav) => fav.id !== id);
return prev?.filter((fav) => !id.includes(fav.id));
});
},
onSuccess: (data) => {

View File

@ -93,51 +93,31 @@ export const getUserFavorites_server = async () => {
return serverFetch<BusterUserFavorite[]>(`/users/favorites`);
};
export const createUserFavorite = async (payload: {
id: string;
asset_type: ShareAssetType;
index?: number;
name: string; //just used for the UI for optimistic update
}) => {
export const createUserFavorite = async (
payload: {
id: string;
asset_type: ShareAssetType;
index?: number;
name: string; //just used for the UI for optimistic update
}[]
) => {
return mainApi
.post<BusterUserFavorite[]>(`/users/favorites`, payload)
.then((response) => response.data);
};
export const createUserFavorite_server = async (
payload: Parameters<typeof createUserFavorite>[0]
) => {
return serverFetch<BusterUserFavorite[]>(`/users/favorites`, {
method: 'POST',
body: JSON.stringify(payload)
});
};
export const deleteUserFavorite = async (id: string) => {
export const deleteUserFavorite = async (data: string[]) => {
return mainApi
.delete<BusterUserFavorite[]>(`/users/favorites/${id}`)
.delete<BusterUserFavorite[]>(`/users/favorites`, { data })
.then((response) => response.data);
};
export const deleteUserFavorite_server = async (id: string) => {
return serverFetch<BusterUserFavorite[]>(`/users/favorites/${id}`, {
method: 'DELETE'
});
};
export const updateUserFavorites = async (payload: string[]) => {
return mainApi
.put<BusterUserFavorite[]>(`/users/favorites`, payload)
.then((response) => response.data);
};
export const updateUserFavorites_server = async (payload: string[]) => {
return serverFetch<BusterUserFavorite[]>(`/users/favorites`, {
method: 'PUT',
body: JSON.stringify(payload)
});
};
//USER LIST
export const getUserList = async (payload: {

View File

@ -19,8 +19,10 @@ export const assetTypeToIcon = (assetType: ShareAssetType) => {
return ASSET_ICONS.dashboards;
case ShareAssetType.COLLECTION:
return ASSET_ICONS.collections;
case ShareAssetType.CHAT:
return ASSET_ICONS.chats;
default:
const _result: unknown = assetType;
const _result: never = assetType;
return ASSET_ICONS.metrics;
}
};

View File

@ -0,0 +1,55 @@
import { ShareAssetType } from '@/api/asset_interfaces/share';
import {
useAddUserFavorite,
useDeleteUserFavorite,
useGetUserFavorites
} from '@/api/buster_rest/users';
import { DropdownItems } from '@/components/ui/dropdown';
import { Star, Xmark } from '@/components/ui/icons';
import { useMemo } from 'react';
export const useThreeDotFavoritesOptions = ({
itemIds,
assetType,
onFinish
}: {
itemIds: string[];
assetType: ShareAssetType;
onFinish: (ids: string[]) => void;
}) => {
const { mutateAsync: addUserFavorite } = useAddUserFavorite();
const { mutateAsync: removeUserFavorite } = useDeleteUserFavorite();
const { data: userFavorites } = useGetUserFavorites();
const dropdownOptions: DropdownItems = useMemo(
() => [
{
label: 'Add to favorites',
icon: <Star />,
value: 'add-to-favorites',
onClick: async () => {
await addUserFavorite([
...itemIds.map((id) => ({
id,
asset_type: assetType,
name: userFavorites?.find((f) => f.id === id)?.name || ''
}))
]);
onFinish(itemIds);
}
},
{
label: 'Remove from favorites',
icon: <Xmark />,
value: 'remove-from-favorites',
onClick: async () => {
await removeUserFavorite(itemIds);
onFinish(itemIds);
}
}
],
[addUserFavorite, removeUserFavorite, itemIds, assetType, userFavorites]
);
return dropdownOptions;
};

View File

@ -84,13 +84,17 @@ export const useFavoriteStar = ({
const onFavoriteClick = useMemoizedFn(async (e?: React.MouseEvent<HTMLButtonElement>) => {
e?.stopPropagation();
e?.preventDefault();
if (!isFavorited)
return await addItemToFavorite({
asset_type: type,
id,
name
});
await removeItemFromFavorite(id);
if (!isFavorited) {
return await addItemToFavorite([
{
asset_type: type,
id,
name
}
]);
}
return await removeItemFromFavorite([id]);
});
return useMemo(

View File

@ -241,7 +241,7 @@ const favoritesDropdown = (
deleteUserFavorite
}: {
onFavoritesReorder: (itemIds: string[]) => void;
deleteUserFavorite: (itemId: string) => void;
deleteUserFavorite: (itemIds: string[]) => void;
}
): ISidebarGroup => {
return {
@ -257,7 +257,7 @@ const favoritesDropdown = (
icon: <Icon />,
route,
id: favorite.id,
onRemove: () => deleteUserFavorite(favorite.id)
onRemove: () => deleteUserFavorite([favorite.id])
};
})
};

View File

@ -1,9 +1,9 @@
'use client';
import React, { useState } from 'react';
import { Dots, Star, Trash, Xmark } from '@/components/ui/icons';
import { Dots, Trash } from '@/components/ui/icons';
import { BusterListSelectedOptionPopupContainer } from '@/components/ui/list';
import { Dropdown, DropdownItems } from '@/components/ui/dropdown';
import { Dropdown } from '@/components/ui/dropdown';
import { Button } from '@/components/ui/buttons';
import { useMemoizedFn } from '@/hooks';
import { SaveToCollectionsDropdown } from '@/components/features/dropdowns/SaveToCollectionsDropdown';
@ -14,12 +14,8 @@ import {
useSaveChatToCollections,
useRemoveChatFromCollections
} from '@/api/buster_rest/chats';
import {
useAddUserFavorite,
useDeleteUserFavorite,
useGetUserFavorites
} from '@/api/buster_rest/users';
import { ShareAssetType } from '@/api/asset_interfaces/share';
import { useThreeDotFavoritesOptions } from '@/components/features/dropdowns/useThreeDotFavoritesOptions';
export const ChatSelectedOptionPopup: React.FC<{
selectedRowKeys: string[];
@ -117,40 +113,11 @@ const ThreeDotButton: React.FC<{
selectedRowKeys: string[];
onSelectChange: (selectedRowKeys: string[]) => void;
}> = React.memo(({ selectedRowKeys, onSelectChange }) => {
const { mutateAsync: removeUserFavorite, isPending: removingFromFavorites } =
useDeleteUserFavorite();
const { mutateAsync: addUserFavorite, isPending: addingToFavorites } = useAddUserFavorite();
const { data: userFavorites } = useGetUserFavorites();
const dropdownOptions: DropdownItems = [
{
label: 'Add to favorites',
icon: <Star />,
value: 'add-to-favorites',
loading: addingToFavorites,
onClick: async () => {
await Promise.all(
selectedRowKeys.map((id) => {
const name = userFavorites?.find((f) => f.id === id)?.name || '';
return addUserFavorite({
id,
asset_type: ShareAssetType.CHAT,
name
});
})
);
}
},
{
label: 'Remove from favorites',
icon: <Xmark />,
loading: removingFromFavorites,
value: 'remove-from-favorites',
onClick: async () => {
await Promise.all(selectedRowKeys.map((id) => removeUserFavorite(id)));
}
}
];
const dropdownOptions = useThreeDotFavoritesOptions({
itemIds: selectedRowKeys,
assetType: ShareAssetType.CHAT,
onFinish: () => onSelectChange([])
});
return (
<Dropdown items={dropdownOptions}>

View File

@ -9,7 +9,7 @@ export const CollectionIndividualSelectedPopup: React.FC<{
selectedRowKeys: string[];
collectionId: string;
onSelectChange: (selectedRowKeys: string[]) => void;
}> = ({ selectedRowKeys, onSelectChange, collectionId }) => {
}> = React.memo(({ selectedRowKeys, onSelectChange, collectionId }) => {
const show = selectedRowKeys.length > 0;
return (
@ -27,7 +27,9 @@ export const CollectionIndividualSelectedPopup: React.FC<{
show={show}
/>
);
};
});
CollectionIndividualSelectedPopup.displayName = 'CollectionIndividualSelectedPopup';
const CollectionDeleteButton: React.FC<{
selectedRowKeys: string[];

View File

@ -1,15 +1,17 @@
import { Trash } from '@/components/ui/icons';
import { Dots, Trash } from '@/components/ui/icons';
import { BusterListSelectedOptionPopupContainer } from '@/components/ui/list';
import { useMemoizedFn } from '@/hooks';
import { Button } from '@/components/ui/buttons';
import React from 'react';
import { useDeleteCollection } from '@/api/buster_rest/collections';
import { ShareAssetType } from '@/api/asset_interfaces/share';
import { Dropdown } from '@/components/ui/dropdown';
import { useThreeDotFavoritesOptions } from '@/components/features/dropdowns/useThreeDotFavoritesOptions';
export const CollectionListSelectedPopup: React.FC<{
selectedRowKeys: string[];
onSelectChange: (selectedRowKeys: string[]) => void;
}> = ({ selectedRowKeys, onSelectChange }) => {
}> = React.memo(({ selectedRowKeys, onSelectChange }) => {
const show = selectedRowKeys.length > 0;
return (
@ -21,12 +23,19 @@ export const CollectionListSelectedPopup: React.FC<{
key="delete"
selectedRowKeys={selectedRowKeys}
onSelectChange={onSelectChange}
/>,
<ThreeDotButton
key="three-dot"
selectedRowKeys={selectedRowKeys}
onSelectChange={onSelectChange}
/>
]}
show={show}
/>
);
};
});
CollectionListSelectedPopup.displayName = 'CollectionListSelectedPopup';
const CollectionDeleteButton: React.FC<{
selectedRowKeys: string[];
@ -49,3 +58,20 @@ const CollectionDeleteButton: React.FC<{
</Button>
);
};
const ThreeDotButton: React.FC<{
selectedRowKeys: string[];
onSelectChange: (selectedRowKeys: string[]) => void;
}> = ({ selectedRowKeys, onSelectChange }) => {
const dropdownOptions = useThreeDotFavoritesOptions({
itemIds: selectedRowKeys,
assetType: ShareAssetType.COLLECTION,
onFinish: () => onSelectChange([])
});
return (
<Dropdown items={dropdownOptions}>
<Button prefix={<Dots />} />
</Dropdown>
);
};

View File

@ -1,29 +1,25 @@
import React, { useState } from 'react';
import { BusterListSelectedOptionPopupContainer } from '@/components/ui/list';
import { Dropdown, DropdownItems } from '@/components/ui/dropdown';
import { Dropdown } from '@/components/ui/dropdown';
import { Button } from '@/components/ui/buttons';
import { useMemoizedFn } from '@/hooks';
import { useBusterNotifications } from '@/context/BusterNotifications';
import { SaveToCollectionsDropdown } from '@/components/features/dropdowns/SaveToCollectionsDropdown';
import { ASSET_ICONS } from '@/components/features/config/assetIcons';
import { Dots, Star, Trash, Xmark } from '@/components/ui/icons';
import {
useAddUserFavorite,
useDeleteUserFavorite,
useGetUserFavorites
} from '@/api/buster_rest/users';
import { Dots, Trash } from '@/components/ui/icons';
import { ShareAssetType } from '@/api/asset_interfaces/share';
import {
useAddDashboardToCollection,
useDeleteDashboards,
useRemoveDashboardFromCollection
} from '@/api/buster_rest/dashboards';
import { useThreeDotFavoritesOptions } from '@/components/features/dropdowns/useThreeDotFavoritesOptions';
export const DashboardSelectedOptionPopup: React.FC<{
selectedRowKeys: string[];
onSelectChange: (selectedRowKeys: string[]) => void;
hasSelected: boolean;
}> = ({ selectedRowKeys, onSelectChange, hasSelected }) => {
}> = React.memo(({ selectedRowKeys, onSelectChange, hasSelected }) => {
return (
<BusterListSelectedOptionPopupContainer
selectedRowKeys={selectedRowKeys}
@ -48,7 +44,9 @@ export const DashboardSelectedOptionPopup: React.FC<{
show={hasSelected}
/>
);
};
});
DashboardSelectedOptionPopup.displayName = 'DashboardSelectedOptionPopup';
const CollectionsButton: React.FC<{
selectedRowKeys: string[];
@ -115,37 +113,11 @@ const ThreeDotButton: React.FC<{
selectedRowKeys: string[];
onSelectChange: (selectedRowKeys: string[]) => void;
}> = ({ selectedRowKeys, onSelectChange }) => {
const { mutateAsync: addUserFavorite } = useAddUserFavorite();
const { mutateAsync: removeUserFavorite } = useDeleteUserFavorite();
const { data: userFavorites } = useGetUserFavorites();
const dropdownOptions: DropdownItems = [
{
label: 'Add to favorites',
icon: <Star />,
value: 'add-to-favorites',
onClick: async () => {
await Promise.all(
selectedRowKeys.map((id) => {
const name = userFavorites?.find((f) => f.id === id)?.name || '';
return addUserFavorite({
id,
asset_type: ShareAssetType.DASHBOARD,
name
});
})
);
}
},
{
label: 'Remove from favorites',
icon: <Xmark />,
value: 'remove-from-favorites',
onClick: async () => {
await Promise.all(selectedRowKeys.map((id) => removeUserFavorite(id)));
}
}
];
const dropdownOptions = useThreeDotFavoritesOptions({
itemIds: selectedRowKeys,
assetType: ShareAssetType.DASHBOARD,
onFinish: () => onSelectChange([])
});
return (
<Dropdown items={dropdownOptions}>

View File

@ -7,26 +7,22 @@ import { SaveToCollectionsDropdown } from '@/components/features/dropdowns/SaveT
import { useBusterNotifications } from '@/context/BusterNotifications';
import { Button } from '@/components/ui/buttons';
import { ASSET_ICONS } from '@/components/features/config/assetIcons';
import { Dropdown, DropdownItems } from '@/components/ui/dropdown';
import { Dropdown } from '@/components/ui/dropdown';
import { StatusBadgeButton } from '@/components/features/metrics/StatusBadgeIndicator';
import { Dots, Star, Trash, Xmark } from '@/components/ui/icons';
import { Dots, Trash } from '@/components/ui/icons';
import {
useDeleteMetric,
useRemoveMetricFromCollection,
useSaveMetricToCollections,
useUpdateMetric
} from '@/api/buster_rest/metrics';
import {
useAddUserFavorite,
useDeleteUserFavorite,
useGetUserFavorites
} from '@/api/buster_rest/users';
import { useThreeDotFavoritesOptions } from '@/components/features/dropdowns/useThreeDotFavoritesOptions';
export const MetricSelectedOptionPopup: React.FC<{
selectedRowKeys: string[];
onSelectChange: (selectedRowKeys: string[]) => void;
hasSelected: boolean;
}> = ({ selectedRowKeys, onSelectChange, hasSelected }) => {
}> = React.memo(({ selectedRowKeys, onSelectChange, hasSelected }) => {
return (
<BusterListSelectedOptionPopupContainer
selectedRowKeys={selectedRowKeys}
@ -61,7 +57,9 @@ export const MetricSelectedOptionPopup: React.FC<{
show={hasSelected}
/>
);
};
});
MetricSelectedOptionPopup.displayName = 'MetricSelectedOptionPopup';
const CollectionsButton: React.FC<{
selectedRowKeys: string[];
@ -177,42 +175,18 @@ const DeleteButton: React.FC<{
const ThreeDotButton: React.FC<{
selectedRowKeys: string[];
onSelectChange: (selectedRowKeys: string[]) => void;
}> = ({ selectedRowKeys, onSelectChange }) => {
const { mutateAsync: addUserFavorite } = useAddUserFavorite();
const { mutateAsync: removeUserFavorite } = useDeleteUserFavorite();
const { data: userFavorites } = useGetUserFavorites();
const dropdownOptions: DropdownItems = [
{
label: 'Add to favorites',
icon: <Star />,
value: 'add-to-favorites',
onClick: async () => {
await Promise.all(
selectedRowKeys.map((id) => {
const name = userFavorites?.find((f) => f.id === id)?.name || '';
return addUserFavorite({
id,
asset_type: ShareAssetType.METRIC,
name
});
})
);
}
},
{
label: 'Remove from favorites',
icon: <Xmark />,
value: 'remove-from-favorites',
onClick: async () => {
await Promise.all(selectedRowKeys.map((id) => removeUserFavorite(id)));
}
}
];
}> = React.memo(({ selectedRowKeys, onSelectChange }) => {
const dropdownOptions = useThreeDotFavoritesOptions({
itemIds: selectedRowKeys,
assetType: ShareAssetType.METRIC,
onFinish: () => onSelectChange([])
});
return (
<Dropdown items={dropdownOptions}>
<Button prefix={<Dots />} />
</Dropdown>
);
};
});
ThreeDotButton.displayName = 'ThreeDotButton';

View File

@ -36,7 +36,6 @@ import { getShareAssetConfig } from '@/components/features/ShareMenu/helpers';
import { useDashboardContentStore } from '@/context/Dashboards';
import { useAppLayoutContextSelector } from '@/context/BusterAppLayout';
import { BusterRoutes, createBusterRoute } from '@/routes/busterRoutes';
import { assetParamsToRoute } from '@/layouts/ChatLayout/ChatLayoutContext/helpers';
export const DashboardThreeDotMenu = React.memo(({ dashboardId }: { dashboardId: string }) => {
const versionHistoryItems = useVersionHistorySelectMenu({ dashboardId });