mirror of https://github.com/buster-so/buster.git
favorites updates
This commit is contained in:
parent
8579aafd0a
commit
4fa7e92a38
|
@ -7,7 +7,8 @@ export enum ShareRole {
|
|||
export enum ShareAssetType {
|
||||
METRIC = 'metric',
|
||||
DASHBOARD = 'dashboard',
|
||||
COLLECTION = 'collection'
|
||||
COLLECTION = 'collection',
|
||||
CHAT = 'chat'
|
||||
}
|
||||
|
||||
export interface BusterShare {
|
||||
|
|
|
@ -38,14 +38,14 @@ export interface BusterUserFavorite {
|
|||
collection_id?: string;
|
||||
assets?: {
|
||||
id: string;
|
||||
type: ShareAssetType;
|
||||
asset_type: ShareAssetType;
|
||||
name: string;
|
||||
}[];
|
||||
}
|
||||
|
||||
export type BusterUserFavoriteAsset = {
|
||||
id: string;
|
||||
asset_type: ShareAssetType;
|
||||
ype: ShareAssetType;
|
||||
index?: number;
|
||||
title: string;
|
||||
};
|
||||
|
|
|
@ -10,9 +10,9 @@ import {
|
|||
createUserFavorite,
|
||||
deleteUserFavorite,
|
||||
updateUserFavorites,
|
||||
inviteUser,
|
||||
getUserList,
|
||||
getUserList_server,
|
||||
inviteUser
|
||||
getUserList_server
|
||||
} from './requests';
|
||||
import { useMemoizedFn } from '@/hooks';
|
||||
import { QueryClient, useQueryClient } from '@tanstack/react-query';
|
||||
|
@ -103,11 +103,11 @@ export const useAddUserFavorite = () => {
|
|||
mutationFn: createUserFavorite,
|
||||
onMutate: (params) => {
|
||||
queryClient.setQueryData(queryKeys.favoritesGetList.queryKey, (prev) => {
|
||||
return [...params, ...(prev || [])];
|
||||
return [params, ...(prev || [])];
|
||||
});
|
||||
},
|
||||
onSettled: () => {
|
||||
queryClient.invalidateQueries(queryKeys.favoritesGetList);
|
||||
onSuccess: (data) => {
|
||||
queryClient.setQueryData(queryKeys.favoritesGetList.queryKey, data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -116,13 +116,13 @@ export const useDeleteUserFavorite = () => {
|
|||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: deleteUserFavorite,
|
||||
onMutate: (params) => {
|
||||
onMutate: (id) => {
|
||||
queryClient.setQueryData(queryKeys.favoritesGetList.queryKey, (prev) => {
|
||||
return prev?.filter((fav) => !params.some((p) => p.id === fav.id));
|
||||
return prev?.filter((fav) => fav.id !== id);
|
||||
});
|
||||
},
|
||||
onSettled: () => {
|
||||
queryClient.invalidateQueries(queryKeys.favoritesGetList);
|
||||
onSuccess: (data) => {
|
||||
queryClient.setQueryData(queryKeys.favoritesGetList.queryKey, data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -140,6 +140,10 @@ export const useUpdateUserFavorites = () => {
|
|||
return { ...favorite, index };
|
||||
});
|
||||
});
|
||||
},
|
||||
onSuccess: (data) => {
|
||||
console.log(data);
|
||||
// queryClient.setQueryData(queryKeys.favoritesGetList.queryKey, data);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
@ -167,12 +171,19 @@ export const prefetchGetUserList = async (
|
|||
|
||||
export const useInviteUser = () => {
|
||||
const { openSuccessMessage } = useBusterNotifications();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
return useMutation({
|
||||
mutationFn: inviteUser,
|
||||
onSuccess: () => {
|
||||
openSuccessMessage('Invites sent');
|
||||
// queryClient.invalidateQueries(queryKeys.userGetUserList);
|
||||
const user = queryClient.getQueryData(queryKeys.userGetUserMyself.queryKey);
|
||||
const teamId = user?.organizations?.[0]?.id;
|
||||
if (teamId) {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: [queryKeys.userGetUserList({ team_id: teamId }).queryKey]
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
|
@ -6,10 +6,8 @@ import type {
|
|||
BusterUserListItem
|
||||
} from '@/api/asset_interfaces/users';
|
||||
import type {
|
||||
UsersFavoritePostPayload,
|
||||
UserFavoriteDeletePayload,
|
||||
UserUpdateFavoritesPayload,
|
||||
UserRequestUserListPayload
|
||||
UserRequestUserListPayload,
|
||||
UsersFavoritePostPayload
|
||||
} from '@/api/request_interfaces/user/interfaces';
|
||||
import { mainApi } from '../instances';
|
||||
import { serverFetch } from '../../createServerInstance';
|
||||
|
@ -82,12 +80,14 @@ export const inviteUser = async ({
|
|||
emails: string[];
|
||||
team_ids?: string[];
|
||||
}) => {
|
||||
return mainApi.post(`/users/invite`, {
|
||||
return mainApi.post<null>(`/users/invite`, {
|
||||
emails,
|
||||
team_ids
|
||||
});
|
||||
};
|
||||
|
||||
//USER FAVORITES
|
||||
|
||||
export const getUserFavorites = async () => {
|
||||
return mainApi.get<BusterUserFavorite[]>(`/users/favorites`).then((response) => response.data);
|
||||
};
|
||||
|
@ -109,38 +109,39 @@ export const createUserFavorite_server = async (payload: UsersFavoritePostPayloa
|
|||
});
|
||||
};
|
||||
|
||||
export const deleteUserFavorite = async (payload: UserFavoriteDeletePayload) => {
|
||||
export const deleteUserFavorite = async (id: string) => {
|
||||
return mainApi
|
||||
.delete<BusterUserFavorite[]>(`/users/favorites`, { data: payload })
|
||||
.delete<BusterUserFavorite[]>(`/users/favorites/${id}`)
|
||||
.then((response) => response.data);
|
||||
};
|
||||
|
||||
export const deleteUserFavorite_server = async (payload: UserFavoriteDeletePayload) => {
|
||||
return serverFetch<BusterUserFavorite[]>(`/users/favorites`, {
|
||||
method: 'DELETE',
|
||||
body: JSON.stringify(payload)
|
||||
export const deleteUserFavorite_server = async (id: string) => {
|
||||
return serverFetch<BusterUserFavorite[]>(`/users/favorites/${id}`, {
|
||||
method: 'DELETE'
|
||||
});
|
||||
};
|
||||
|
||||
export const updateUserFavorites = async (payload: UserUpdateFavoritesPayload) => {
|
||||
export const updateUserFavorites = async (payload: string[]) => {
|
||||
return mainApi
|
||||
.put<BusterUserFavorite[]>(`/users/favorites`, payload)
|
||||
.then((response) => response.data);
|
||||
};
|
||||
|
||||
export const updateUserFavorites_server = async (payload: UserUpdateFavoritesPayload) => {
|
||||
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: UserRequestUserListPayload) => {
|
||||
return mainApi
|
||||
.get<BusterUserListItem[]>(`/users/list`, { params: payload })
|
||||
.get<BusterUserListItem[]>(`/users`, { params: payload })
|
||||
.then((response) => response.data);
|
||||
};
|
||||
|
||||
export const getUserList_server = async (payload: UserRequestUserListPayload) => {
|
||||
return serverFetch<BusterUserListItem[]>(`/users/list`, { params: payload });
|
||||
return serverFetch<BusterUserListItem[]>(`/users`, { params: payload });
|
||||
};
|
||||
|
|
|
@ -4,17 +4,8 @@ export type UsersFavoritePostPayload = {
|
|||
id: string;
|
||||
asset_type: ShareAssetType;
|
||||
index?: number;
|
||||
name: string;
|
||||
}[];
|
||||
|
||||
export type UserFavoriteDeletePayload = {
|
||||
id: string;
|
||||
asset_type: ShareAssetType;
|
||||
}[];
|
||||
|
||||
export interface UserUpdateFavoritesPayload {
|
||||
favorites: string[]; // Array of favorite ids
|
||||
}
|
||||
name: string; //just used for the UI for optimistic update
|
||||
};
|
||||
|
||||
export interface UserRequestUserListPayload {
|
||||
team_id: string;
|
||||
|
|
|
@ -48,20 +48,13 @@ export const FavoriteStar: React.FC<{
|
|||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
if (!isFavorited)
|
||||
return await addItemToFavorite([
|
||||
{
|
||||
asset_type: type,
|
||||
id,
|
||||
name
|
||||
}
|
||||
]);
|
||||
|
||||
await removeItemFromFavorite([
|
||||
{
|
||||
return await addItemToFavorite({
|
||||
asset_type: type,
|
||||
id
|
||||
}
|
||||
]);
|
||||
id,
|
||||
name
|
||||
});
|
||||
|
||||
await removeItemFromFavorite(id);
|
||||
});
|
||||
|
||||
const tooltipText = isFavorited ? 'Remove from favorites' : 'Add to favorites';
|
||||
|
|
|
@ -25,7 +25,7 @@ const mockFavorites = [
|
|||
{
|
||||
id: '123',
|
||||
name: 'Favorite Dashboard',
|
||||
asset_type: ShareAssetType.DASHBOARD,
|
||||
ype: ShareAssetType.DASHBOARD,
|
||||
asset_id: '123',
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
|
@ -35,7 +35,7 @@ const mockFavorites = [
|
|||
id: '456',
|
||||
name: 'Important Metrics',
|
||||
route: createBusterRoute({ route: BusterRoutes.APP_METRIC_ID, metricId: '456' }),
|
||||
asset_type: ShareAssetType.METRIC,
|
||||
ype: ShareAssetType.METRIC,
|
||||
asset_id: '456',
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
|
@ -44,7 +44,7 @@ const mockFavorites = [
|
|||
id: '789',
|
||||
name: 'Favorite Metric 3',
|
||||
route: createBusterRoute({ route: BusterRoutes.APP_METRIC_ID, metricId: '789' }),
|
||||
asset_type: ShareAssetType.METRIC,
|
||||
ype: ShareAssetType.METRIC,
|
||||
asset_id: '789',
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString()
|
||||
|
|
|
@ -18,7 +18,7 @@ import { SupportModal } from '../modal/SupportModal';
|
|||
import { InvitePeopleModal } from '../modal/InvitePeopleModal';
|
||||
import { useMemoizedFn } from '@/hooks';
|
||||
import { SidebarUserFooter } from './SidebarUserFooter/SidebarUserFooter';
|
||||
import { useGetUserFavorites } from '@/api/buster_rest';
|
||||
import { useGetUserFavorites, useUpdateUserFavorites } from '@/api/buster_rest';
|
||||
|
||||
const topItems: ISidebarList = {
|
||||
items: [
|
||||
|
@ -114,6 +114,11 @@ export const SidebarPrimary = React.memo(() => {
|
|||
const currentRoute = useAppLayoutContextSelector((x) => x.currentRoute);
|
||||
const onToggleInviteModal = useAppLayoutContextSelector((s) => s.onToggleInviteModal);
|
||||
const [openSupportModal, setOpenSupportModal] = useState(false);
|
||||
const { mutateAsync: updateUserFavorites } = useUpdateUserFavorites();
|
||||
|
||||
const onFavoritesReorder = useMemoizedFn((itemIds: string[]) => {
|
||||
updateUserFavorites(itemIds);
|
||||
});
|
||||
|
||||
const sidebarItems: SidebarProps['content'] = useMemo(() => {
|
||||
const items = [topItems];
|
||||
|
@ -125,13 +130,13 @@ export const SidebarPrimary = React.memo(() => {
|
|||
items.push(yourStuff);
|
||||
|
||||
if (favorites && favorites.length > 0) {
|
||||
items.push(favoritesDropdown(favorites));
|
||||
items.push(favoritesDropdown(favorites, onFavoritesReorder));
|
||||
}
|
||||
|
||||
items.push(tryGroup(onToggleInviteModal, () => setOpenSupportModal(true)));
|
||||
|
||||
return items;
|
||||
}, [isAdmin, favorites, currentRoute]);
|
||||
}, [isAdmin, favorites, currentRoute, onFavoritesReorder]);
|
||||
|
||||
const onCloseSupportModal = useMemoizedFn(() => setOpenSupportModal(false));
|
||||
|
||||
|
@ -206,13 +211,14 @@ const GlobalModals = React.memo(
|
|||
);
|
||||
GlobalModals.displayName = 'GlobalModals';
|
||||
|
||||
const favoritesDropdown = (favorites: BusterUserFavorite[]): ISidebarGroup => {
|
||||
const favoritesDropdown = (
|
||||
favorites: BusterUserFavorite[],
|
||||
onItemsReorder: (itemIds: string[]) => void
|
||||
): ISidebarGroup => {
|
||||
return {
|
||||
label: 'Favorites',
|
||||
isSortable: true,
|
||||
onItemsReorder: (itemIds) => {
|
||||
console.warn('onItemsReorder', itemIds);
|
||||
},
|
||||
onItemsReorder,
|
||||
items: favorites.map((favorite) => {
|
||||
const Icon = assetTypeToIcon(favorite.asset_type);
|
||||
const route = assetTypeToRoute(favorite.asset_type, favorite.id);
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
'use client';
|
||||
|
||||
import React from 'react';
|
||||
import { ShareAssetType } from '@/api/asset_interfaces';
|
||||
import { BusterLogo } from '@/assets/svg/BusterLogo';
|
||||
import { Title } from '@/components/ui/typography';
|
||||
import { useBusterNotifications } from '@/context/BusterNotifications';
|
||||
|
@ -10,10 +9,9 @@ import { Button } from '@/components/ui/buttons';
|
|||
import Link from 'next/link';
|
||||
|
||||
export const AppNoPageAccess: React.FC<{
|
||||
asset_type: ShareAssetType;
|
||||
metricId?: string;
|
||||
dashboardId?: string;
|
||||
}> = React.memo(({ asset_type }) => {
|
||||
}> = React.memo(({}) => {
|
||||
const { openInfoMessage } = useBusterNotifications();
|
||||
|
||||
return (
|
||||
|
|
|
@ -167,7 +167,7 @@ const TitleCell = React.memo<{ title: string; status: VerificationStatus; chatId
|
|||
<div className="flex items-center" onClick={onFavoriteDivClick}>
|
||||
<FavoriteStar
|
||||
id={chatId}
|
||||
type={ShareAssetType.METRIC}
|
||||
type={ShareAssetType.CHAT}
|
||||
iconStyle="tertiary"
|
||||
title={title}
|
||||
className="hidden! group-hover:flex!"
|
||||
|
|
|
@ -153,12 +153,15 @@ const ThreeDotButton: React.FC<{
|
|||
value: 'add-to-favorites',
|
||||
loading: addingToFavorites,
|
||||
onClick: async () => {
|
||||
await addUserFavorite(
|
||||
selectedRowKeys.map((id) => ({
|
||||
id,
|
||||
asset_type: ShareAssetType.METRIC,
|
||||
name: 'Metric'
|
||||
}))
|
||||
await Promise.all(
|
||||
selectedRowKeys.map((id) => {
|
||||
const name = userFavorites?.find((f) => f.id === id)?.name || '';
|
||||
return addUserFavorite({
|
||||
id,
|
||||
asset_type: ShareAssetType.METRIC,
|
||||
name
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
},
|
||||
|
@ -168,10 +171,7 @@ const ThreeDotButton: React.FC<{
|
|||
loading: removingFromFavorites,
|
||||
value: 'remove-from-favorites',
|
||||
onClick: async () => {
|
||||
const allFavorites: Parameters<typeof removeUserFavorite>[0] = userFavorites
|
||||
.filter((f) => !selectedRowKeys.includes(f.id))
|
||||
.map((f) => ({ id: f.id, asset_type: ShareAssetType.METRIC }));
|
||||
await removeUserFavorite(allFavorites);
|
||||
await Promise.all(selectedRowKeys.map((id) => removeUserFavorite(id)));
|
||||
}
|
||||
}
|
||||
];
|
||||
|
|
|
@ -141,12 +141,16 @@ const ThreeDotButton: React.FC<{
|
|||
icon: <Star />,
|
||||
value: 'add-to-favorites',
|
||||
onClick: async () => {
|
||||
const allFavorites: Parameters<typeof addUserFavorite>[0] = selectedRowKeys.map((id) => ({
|
||||
id,
|
||||
asset_type: ShareAssetType.DASHBOARD,
|
||||
name: 'Dashboard'
|
||||
}));
|
||||
await addUserFavorite(allFavorites);
|
||||
await Promise.all(
|
||||
selectedRowKeys.map((id) => {
|
||||
const name = userFavorites?.find((f) => f.id === id)?.name || '';
|
||||
return addUserFavorite({
|
||||
id,
|
||||
asset_type: ShareAssetType.DASHBOARD,
|
||||
name
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -154,11 +158,7 @@ const ThreeDotButton: React.FC<{
|
|||
icon: <Xmark />,
|
||||
value: 'remove-from-favorites',
|
||||
onClick: async () => {
|
||||
const allFavorites: Parameters<typeof removeUserFavorite>[0] = userFavorites
|
||||
.map((f) => f.id)
|
||||
.filter((id) => !selectedRowKeys.includes(id))
|
||||
.map((id) => ({ id, asset_type: ShareAssetType.DASHBOARD }));
|
||||
await removeUserFavorite(allFavorites);
|
||||
await Promise.all(selectedRowKeys.map((id) => removeUserFavorite(id)));
|
||||
}
|
||||
}
|
||||
];
|
||||
|
|
|
@ -194,12 +194,16 @@ const ThreeDotButton: React.FC<{
|
|||
icon: <Star />,
|
||||
value: 'add-to-favorites',
|
||||
onClick: async () => {
|
||||
const allFavorites: Parameters<typeof addUserFavorite>[0] = selectedRowKeys.map((id) => ({
|
||||
id,
|
||||
asset_type: ShareAssetType.METRIC,
|
||||
name: 'Metric'
|
||||
}));
|
||||
await addUserFavorite(allFavorites);
|
||||
await Promise.all(
|
||||
selectedRowKeys.map((id) => {
|
||||
const name = userFavorites?.find((f) => f.id === id)?.name || '';
|
||||
return addUserFavorite({
|
||||
id,
|
||||
asset_type: ShareAssetType.METRIC,
|
||||
name
|
||||
});
|
||||
})
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -207,11 +211,7 @@ const ThreeDotButton: React.FC<{
|
|||
icon: <Xmark />,
|
||||
value: 'remove-from-favorites',
|
||||
onClick: async () => {
|
||||
const allFavorites: Parameters<typeof removeUserFavorite>[0] = userFavorites
|
||||
.map((f) => f.id)
|
||||
.filter((id) => !selectedRowKeys.includes(id))
|
||||
.map((id) => ({ id, asset_type: ShareAssetType.METRIC }));
|
||||
await removeUserFavorite(allFavorites);
|
||||
await Promise.all(selectedRowKeys.map((id) => removeUserFavorite(id)));
|
||||
}
|
||||
}
|
||||
];
|
||||
|
|
|
@ -72,11 +72,7 @@ export const AppAssetCheckLayout: React.FC<
|
|||
if (!has_access && !pagePublic) {
|
||||
return (
|
||||
<ClientSideAnonCheck jwtToken={jwtToken}>
|
||||
<AppNoPageAccess
|
||||
asset_type={type as ShareAssetType}
|
||||
metricId={props.metricId}
|
||||
dashboardId={props.dashboardId}
|
||||
/>
|
||||
<AppNoPageAccess metricId={props.metricId} dashboardId={props.dashboardId} />
|
||||
</ClientSideAnonCheck>
|
||||
);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue