Favorite folders (#7998)
closes - #5755 --------- Co-authored-by: martmull <martmull@hotmail.fr> Co-authored-by: Lucas Bordeau <bordeau.lucas@gmail.com> Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -1,127 +1,197 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { Avatar, isDefined } from 'twenty-ui';
|
||||
|
||||
import { FavoritesSkeletonLoader } from '@/favorites/components/FavoritesSkeletonLoader';
|
||||
import { useIsPrefetchLoading } from '@/prefetch/hooks/useIsPrefetchLoading';
|
||||
import { FavoriteFolderNavigationDrawerItemDropdown } from '@/favorites/components/FavoriteFolderNavigationDrawerItemDropdown';
|
||||
import { FavoriteIcon } from '@/favorites/components/FavoriteIcon';
|
||||
import { useDeleteFavorite } from '@/favorites/hooks/useDeleteFavorite';
|
||||
import { useDeleteFavoriteFolder } from '@/favorites/hooks/useDeleteFavoriteFolder';
|
||||
import { useRenameFavoriteFolder } from '@/favorites/hooks/useRenameFavoriteFolder';
|
||||
import { useReorderFavorite } from '@/favorites/hooks/useReorderFavorite';
|
||||
import { activeFavoriteFolderIdState } from '@/favorites/states/activeFavoriteFolderIdState';
|
||||
import { isLocationMatchingFavorite } from '@/favorites/utils/isLocationMatchingFavorite';
|
||||
import { ProcessedFavorite } from '@/favorites/utils/sortFavorites';
|
||||
import { DraggableItem } from '@/ui/layout/draggable-list/components/DraggableItem';
|
||||
import { DraggableList } from '@/ui/layout/draggable-list/components/DraggableList';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||
import { NavigationDrawerInput } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerInput';
|
||||
import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
|
||||
import { NavigationDrawerSection } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSection';
|
||||
import { NavigationDrawerSectionTitle } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSectionTitle';
|
||||
import { useNavigationSection } from '@/ui/navigation/navigation-drawer/hooks/useNavigationSection';
|
||||
import { NavigationDrawerItemsCollapsableContainer } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItemsCollapsableContainer';
|
||||
import { NavigationDrawerSubItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSubItem';
|
||||
import { getNavigationSubItemLeftAdornment } from '@/ui/navigation/navigation-drawer/utils/getNavigationSubItemLeftAdornment';
|
||||
import { useState } from 'react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { IconFolder, IconHeartOff, LightIconButton } from 'twenty-ui';
|
||||
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { NavigationDrawerAnimatedCollapseWrapper } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerAnimatedCollapseWrapper';
|
||||
import { NavigationDrawerItemsCollapsedContainer } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItemsCollapsedContainer';
|
||||
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
|
||||
import { useFavorites } from '../hooks/useFavorites';
|
||||
type CurrentWorkspaceMemberFavoritesProps = {
|
||||
folder: {
|
||||
folderId: string;
|
||||
folderName: string;
|
||||
favorites: ProcessedFavorite[];
|
||||
};
|
||||
isGroup: boolean;
|
||||
};
|
||||
|
||||
const StyledContainer = styled(NavigationDrawerSection)`
|
||||
width: 100%;
|
||||
`;
|
||||
export const CurrentWorkspaceMemberFavorites = ({
|
||||
folder,
|
||||
isGroup,
|
||||
}: CurrentWorkspaceMemberFavoritesProps) => {
|
||||
const currentPath = useLocation().pathname;
|
||||
const currentViewPath = useLocation().pathname + useLocation().search;
|
||||
|
||||
const StyledAvatar = styled(Avatar)`
|
||||
:hover {
|
||||
cursor: grab;
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledNavigationDrawerItem = styled(NavigationDrawerItem)`
|
||||
:active {
|
||||
cursor: grabbing;
|
||||
|
||||
.fav-avatar:hover {
|
||||
cursor: grabbing;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
export const CurrentWorkspaceMemberFavorites = () => {
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
|
||||
const { favorites, handleReorderFavorite } = useFavorites();
|
||||
const loading = useIsPrefetchLoading();
|
||||
const { toggleNavigationSection, isNavigationSectionOpenState } =
|
||||
useNavigationSection('Favorites');
|
||||
const isNavigationSectionOpen = useRecoilValue(isNavigationSectionOpenState);
|
||||
|
||||
if (loading && isDefined(currentWorkspaceMember)) {
|
||||
return <FavoritesSkeletonLoader />;
|
||||
}
|
||||
|
||||
const currentWorkspaceMemberFavorites = favorites.filter(
|
||||
(favorite) => favorite.workspaceMemberId === currentWorkspaceMember?.id,
|
||||
const [isFavoriteFolderRenaming, setIsFavoriteFolderRenaming] =
|
||||
useState(false);
|
||||
const [favoriteFolderName, setFavoriteFolderName] = useState(
|
||||
folder.folderName,
|
||||
);
|
||||
const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
|
||||
const [activeFavoriteFolderId, setActiveFavoriteFolderId] = useRecoilState(
|
||||
activeFavoriteFolderIdState,
|
||||
);
|
||||
const isOpen = activeFavoriteFolderId === folder.folderId;
|
||||
|
||||
if (
|
||||
!currentWorkspaceMemberFavorites ||
|
||||
currentWorkspaceMemberFavorites.length === 0
|
||||
)
|
||||
return <></>;
|
||||
const handleToggle = () => {
|
||||
setActiveFavoriteFolderId(isOpen ? null : folder.folderId);
|
||||
};
|
||||
|
||||
const isGroup = currentWorkspaceMemberFavorites.length > 1;
|
||||
const { renameFavoriteFolder } = useRenameFavoriteFolder();
|
||||
const { deleteFavoriteFolder } = useDeleteFavoriteFolder();
|
||||
const {
|
||||
closeDropdown: closeFavoriteFolderEditDropdown,
|
||||
isDropdownOpen: isFavoriteFolderEditDropdownOpen,
|
||||
} = useDropdown(`favorite-folder-edit-${folder.folderId}`);
|
||||
const selectedFavoriteIndex = folder.favorites.findIndex((favorite) =>
|
||||
isLocationMatchingFavorite(currentPath, currentViewPath, favorite),
|
||||
);
|
||||
const handleReorderFavorite = useReorderFavorite();
|
||||
|
||||
const draggableListContent = (
|
||||
<DraggableList
|
||||
onDragEnd={handleReorderFavorite}
|
||||
draggableItems={
|
||||
<>
|
||||
{currentWorkspaceMemberFavorites.map((favorite, index) => {
|
||||
const {
|
||||
id,
|
||||
labelIdentifier,
|
||||
avatarUrl,
|
||||
avatarType,
|
||||
link,
|
||||
recordId,
|
||||
} = favorite;
|
||||
const deleteFavorite = useDeleteFavorite();
|
||||
|
||||
return (
|
||||
<DraggableItem
|
||||
key={id}
|
||||
draggableId={id}
|
||||
index={index}
|
||||
itemComponent={
|
||||
<StyledNavigationDrawerItem
|
||||
key={id}
|
||||
label={labelIdentifier}
|
||||
Icon={() => (
|
||||
<StyledAvatar
|
||||
placeholderColorSeed={recordId}
|
||||
avatarUrl={avatarUrl}
|
||||
type={avatarType}
|
||||
placeholder={labelIdentifier}
|
||||
className="fav-avatar"
|
||||
/>
|
||||
)}
|
||||
to={link}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
}
|
||||
const favoriteFolderContentLength = folder.favorites.length;
|
||||
|
||||
const handleSubmitRename = async (value: string) => {
|
||||
if (value === '') return;
|
||||
await renameFavoriteFolder(folder.folderId, value);
|
||||
setIsFavoriteFolderRenaming(false);
|
||||
return true;
|
||||
};
|
||||
|
||||
const handleCancelRename = () => {
|
||||
setFavoriteFolderName(folder.folderName);
|
||||
setIsFavoriteFolderRenaming(false);
|
||||
};
|
||||
|
||||
const handleClickOutside = async (
|
||||
event: MouseEvent | TouchEvent,
|
||||
value: string,
|
||||
) => {
|
||||
if (!value) {
|
||||
setIsFavoriteFolderRenaming(false);
|
||||
return;
|
||||
}
|
||||
|
||||
await renameFavoriteFolder(folder.folderId, value);
|
||||
setIsFavoriteFolderRenaming(false);
|
||||
};
|
||||
|
||||
const handleFavoriteFolderDelete = async () => {
|
||||
if (folder.favorites.length > 0) {
|
||||
setIsDeleteModalOpen(true);
|
||||
closeFavoriteFolderEditDropdown();
|
||||
} else {
|
||||
await deleteFavoriteFolder(folder.folderId);
|
||||
closeFavoriteFolderEditDropdown();
|
||||
}
|
||||
};
|
||||
|
||||
const handleConfirmDelete = async () => {
|
||||
await deleteFavoriteFolder(folder.folderId);
|
||||
setIsDeleteModalOpen(false);
|
||||
};
|
||||
|
||||
const rightOptions = (
|
||||
<FavoriteFolderNavigationDrawerItemDropdown
|
||||
folderId={folder.folderId}
|
||||
onRename={() => setIsFavoriteFolderRenaming(true)}
|
||||
onDelete={handleFavoriteFolderDelete}
|
||||
closeDropdown={closeFavoriteFolderEditDropdown}
|
||||
/>
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledContainer>
|
||||
<NavigationDrawerAnimatedCollapseWrapper>
|
||||
<NavigationDrawerSectionTitle
|
||||
label="Favorites"
|
||||
onClick={() => toggleNavigationSection()}
|
||||
/>
|
||||
</NavigationDrawerAnimatedCollapseWrapper>
|
||||
<>
|
||||
<NavigationDrawerItemsCollapsableContainer
|
||||
key={folder.folderId}
|
||||
isGroup={isGroup}
|
||||
>
|
||||
{isFavoriteFolderRenaming ? (
|
||||
<NavigationDrawerInput
|
||||
Icon={IconFolder}
|
||||
value={favoriteFolderName}
|
||||
onChange={setFavoriteFolderName}
|
||||
onSubmit={handleSubmitRename}
|
||||
onCancel={handleCancelRename}
|
||||
onClickOutside={handleClickOutside}
|
||||
hotkeyScope="favorites-folder-input"
|
||||
/>
|
||||
) : (
|
||||
<NavigationDrawerItem
|
||||
key={folder.folderId}
|
||||
label={folder.folderName}
|
||||
Icon={IconFolder}
|
||||
onClick={handleToggle}
|
||||
rightOptions={rightOptions}
|
||||
className="navigation-drawer-item"
|
||||
active={isFavoriteFolderEditDropdownOpen}
|
||||
/>
|
||||
)}
|
||||
|
||||
{isNavigationSectionOpen && (
|
||||
<ScrollWrapper contextProviderName="navigationDrawer">
|
||||
<NavigationDrawerItemsCollapsedContainer isGroup={isGroup}>
|
||||
{draggableListContent}
|
||||
</NavigationDrawerItemsCollapsedContainer>
|
||||
</ScrollWrapper>
|
||||
)}
|
||||
</StyledContainer>
|
||||
{isOpen && (
|
||||
<DraggableList
|
||||
onDragEnd={handleReorderFavorite}
|
||||
draggableItems={
|
||||
<>
|
||||
{folder.favorites.map((favorite, index) => (
|
||||
<DraggableItem
|
||||
key={favorite.id}
|
||||
draggableId={favorite.id}
|
||||
index={index}
|
||||
itemComponent={
|
||||
<NavigationDrawerSubItem
|
||||
key={favorite.id}
|
||||
label={favorite.labelIdentifier}
|
||||
Icon={() => <FavoriteIcon favorite={favorite} />}
|
||||
to={favorite.link}
|
||||
active={index === selectedFavoriteIndex}
|
||||
subItemState={getNavigationSubItemLeftAdornment({
|
||||
index,
|
||||
arrayLength: favoriteFolderContentLength,
|
||||
selectedIndex: selectedFavoriteIndex,
|
||||
})}
|
||||
rightOptions={
|
||||
<LightIconButton
|
||||
Icon={IconHeartOff}
|
||||
onClick={() => deleteFavorite(favorite.id)}
|
||||
accent="tertiary"
|
||||
/>
|
||||
}
|
||||
isDraggable
|
||||
/>
|
||||
}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</NavigationDrawerItemsCollapsableContainer>
|
||||
|
||||
<ConfirmationModal
|
||||
isOpen={isDeleteModalOpen}
|
||||
setIsOpen={setIsDeleteModalOpen}
|
||||
title={`Remove ${folder.favorites.length} ${folder.favorites.length > 1 ? 'favorites' : 'favorite'}?`}
|
||||
subtitle={`This action will delete this favorite folder ${folder.favorites.length > 1 ? `and all ${folder.favorites.length} favorites` : 'and the favorite'} inside. Do you want to continue?`}
|
||||
onConfirmClick={handleConfirmDelete}
|
||||
deleteButtonText="Delete Folder"
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@ -0,0 +1,137 @@
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { useLocation } from 'react-router-dom';
|
||||
import { useRecoilState, useRecoilValue } from 'recoil';
|
||||
import {
|
||||
IconFolderPlus,
|
||||
IconHeartOff,
|
||||
isDefined,
|
||||
LightIconButton,
|
||||
} from 'twenty-ui';
|
||||
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { FavoriteIcon } from '@/favorites/components/FavoriteIcon';
|
||||
import { FavoriteFolders } from '@/favorites/components/FavoritesFolders';
|
||||
import { FavoritesSkeletonLoader } from '@/favorites/components/FavoritesSkeletonLoader';
|
||||
import { useDeleteFavorite } from '@/favorites/hooks/useDeleteFavorite';
|
||||
import { useFavorites } from '@/favorites/hooks/useFavorites';
|
||||
import { useReorderFavorite } from '@/favorites/hooks/useReorderFavorite';
|
||||
import { isFavoriteFolderCreatingState } from '@/favorites/states/isFavoriteFolderCreatingState';
|
||||
import { isLocationMatchingFavorite } from '@/favorites/utils/isLocationMatchingFavorite';
|
||||
import { useIsPrefetchLoading } from '@/prefetch/hooks/useIsPrefetchLoading';
|
||||
import { DraggableItem } from '@/ui/layout/draggable-list/components/DraggableItem';
|
||||
import { DraggableList } from '@/ui/layout/draggable-list/components/DraggableList';
|
||||
import { NavigationDrawerAnimatedCollapseWrapper } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerAnimatedCollapseWrapper';
|
||||
import { NavigationDrawerItem } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
|
||||
import { NavigationDrawerSection } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSection';
|
||||
import { NavigationDrawerSectionTitle } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerSectionTitle';
|
||||
import { useNavigationSection } from '@/ui/navigation/navigation-drawer/hooks/useNavigationSection';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
|
||||
export const CurrentWorkspaceMemberFavoritesFolders = () => {
|
||||
const currentPath = useLocation().pathname;
|
||||
const currentViewPath = useLocation().pathname + useLocation().search;
|
||||
const theme = useTheme();
|
||||
const currentWorkspaceMember = useRecoilValue(currentWorkspaceMemberState);
|
||||
const favorites = useFavorites();
|
||||
const deleteFavorite = useDeleteFavorite();
|
||||
const handleReorderFavorite = useReorderFavorite();
|
||||
const [isFavoriteFolderCreating, setIsFavoriteFolderCreating] =
|
||||
useRecoilState(isFavoriteFolderCreatingState);
|
||||
|
||||
const isFavoriteFolderEnabled = useIsFeatureEnabled(
|
||||
'IS_FAVORITE_FOLDER_ENABLED',
|
||||
);
|
||||
const loading = useIsPrefetchLoading();
|
||||
|
||||
const { toggleNavigationSection, isNavigationSectionOpenState } =
|
||||
useNavigationSection('Favorites');
|
||||
const isNavigationSectionOpen = useRecoilValue(isNavigationSectionOpenState);
|
||||
|
||||
const toggleNewFolder = () => {
|
||||
setIsFavoriteFolderCreating((current) => !current);
|
||||
};
|
||||
|
||||
if (loading && isDefined(currentWorkspaceMember)) {
|
||||
return <FavoritesSkeletonLoader />;
|
||||
}
|
||||
|
||||
const currentWorkspaceMemberFavorites = favorites.filter(
|
||||
(favorite) => favorite.workspaceMemberId === currentWorkspaceMember?.id,
|
||||
);
|
||||
|
||||
const orphanFavorites = currentWorkspaceMemberFavorites.filter(
|
||||
(favorite) => !favorite.favoriteFolderId,
|
||||
);
|
||||
|
||||
if (
|
||||
(!currentWorkspaceMemberFavorites ||
|
||||
currentWorkspaceMemberFavorites.length === 0) &&
|
||||
!isFavoriteFolderCreating
|
||||
) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<NavigationDrawerSection>
|
||||
<NavigationDrawerAnimatedCollapseWrapper>
|
||||
<NavigationDrawerSectionTitle
|
||||
label="Favorites"
|
||||
onClick={toggleNavigationSection}
|
||||
rightIcon={
|
||||
isFavoriteFolderEnabled ? (
|
||||
<IconFolderPlus size={theme.icon.size.sm} />
|
||||
) : undefined
|
||||
}
|
||||
onRightIconClick={
|
||||
isFavoriteFolderEnabled ? toggleNewFolder : undefined
|
||||
}
|
||||
/>
|
||||
</NavigationDrawerAnimatedCollapseWrapper>
|
||||
|
||||
{isNavigationSectionOpen && (
|
||||
<>
|
||||
{isFavoriteFolderEnabled && (
|
||||
<FavoriteFolders
|
||||
isNavigationSectionOpen={isNavigationSectionOpen}
|
||||
/>
|
||||
)}
|
||||
|
||||
{orphanFavorites.length > 0 && (
|
||||
<DraggableList
|
||||
onDragEnd={handleReorderFavorite}
|
||||
draggableItems={orphanFavorites.map((favorite, index) => (
|
||||
<DraggableItem
|
||||
key={favorite.id}
|
||||
draggableId={favorite.id}
|
||||
index={index}
|
||||
itemComponent={
|
||||
<NavigationDrawerItem
|
||||
key={favorite.id}
|
||||
className="navigation-drawer-item"
|
||||
label={favorite.labelIdentifier}
|
||||
Icon={() => <FavoriteIcon favorite={favorite} />}
|
||||
active={isLocationMatchingFavorite(
|
||||
currentPath,
|
||||
currentViewPath,
|
||||
favorite,
|
||||
)}
|
||||
to={favorite.link}
|
||||
rightOptions={
|
||||
<LightIconButton
|
||||
Icon={IconHeartOff}
|
||||
onClick={() => deleteFavorite(favorite.id)}
|
||||
accent="tertiary"
|
||||
/>
|
||||
}
|
||||
isDraggable={true}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
))}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</NavigationDrawerSection>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,65 @@
|
||||
import { FavoriteFolderHotkeyScope } from '@/favorites/constants/FavoriteFolderRightIconDropdownHotkeyScope';
|
||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import {
|
||||
IconDotsVertical,
|
||||
IconPencil,
|
||||
IconTrash,
|
||||
LightIconButton,
|
||||
MenuItem,
|
||||
} from 'twenty-ui';
|
||||
|
||||
type FavoriteFolderNavigationDrawerItemDropdownProps = {
|
||||
folderId: string;
|
||||
onRename: () => void;
|
||||
onDelete: () => void;
|
||||
closeDropdown: () => void;
|
||||
};
|
||||
|
||||
export const FavoriteFolderNavigationDrawerItemDropdown = ({
|
||||
folderId,
|
||||
onRename,
|
||||
onDelete,
|
||||
closeDropdown,
|
||||
}: FavoriteFolderNavigationDrawerItemDropdownProps) => {
|
||||
const handleRename = () => {
|
||||
onRename();
|
||||
closeDropdown();
|
||||
};
|
||||
|
||||
const handleDelete = () => {
|
||||
onDelete();
|
||||
closeDropdown();
|
||||
};
|
||||
|
||||
return (
|
||||
<Dropdown
|
||||
dropdownId={`favorite-folder-edit-${folderId}`}
|
||||
dropdownHotkeyScope={{
|
||||
scope: FavoriteFolderHotkeyScope.FavoriteFolderRightIconDropdown,
|
||||
}}
|
||||
data-select-disable
|
||||
clickableComponent={
|
||||
<LightIconButton Icon={IconDotsVertical} accent="tertiary" />
|
||||
}
|
||||
dropdownPlacement="right"
|
||||
dropdownOffset={{ y: -15 }}
|
||||
dropdownComponents={
|
||||
<DropdownMenuItemsContainer>
|
||||
<MenuItem
|
||||
LeftIcon={IconPencil}
|
||||
onClick={handleRename}
|
||||
accent="default"
|
||||
text="Rename"
|
||||
/>
|
||||
<MenuItem
|
||||
LeftIcon={IconTrash}
|
||||
onClick={handleDelete}
|
||||
accent="danger"
|
||||
text="Delete"
|
||||
/>
|
||||
</DropdownMenuItemsContainer>
|
||||
}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,27 @@
|
||||
import { ProcessedFavorite } from '@/favorites/utils/sortFavorites';
|
||||
import { useGetStandardObjectIcon } from '@/object-metadata/hooks/useGetStandardObjectIcon';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { Avatar, useIcons } from 'twenty-ui';
|
||||
|
||||
export const FavoriteIcon = ({ favorite }: { favorite: ProcessedFavorite }) => {
|
||||
const theme = useTheme();
|
||||
const { getIcon } = useIcons();
|
||||
const { Icon: StandardIcon, IconColor } = useGetStandardObjectIcon(
|
||||
favorite.objectNameSingular || '',
|
||||
);
|
||||
const IconToUse =
|
||||
StandardIcon || (favorite.Icon ? getIcon(favorite.Icon) : undefined);
|
||||
const iconColorToUse = StandardIcon ? IconColor : theme.font.color.secondary;
|
||||
|
||||
return (
|
||||
<Avatar
|
||||
size="md"
|
||||
type={favorite.avatarType}
|
||||
Icon={IconToUse}
|
||||
iconColor={iconColorToUse}
|
||||
avatarUrl={favorite.avatarUrl}
|
||||
placeholder={favorite.labelIdentifier}
|
||||
placeholderColorSeed={favorite.recordId}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,85 @@
|
||||
import { useState } from 'react';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { IconFolder } from 'twenty-ui';
|
||||
|
||||
import { CurrentWorkspaceMemberFavorites } from '@/favorites/components/CurrentWorkspaceMemberFavorites';
|
||||
import { FavoriteFolderHotkeyScope } from '@/favorites/constants/FavoriteFolderRightIconDropdownHotkeyScope';
|
||||
import { useCreateFavoriteFolder } from '@/favorites/hooks/useCreateFavoriteFolder';
|
||||
import { useFavoritesByFolder } from '@/favorites/hooks/useFavoritesByFolder';
|
||||
import { isFavoriteFolderCreatingState } from '@/favorites/states/isFavoriteFolderCreatingState';
|
||||
import { NavigationDrawerInput } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerInput';
|
||||
|
||||
type FavoriteFoldersProps = {
|
||||
isNavigationSectionOpen: boolean;
|
||||
};
|
||||
|
||||
export const FavoriteFolders = ({
|
||||
isNavigationSectionOpen,
|
||||
}: FavoriteFoldersProps) => {
|
||||
const [newFolderName, setNewFolderName] = useState('');
|
||||
|
||||
const favoritesByFolder = useFavoritesByFolder();
|
||||
const createFavoriteFolder = useCreateFavoriteFolder();
|
||||
|
||||
const [isFavoriteFolderCreating, setIsFavoriteFolderCreating] =
|
||||
useRecoilState(isFavoriteFolderCreatingState);
|
||||
|
||||
const handleFavoriteFolderNameChange = (value: string) => {
|
||||
setNewFolderName(value);
|
||||
};
|
||||
|
||||
const handleSubmitFavoriteFolderCreation = async (value: string) => {
|
||||
if (value === '') return;
|
||||
|
||||
setIsFavoriteFolderCreating(false);
|
||||
setNewFolderName('');
|
||||
await createFavoriteFolder(value);
|
||||
return true;
|
||||
};
|
||||
|
||||
const handleClickOutside = async (
|
||||
event: MouseEvent | TouchEvent,
|
||||
value: string,
|
||||
) => {
|
||||
if (!value) {
|
||||
setIsFavoriteFolderCreating(false);
|
||||
return;
|
||||
}
|
||||
|
||||
setIsFavoriteFolderCreating(false);
|
||||
setNewFolderName('');
|
||||
await createFavoriteFolder(value);
|
||||
};
|
||||
|
||||
const handleCancelFavoriteFolderCreation = () => {
|
||||
setNewFolderName('');
|
||||
setIsFavoriteFolderCreating(false);
|
||||
};
|
||||
|
||||
if (!isNavigationSectionOpen) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{isFavoriteFolderCreating && (
|
||||
<NavigationDrawerInput
|
||||
Icon={IconFolder}
|
||||
value={newFolderName}
|
||||
onChange={handleFavoriteFolderNameChange}
|
||||
onSubmit={handleSubmitFavoriteFolderCreation}
|
||||
onCancel={handleCancelFavoriteFolderCreation}
|
||||
onClickOutside={handleClickOutside}
|
||||
hotkeyScope={FavoriteFolderHotkeyScope.FavoriteFolderNavigationInput}
|
||||
/>
|
||||
)}
|
||||
{favoritesByFolder.map((folder) => (
|
||||
<CurrentWorkspaceMemberFavorites
|
||||
key={folder.folderId}
|
||||
folder={folder}
|
||||
isGroup={favoritesByFolder.length > 1}
|
||||
/>
|
||||
))}
|
||||
</>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user