mirror of
https://github.com/makeplane/plane
synced 2025-08-07 19:59:33 +00:00
refactor: favorites sidebar implementation (#6716)
* chore: code separation for favorites * chore: error handling
This commit is contained in:
committed by
GitHub
parent
40c0bbcfb4
commit
cb344ea1f5
@@ -2,3 +2,4 @@ export * from "./ai";
|
||||
export * from "./estimates";
|
||||
export * from "./gantt-chart";
|
||||
export * from "./project";
|
||||
export * from "./sidebar-favorites";
|
||||
|
||||
41
web/ce/constants/sidebar-favorites.ts
Normal file
41
web/ce/constants/sidebar-favorites.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { Briefcase, ContrastIcon, FileText, Layers, LucideIcon } from "lucide-react";
|
||||
// plane imports
|
||||
import { IFavorite } from "@plane/types";
|
||||
import { DiceIcon, FavoriteFolderIcon, ISvgIcons } from "@plane/ui";
|
||||
|
||||
export const FAVORITE_ITEM_ICONS: Record<string, React.FC<ISvgIcons> | LucideIcon> = {
|
||||
page: FileText,
|
||||
project: Briefcase,
|
||||
view: Layers,
|
||||
module: DiceIcon,
|
||||
cycle: ContrastIcon,
|
||||
folder: FavoriteFolderIcon,
|
||||
};
|
||||
|
||||
export const FAVORITE_ITEM_LINKS: {
|
||||
[key: string]: {
|
||||
itemLevel: "project" | "workspace";
|
||||
getLink: (favorite: IFavorite) => string;
|
||||
};
|
||||
} = {
|
||||
project: {
|
||||
itemLevel: "project",
|
||||
getLink: () => `issues`,
|
||||
},
|
||||
cycle: {
|
||||
itemLevel: "project",
|
||||
getLink: (favorite) => `cycles/${favorite.entity_identifier}`,
|
||||
},
|
||||
module: {
|
||||
itemLevel: "project",
|
||||
getLink: (favorite) => `modules/${favorite.entity_identifier}`,
|
||||
},
|
||||
view: {
|
||||
itemLevel: "project",
|
||||
getLink: (favorite) => `views/${favorite.entity_identifier}`,
|
||||
},
|
||||
page: {
|
||||
itemLevel: "project",
|
||||
getLink: (favorite) => `pages/${favorite.entity_identifier}`,
|
||||
},
|
||||
};
|
||||
26
web/ce/hooks/use-additional-favorite-item-details.ts
Normal file
26
web/ce/hooks/use-additional-favorite-item-details.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
// plane imports
|
||||
import { IFavorite } from "@plane/types";
|
||||
// components
|
||||
import { getFavoriteItemIcon } from "@/components/workspace/sidebar/favorites/favorite-items/common";
|
||||
|
||||
export const useAdditionalFavoriteItemDetails = () => {
|
||||
const getAdditionalFavoriteItemDetails = (_workspaceSlug: string, favorite: IFavorite) => {
|
||||
const { entity_type: favoriteItemEntityType } = favorite;
|
||||
const favoriteItemName = favorite?.entity_data?.name || favorite?.name;
|
||||
|
||||
let itemIcon;
|
||||
let itemTitle;
|
||||
|
||||
switch (favoriteItemEntityType) {
|
||||
default:
|
||||
itemTitle = favoriteItemName;
|
||||
itemIcon = getFavoriteItemIcon(favoriteItemEntityType);
|
||||
break;
|
||||
}
|
||||
return { itemIcon, itemTitle };
|
||||
};
|
||||
|
||||
return {
|
||||
getAdditionalFavoriteItemDetails,
|
||||
};
|
||||
};
|
||||
@@ -1,49 +1,45 @@
|
||||
"use client";
|
||||
// lucide
|
||||
import { Briefcase, FileText, Layers } from "lucide-react";
|
||||
// types
|
||||
|
||||
import { FileText } from "lucide-react";
|
||||
// plane imports
|
||||
import { IFavorite, TLogoProps } from "@plane/types";
|
||||
// ui
|
||||
import { ContrastIcon, DiceIcon, FavoriteFolderIcon } from "@plane/ui";
|
||||
// components
|
||||
import { Logo } from "@/components/common";
|
||||
// plane web constants
|
||||
import { FAVORITE_ITEM_ICONS, FAVORITE_ITEM_LINKS } from "@/plane-web/constants";
|
||||
|
||||
const iconClassName = `flex-shrink-0 size-4 stroke-[1.5] m-auto`;
|
||||
export const getFavoriteItemIcon = (type: string, logo?: TLogoProps | undefined) => {
|
||||
const Icon = FAVORITE_ITEM_ICONS[type] || FileText;
|
||||
|
||||
export const FAVORITE_ITEM_ICON: Record<string, JSX.Element> = {
|
||||
page: <FileText className={iconClassName} />,
|
||||
project: <Briefcase className={iconClassName} />,
|
||||
view: <Layers className={iconClassName} />,
|
||||
module: <DiceIcon className={iconClassName} />,
|
||||
cycle: <ContrastIcon className={iconClassName} />,
|
||||
folder: <FavoriteFolderIcon className={iconClassName} />,
|
||||
};
|
||||
|
||||
export const getFavoriteItemIcon = (type: string, logo?: TLogoProps | undefined) => (
|
||||
<>
|
||||
<div className="hidden group-hover:flex items-center justify-center size-5">
|
||||
{FAVORITE_ITEM_ICON[type] || <FileText className={iconClassName} />}
|
||||
</div>
|
||||
<div className="flex items-center justify-center size-5 group-hover:hidden">
|
||||
{logo?.in_use ? (
|
||||
<Logo logo={logo} size={16} type={type === "project" ? "material" : "lucide"} />
|
||||
) : (
|
||||
FAVORITE_ITEM_ICON[type] || <FileText className={iconClassName} />
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
const entityPaths: Record<string, string> = {
|
||||
project: "issues",
|
||||
cycle: "cycles",
|
||||
module: "modules",
|
||||
view: "views",
|
||||
page: "pages",
|
||||
return (
|
||||
<>
|
||||
<div className="hidden group-hover:flex items-center justify-center size-5">
|
||||
<Icon className="flex-shrink-0 size-4 stroke-[1.5] m-auto" />
|
||||
</div>
|
||||
<div className="flex items-center justify-center size-5 group-hover:hidden">
|
||||
{logo?.in_use ? (
|
||||
<Logo logo={logo} size={16} type={type === "project" ? "material" : "lucide"} />
|
||||
) : (
|
||||
<Icon className="flex-shrink-0 size-4 stroke-[1.5] m-auto" />
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const generateFavoriteItemLink = (workspaceSlug: string, favorite: IFavorite) => {
|
||||
const entityPath = entityPaths[favorite.entity_type];
|
||||
return entityPath
|
||||
? `/${workspaceSlug}/projects/${favorite.project_id}/${entityPath}/${entityPath === "issues" ? "" : favorite.entity_identifier || ""}`
|
||||
: `/${workspaceSlug}`;
|
||||
const entityLinkDetails = FAVORITE_ITEM_LINKS[favorite.entity_type];
|
||||
|
||||
if (!entityLinkDetails) {
|
||||
console.error(`Unrecognized favorite entity type: ${favorite.entity_type}`);
|
||||
return `/${workspaceSlug}`;
|
||||
}
|
||||
|
||||
if (entityLinkDetails.itemLevel === "workspace") {
|
||||
return `/${workspaceSlug}/${entityLinkDetails.getLink(favorite)}`;
|
||||
} else if (entityLinkDetails.itemLevel === "project") {
|
||||
return `/${workspaceSlug}/projects/${favorite.project_id}/${entityLinkDetails.getLink(favorite)}`;
|
||||
} else {
|
||||
return `/${workspaceSlug}`;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
// plane types
|
||||
// plane imports
|
||||
import { IFavorite } from "@plane/types";
|
||||
// components
|
||||
import {
|
||||
@@ -11,19 +11,22 @@ import { getPageName } from "@/helpers/page.helper";
|
||||
import { useProject, useProjectView, useCycle, useModule } from "@/hooks/store";
|
||||
// plane web hooks
|
||||
import { EPageStoreType, usePage } from "@/plane-web/hooks/store";
|
||||
import { useAdditionalFavoriteItemDetails } from "@/plane-web/hooks/use-additional-favorite-item-details";
|
||||
|
||||
export const useFavoriteItemDetails = (workspaceSlug: string, favorite: IFavorite) => {
|
||||
const favoriteItemId = favorite?.entity_identifier;
|
||||
const favoriteItemLogoProps = favorite?.entity_data?.logo_props;
|
||||
const {
|
||||
entity_identifier: favoriteItemId,
|
||||
entity_data: { logo_props: favoriteItemLogoProps },
|
||||
entity_type: favoriteItemEntityType,
|
||||
} = favorite;
|
||||
const favoriteItemName = favorite?.entity_data?.name || favorite?.name;
|
||||
const favoriteItemEntityType = favorite?.entity_type;
|
||||
|
||||
// store hooks
|
||||
const { getViewById } = useProjectView();
|
||||
const { getProjectById } = useProject();
|
||||
const { getCycleById } = useCycle();
|
||||
const { getModuleById } = useModule();
|
||||
|
||||
// additional details
|
||||
const { getAdditionalFavoriteItemDetails } = useAdditionalFavoriteItemDetails();
|
||||
// derived values
|
||||
const pageDetail = usePage({
|
||||
pageId: favoriteItemId ?? "",
|
||||
@@ -32,7 +35,6 @@ export const useFavoriteItemDetails = (workspaceSlug: string, favorite: IFavorit
|
||||
const viewDetails = getViewById(favoriteItemId ?? "");
|
||||
const cycleDetail = getCycleById(favoriteItemId ?? "");
|
||||
const moduleDetail = getModuleById(favoriteItemId ?? "");
|
||||
|
||||
const currentProjectDetails = getProjectById(favorite.project_id ?? "");
|
||||
|
||||
let itemIcon;
|
||||
@@ -60,10 +62,12 @@ export const useFavoriteItemDetails = (workspaceSlug: string, favorite: IFavorit
|
||||
itemTitle = moduleDetail?.name || favoriteItemName;
|
||||
itemIcon = getFavoriteItemIcon("module");
|
||||
break;
|
||||
default:
|
||||
itemTitle = favoriteItemName;
|
||||
itemIcon = getFavoriteItemIcon(favoriteItemEntityType);
|
||||
default: {
|
||||
const additionalDetails = getAdditionalFavoriteItemDetails(workspaceSlug, favorite);
|
||||
itemTitle = additionalDetails.itemTitle;
|
||||
itemIcon = additionalDetails.itemIcon;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return { itemIcon, itemTitle, itemLink };
|
||||
|
||||
1
web/ee/constants/index.ts
Normal file
1
web/ee/constants/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "./sidebar-favorites";
|
||||
1
web/ee/constants/sidebar-favorites.ts
Normal file
1
web/ee/constants/sidebar-favorites.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "ce/constants/sidebar-favorites";
|
||||
1
web/ee/hooks/use-additional-favorite-item-details.ts
Normal file
1
web/ee/hooks/use-additional-favorite-item-details.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from "ce/hooks/use-additional-favorite-item-details";
|
||||
Reference in New Issue
Block a user