Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WEB-1907] Fix: favorites #5292

Merged
merged 22 commits into from
Aug 4, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
790e16e
chore: workspace user favorites
NarayanBavisetti Jul 23, 2024
9d1ce25
chore: added project id in entity type
NarayanBavisetti Jul 25, 2024
827e11f
chore: removed the extra key
NarayanBavisetti Jul 25, 2024
9ee459f
chore: removed the project member filter
NarayanBavisetti Jul 26, 2024
1054263
chore: updated the project permission layer
NarayanBavisetti Jul 26, 2024
d8f13b3
chore: updated the workspace group favorite filter
NarayanBavisetti Jul 26, 2024
9454e4d
fix: project favorite toggle
NarayanBavisetti Jul 29, 2024
4077618
chore: Fav feature
gakshita Jul 29, 2024
4e80865
Merge branch 'preview' of https://github.com/makeplane/plane into cho…
gakshita Jul 29, 2024
db9be42
fix: build errors + added navigation
gakshita Jul 29, 2024
44bcec7
fix: added remove entity icon
gakshita Jul 30, 2024
11dcce2
fix: nomenclature
gakshita Jul 30, 2024
049b96e
chore: hard delete favorites
NarayanBavisetti Jul 31, 2024
d51953f
fix: review changes
gakshita Aug 1, 2024
e1400da
Merge branch 'chore/soft-delete-favorite' of https://github.com/makep…
gakshita Aug 1, 2024
eff77c3
fix: added optimistic addition to the store
gakshita Aug 1, 2024
c4f3f9a
chore: user favorite hard delete
NarayanBavisetti Aug 1, 2024
e700071
fix: linting fixed
gakshita Aug 1, 2024
c26c028
Merge branch 'chore/user-favorites' of https://github.com/makeplane/p…
gakshita Aug 1, 2024
477aeb9
fix: favorite bugs
gakshita Aug 3, 2024
fd12f54
fix: ts bugs
gakshita Aug 3, 2024
e55adca
Merge branch 'preview' of https://github.com/makeplane/plane into fix…
gakshita Aug 3, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
chore: Fav feature
  • Loading branch information
gakshita committed Jul 29, 2024
commit 40776186043c233b1cae93a69888b99904faeda3
14 changes: 14 additions & 0 deletions packages/types/src/favourite/favourite.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export type IFavourite = {
id: string;
name: string;
entity_type: string;
entity_data: {
name: string;
};
is_folder: boolean;
sort_order: number;
parent: string | null;
entity_identifier?: string | null;
children: IFavourite[];
project_id: string | null;
};
1 change: 1 addition & 0 deletions packages/types/src/favourite/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./favourite";
1 change: 1 addition & 0 deletions packages/types/src/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ export * from "./common";
export * from "./pragmatic";
export * from "./publish";
export * from "./workspace-notifications";
export * from "./favourite";
36 changes: 36 additions & 0 deletions packages/ui/src/icons/favourite-folder-icon.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import * as React from "react";

import { ISvgIcons } from "./type";

export const FavouriteFolderIcon: React.FC<ISvgIcons> = ({
className = "text-current",
color = "#a3a3a3",
...rest
}) => (
<svg
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
xmlns="http://www.w3.org/2000/svg"
stroke={color}
className={`${className} stroke-2`}
{...rest}
>
<path
d="M7.33325 13.3334H2.66659C2.31296 13.3334 1.97382 13.1929 1.72378 12.9429C1.47373 12.6928 1.33325 12.3537 1.33325 12.0001V3.3334C1.33325 2.97978 1.47373 2.64064 1.72378 2.39059C1.97382 2.14054 2.31296 2.00006 2.66659 2.00006H5.26659C5.48958 1.99788 5.70955 2.05166 5.90638 2.15648C6.10322 2.2613 6.27061 2.41381 6.39325 2.60006L6.93325 3.40006C7.05466 3.58442 7.21994 3.73574 7.41425 3.84047C7.60857 3.94519 7.82585 4.00003 8.04658 4.00006H13.3333C13.6869 4.00006 14.026 4.14054 14.2761 4.39059C14.5261 4.64064 14.6666 4.97978 14.6666 5.3334V6.3334"
// stroke="#60646C"
stroke-width="1.25"
stroke-linecap="round"
stroke-linejoin="round"
/>
<path
d="M12.1373 8L13.0038 9.75535L14.9414 10.0386L13.5394 11.4041L13.8702 13.3333L12.1373 12.422L10.4044 13.3333L10.7353 11.4041L9.33325 10.0386L11.2709 9.75535L12.1373 8Z"
stroke-width="1.25"
// stroke="#60646C"
stroke-linecap="round"
stroke-linejoin="round"
fill="none"
/>
</svg>
);
1 change: 1 addition & 0 deletions packages/ui/src/icons/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,4 @@ export * from "./info-icon";
export * from "./dropdown-icon";
export * from "./intake";
export * from "./user-activity-icon";
export * from "./favourite-folder-icon";
8 changes: 7 additions & 1 deletion web/app/[workspaceSlug]/(projects)/sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
SidebarWorkspaceMenu,
} from "@/components/workspace";
// helpers
import { SidebarFavouritesMenu } from "@/components/workspace/sidebar/favourites/favourites-menu";
import { cn } from "@/helpers/common.helper";
// hooks
import { useAppTheme } from "@/hooks/store";
Expand Down Expand Up @@ -41,7 +42,6 @@ export const AppSidebar: FC<IAppSidebar> = observer(() => {
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [windowSize]);


return (
<div
className={cn(
Expand Down Expand Up @@ -78,6 +78,12 @@ export const AppSidebar: FC<IAppSidebar> = observer(() => {
"opacity-0": !sidebarCollapsed,
})}
/>
<SidebarFavouritesMenu />
<hr
className={cn("flex-shrink-0 border-custom-sidebar-border-300 h-[0.5px] w-3/5 mx-auto my-1", {
"opacity-0": !sidebarCollapsed,
})}
/>
<SidebarProjectsList />
<SidebarHelpSection />
</div>
Expand Down
262 changes: 262 additions & 0 deletions web/core/components/workspace/sidebar/favourites/favourite-folder.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
"use client";

import { useEffect, useRef, useState } from "react";
import { combine } from "@atlaskit/pragmatic-drag-and-drop/combine";
import { dropTargetForElements } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";

import { useParams } from "next/navigation";
import { PenSquare, Star, MoreHorizontal, ChevronRight } from "lucide-react";
import { Disclosure, Transition } from "@headlessui/react";
// ui
import { IFavourite } from "@plane/types";
import { CustomMenu, Tooltip, DropIndicator, setToast, TOAST_TYPE, FavouriteFolderIcon } from "@plane/ui";

// helpers
import { cn } from "@/helpers/common.helper";
// hooks
import { useAppTheme } from "@/hooks/store";
import { useFavourite } from "@/hooks/store/use-favourite";
import { usePlatformOS } from "@/hooks/use-platform-os";
// constants
import { FavouriteItem } from "./favourite-item";
import { NewFavouriteFolder } from "./new-fav-folder";
type Props = {
isLastChild: boolean;
favourite: IFavourite;
};

export const FavouriteFolder: React.FC<Props> = (props) => {
const { isLastChild, favourite } = props;
// store hooks
const { sidebarCollapsed: isSidebarCollapsed } = useAppTheme();

const { isMobile } = usePlatformOS();
const { deleteFavourite, moveFavourite, getGroupedFavourites } = useFavourite();
const { workspaceSlug } = useParams();
// states
const [isMenuActive, setIsMenuActive] = useState(false);
const [isDragging, setIsDragging] = useState(false);
const [instruction, setInstruction] = useState<"DRAG_OVER" | "DRAG_BELOW" | undefined>(undefined);
const [folderToRename, setFolderToRename] = useState<string | null>(null);
// refs
const actionSectionRef = useRef<HTMLDivElement | null>(null);
const elementRef = useRef<HTMLDivElement | null>(null);

!favourite.children && getGroupedFavourites(workspaceSlug.toString(), favourite.id);

const handleRemoveFromFavorites = () => {
console.log(favourite.id, "handleRemoveFromFavorites");
deleteFavourite(workspaceSlug.toString(), favourite.id)
.then((res) => {
console.log(res, "res");
setToast({
type: TOAST_TYPE.SUCCESS,
title: "Success!",
message: "Favourite removed successfully.",
});
})
.catch((err) => {
Object.keys(err.data).map((key) => {
setToast({
type: TOAST_TYPE.ERROR,
title: "Error!",
message: err.data[key],
});
});
});
};

const handleOnDrop = (source: string, destination: string) => {
moveFavourite(workspaceSlug.toString(), source, {
parent: destination,
})
.then((res) => {
console.log(res, "res");
setToast({
type: TOAST_TYPE.SUCCESS,
title: "Success!",
message: "Favourite moved successfully.",
});
})
.catch((err) => {
console.log(err, "err");
setToast({
type: TOAST_TYPE.ERROR,
title: "Error!",
message: "Failed to move favourite.",
});
});
};

useEffect(() => {
const element = elementRef.current;

if (!element) return;

return combine(
dropTargetForElements({
element,
getData: () => ({ type: "PARENT", id: favourite.id }),
onDragEnter: () => {
setIsDragging(true);
},
onDragLeave: () => {
setIsDragging(false);
},
onDragStart: () => {
setIsDragging(true);
},
onDrop: ({ self, source }) => {
setIsDragging(false);
const sourceId = source?.data?.id as string | undefined;
const destinationId = self?.data?.id as string | undefined;

if (sourceId === destinationId) return;
if (!sourceId || !destinationId) return;

handleOnDrop(sourceId, destinationId);
},
})
);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [elementRef.current, isDragging, favourite.id, handleOnDrop]);

// useOutsideClickDetector(actionSectionRef, () => setIsMenuActive(false));
// useOutsideClickDetector(projectRef, () => projectRef?.current?.classList?.remove(HIGHLIGHT_CLASS));

return folderToRename ? (
<NewFavouriteFolder
setCreateNewFolder={setFolderToRename}
actionType="rename"
defaultName={favourite.name}
favouriteId={favourite.id}
/>
) : (
<>
<Disclosure key={`${favourite.id}`} ref={elementRef} defaultOpen={false}>
{({ open }) => (
<div
// id={`sidebar-${projectId}-${projectListType}`}
className={cn("relative", {
"bg-custom-sidebar-background-80 opacity-60": isDragging,
})}
>
<DropIndicator classNames="absolute top-0" isVisible={instruction === "DRAG_OVER"} />
<div
className={cn(
"group/project-item relative w-full px-2 py-1.5 flex items-center rounded-md text-custom-sidebar-text-100 hover:bg-custom-sidebar-background-90",
{
"bg-custom-sidebar-background-90": isMenuActive,
"p-0 size-8 aspect-square justify-center mx-auto": isSidebarCollapsed,
}
)}
>
{isSidebarCollapsed ? (
<div
className={cn("flex-grow flex items-center gap-1.5 truncate text-left select-none", {
"justify-center": isSidebarCollapsed,
})}
>
<Disclosure.Button as="button" className="size-8 aspect-square flex-shrink-0 grid place-items-center">
<div className="size-4 grid place-items-center flex-shrink-0">
<FavouriteFolderIcon />
</div>
</Disclosure.Button>
</div>
) : (
<>
<Tooltip
tooltipContent={`${favourite.name}`}
position="right"
disabled={!isSidebarCollapsed}
isMobile={isMobile}
>
<div className="flex-grow flex truncate">
<Disclosure.Button
as="button"
type="button"
className={cn("flex-grow flex items-center gap-1.5 text-left select-none w-full", {
"justify-center": isSidebarCollapsed,
})}
>
<div className="size-4 grid place-items-center flex-shrink-0">
<FavouriteFolderIcon />
</div>
<p className="truncate text-sm font-medium text-custom-sidebar-text-200">{favourite.name}</p>
</Disclosure.Button>
</div>
</Tooltip>
<CustomMenu
customButton={
<span
ref={actionSectionRef}
className="grid place-items-center p-0.5 text-custom-sidebar-text-400 hover:bg-custom-sidebar-background-80 rounded"
onClick={() => setIsMenuActive(!isMenuActive)}
>
<MoreHorizontal className="size-4" />
</span>
}
className={cn(
"opacity-0 pointer-events-none flex-shrink-0 group-hover/project-item:opacity-100 group-hover/project-item:pointer-events-auto",
{
"opacity-100 pointer-events-auto": isMenuActive,
}
)}
customButtonClassName="grid place-items-center"
placement="bottom-start"
>
<CustomMenu.MenuItem onClick={handleRemoveFromFavorites}>
<span className="flex items-center justify-start gap-2">
<Star className="h-3.5 w-3.5 fill-yellow-500 stroke-yellow-500" />
<span>Remove from favorites</span>
</span>
</CustomMenu.MenuItem>
<CustomMenu.MenuItem onClick={() => setFolderToRename(favourite.id)}>
<div className="flex items-center justify-start gap-2">
<PenSquare className="h-3.5 w-3.5 stroke-[1.5] text-custom-text-300" />
<span>Rename Folder</span>
</div>
</CustomMenu.MenuItem>
</CustomMenu>
<Disclosure.Button
as="button"
type="button"
className={cn(
"hidden group-hover/project-item:inline-block p-0.5 rounded hover:bg-custom-sidebar-background-80",
{
"inline-block": isMenuActive,
}
)}
>
<ChevronRight
className={cn("size-4 flex-shrink-0 text-custom-sidebar-text-400 transition-transform", {
"rotate-90": open,
})}
/>
</Disclosure.Button>
</>
)}
</div>
{favourite.children && favourite.children.length > 0 && (
<Transition
enter="transition duration-100 ease-out"
enterFrom="transform scale-95 opacity-0"
enterTo="transform scale-100 opacity-100"
leave="transition duration-75 ease-out"
leaveFrom="transform scale-100 opacity-100"
leaveTo="transform scale-95 opacity-0"
>
<Disclosure.Panel as="div" className="flex flex-col gap-0.5 mt-1 px-2">
{favourite.children.map((child) => (
<FavouriteItem key={child.id} favourite={child} />
))}
</Disclosure.Panel>
</Transition>
)}
{isLastChild && <DropIndicator isVisible={instruction === "DRAG_BELOW"} />}
</div>
)}
</Disclosure>
</>
);
};
Loading