[WEB-3422] fix: app sidebar fixes and improvements (#6633)

* chore: app sidebar improvements

* chore: overview icon updated
This commit is contained in:
Anmol Singh Bhatia 2025-02-18 20:49:17 +05:30 committed by GitHub
parent 1478e66dc4
commit 23b0d4339d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
12 changed files with 49 additions and 43 deletions

View file

@ -396,6 +396,7 @@ class WorkspaceUserPreference(BaseModel):
ACTIVE_CYCLES = "active_cycles", "Active Cycles" ACTIVE_CYCLES = "active_cycles", "Active Cycles"
ANALYTICS = "analytics", "Analytics" ANALYTICS = "analytics", "Analytics"
DRAFTS = "drafts", "Drafts" DRAFTS = "drafts", "Drafts"
YOUR_WORK = "your_work", "Your Work"
ARCHIVES = "archives", "Archives" ARCHIVES = "archives", "Archives"
workspace = models.ForeignKey( workspace = models.ForeignKey(

View file

@ -259,6 +259,12 @@ export interface IWorkspaceSidebarNavigationItem {
} }
export const WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS: Record<string, IWorkspaceSidebarNavigationItem> = { export const WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS: Record<string, IWorkspaceSidebarNavigationItem> = {
"your-work": {
key: "your_work",
labelTranslationKey: "your_work",
href: `/profile/`,
access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER],
},
views: { views: {
key: "views", key: "views",
labelTranslationKey: "views", labelTranslationKey: "views",
@ -287,6 +293,7 @@ export const WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS: Record<string, IWorkspa
export const WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS_LINKS: IWorkspaceSidebarNavigationItem[] = [ export const WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS_LINKS: IWorkspaceSidebarNavigationItem[] = [
WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS["views"], WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS["views"],
WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS["analytics"], WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS["analytics"],
WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS["your-work"],
WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS["drafts"], WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS["drafts"],
WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS["archives"], WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS["archives"],
]; ];
@ -298,14 +305,8 @@ export const WORKSPACE_SIDEBAR_STATIC_NAVIGATION_ITEMS: Record<string, IWorkspac
href: `/`, href: `/`,
access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER, EUserWorkspaceRoles.GUEST], access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER, EUserWorkspaceRoles.GUEST],
}, },
"your-work": { inbox: {
key: "your_work", key: "inbox",
labelTranslationKey: "your_work",
href: `/profile/`,
access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER],
},
notifications: {
key: "notifications",
labelTranslationKey: "notification.label", labelTranslationKey: "notification.label",
href: `/notifications/`, href: `/notifications/`,
access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER, EUserWorkspaceRoles.GUEST], access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER, EUserWorkspaceRoles.GUEST],
@ -320,7 +321,6 @@ export const WORKSPACE_SIDEBAR_STATIC_NAVIGATION_ITEMS: Record<string, IWorkspac
export const WORKSPACE_SIDEBAR_STATIC_NAVIGATION_ITEMS_LINKS: IWorkspaceSidebarNavigationItem[] = [ export const WORKSPACE_SIDEBAR_STATIC_NAVIGATION_ITEMS_LINKS: IWorkspaceSidebarNavigationItem[] = [
WORKSPACE_SIDEBAR_STATIC_NAVIGATION_ITEMS["home"], WORKSPACE_SIDEBAR_STATIC_NAVIGATION_ITEMS["home"],
WORKSPACE_SIDEBAR_STATIC_NAVIGATION_ITEMS["your-work"], WORKSPACE_SIDEBAR_STATIC_NAVIGATION_ITEMS["inbox"],
WORKSPACE_SIDEBAR_STATIC_NAVIGATION_ITEMS["notifications"],
WORKSPACE_SIDEBAR_STATIC_NAVIGATION_ITEMS["projects"], WORKSPACE_SIDEBAR_STATIC_NAVIGATION_ITEMS["projects"],
]; ];

View file

@ -3,11 +3,11 @@ import * as React from "react";
import { ISvgIcons } from "./type"; import { ISvgIcons } from "./type";
export const OverviewIcon: React.FC<ISvgIcons> = ({ className = "text-current", ...rest }) => ( export const OverviewIcon: React.FC<ISvgIcons> = ({ className = "text-current", ...rest }) => (
<svg viewBox="0 0 16 16" className={className} xmlns="http://www.w3.org/2000/svg" {...rest}> <svg viewBox="0 0 12 12" className={className} fill="none" xmlns="http://www.w3.org/2000/svg" {...rest}>
<path <path
fillRule="evenodd" fillRule="evenodd"
clipRule="evenodd" clipRule="evenodd"
d="M2.5 3C2.5 2.86739 2.55268 2.74021 2.64645 2.64645C2.74021 2.55268 2.86739 2.5 3 2.5H3.5C9.02267 2.5 13.5 6.97733 13.5 12.5V13C13.5 13.1326 13.4473 13.2598 13.3536 13.3536C13.2598 13.4473 13.1326 13.5 13 13.5H12.5C12.3674 13.5 12.2402 13.4473 12.1464 13.3536C12.0527 13.2598 12 13.1326 12 13V12.5C12 7.80533 8.19467 4 3.5 4H3C2.86739 4 2.74021 3.94732 2.64645 3.85355C2.55268 3.75979 2.5 3.63261 2.5 3.5V3ZM2.5 7.5C2.5 7.36739 2.55268 7.24022 2.64645 7.14645C2.74021 7.05268 2.86739 7 3 7H3.5C4.22227 7 4.93747 7.14226 5.60476 7.41866C6.27205 7.69506 6.87837 8.10019 7.38909 8.61091C7.89981 9.12164 8.30494 9.72795 8.58134 10.3952C8.85774 11.0625 9 11.7777 9 12.5V13C9 13.1326 8.94732 13.2598 8.85355 13.3536C8.75978 13.4473 8.63261 13.5 8.5 13.5H8C7.86739 13.5 7.74022 13.4473 7.64645 13.3536C7.55268 13.2598 7.5 13.1326 7.5 13V12.5C7.5 11.4391 7.07857 10.4217 6.32843 9.67157C5.57828 8.92143 4.56087 8.5 3.5 8.5H3C2.86739 8.5 2.74021 8.44732 2.64645 8.35355C2.55268 8.25978 2.5 8.13261 2.5 8V7.5ZM2.5 12.5C2.5 12.2348 2.60536 11.9804 2.79289 11.7929C2.98043 11.6054 3.23478 11.5 3.5 11.5C3.76522 11.5 4.01957 11.6054 4.20711 11.7929C4.39464 11.9804 4.5 12.2348 4.5 12.5C4.5 12.7652 4.39464 13.0196 4.20711 13.2071C4.01957 13.3946 3.76522 13.5 3.5 13.5C3.23478 13.5 2.98043 13.3946 2.79289 13.2071C2.60536 13.0196 2.5 12.7652 2.5 12.5Z" d="M0.5 1C0.5 0.867392 0.552678 0.740215 0.646447 0.646447C0.740215 0.552678 0.867392 0.5 1 0.5H1.5C7.02267 0.5 11.5 4.97733 11.5 10.5V11C11.5 11.1326 11.4473 11.2598 11.3536 11.3536C11.2598 11.4473 11.1326 11.5 11 11.5H10.5C10.3674 11.5 10.2402 11.4473 10.1464 11.3536C10.0527 11.2598 10 11.1326 10 11V10.5C10 5.80533 6.19467 2 1.5 2H1C0.867392 2 0.740215 1.94732 0.646447 1.85355C0.552678 1.75979 0.5 1.63261 0.5 1.5V1ZM0.5 5.5C0.5 5.36739 0.552678 5.24022 0.646447 5.14645C0.740215 5.05268 0.867392 5 1 5H1.5C2.22227 5 2.93747 5.14226 3.60476 5.41866C4.27205 5.69506 4.87837 6.10019 5.38909 6.61091C5.89981 7.12164 6.30494 7.72795 6.58134 8.39524C6.85774 9.06253 7 9.77773 7 10.5V11C7 11.1326 6.94732 11.2598 6.85355 11.3536C6.75978 11.4473 6.63261 11.5 6.5 11.5H6C5.86739 11.5 5.74022 11.4473 5.64645 11.3536C5.55268 11.2598 5.5 11.1326 5.5 11V10.5C5.5 9.43913 5.07857 8.42172 4.32843 7.67157C3.57828 6.92143 2.56087 6.5 1.5 6.5H1C0.867392 6.5 0.740215 6.44732 0.646447 6.35355C0.552678 6.25978 0.5 6.13261 0.5 6V5.5ZM0.5 10.5C0.5 10.2348 0.605357 9.98043 0.792893 9.79289C0.98043 9.60536 1.23478 9.5 1.5 9.5C1.76522 9.5 2.01957 9.60536 2.20711 9.79289C2.39464 9.98043 2.5 10.2348 2.5 10.5C2.5 10.7652 2.39464 11.0196 2.20711 11.2071C2.01957 11.3946 1.76522 11.5 1.5 11.5C1.23478 11.5 0.98043 11.3946 0.792893 11.2071C0.605357 11.0196 0.5 10.7652 0.5 10.5Z"
fill="currentColor" fill="currentColor"
/> />
</svg> </svg>

View file

@ -14,6 +14,7 @@ import { CreateProjectModal } from "@/components/project";
import { SidebarProjectsListItem } from "@/components/workspace"; import { SidebarProjectsListItem } from "@/components/workspace";
// hooks // hooks
import { orderJoinedProjects } from "@/helpers/project.helper"; import { orderJoinedProjects } from "@/helpers/project.helper";
import { copyUrlToClipboard } from "@/helpers/string.helper";
import { useAppTheme, useProject, useUserPermissions } from "@/hooks/store"; import { useAppTheme, useProject, useUserPermissions } from "@/hooks/store";
import useExtendedSidebarOutsideClickDetector from "@/hooks/use-extended-sidebar-overview-outside-click"; import useExtendedSidebarOutsideClickDetector from "@/hooks/use-extended-sidebar-overview-outside-click";
import { TProject } from "@/plane-web/types"; import { TProject } from "@/plane-web/types";
@ -85,6 +86,15 @@ export const ExtendedProjectSidebar = observer(() => {
"extended-project-sidebar-toggle" "extended-project-sidebar-toggle"
); );
const handleCopyText = (projectId: string) => {
copyUrlToClipboard(`${workspaceSlug}/projects/${projectId}/issues`).then(() => {
setToast({
type: TOAST_TYPE.SUCCESS,
title: t("link_copied"),
message: t("project_link_copied_to_clipboard"),
});
});
};
return ( return (
<> <>
{workspaceSlug && ( {workspaceSlug && (
@ -140,7 +150,7 @@ export const ExtendedProjectSidebar = observer(() => {
<SidebarProjectsListItem <SidebarProjectsListItem
key={projectId} key={projectId}
projectId={projectId} projectId={projectId}
handleCopyText={() => {}} handleCopyText={() => handleCopyText(projectId)}
projectListType={"JOINED"} projectListType={"JOINED"}
disableDrag={false} disableDrag={false}
disableDrop={false} disableDrop={false}

View file

@ -104,7 +104,7 @@ export const ExtendedAppSidebar = observer(() => {
<div <div
ref={extendedSidebarRef} ref={extendedSidebarRef}
className={cn( className={cn(
"fixed top-0 h-full z-[19] flex flex-col gap-0.5 w-[300px] transform transition-all duration-300 ease-in-out bg-custom-sidebar-background-100 border-r border-custom-sidebar-border-200 p-4 shadow-md", "fixed top-0 h-full z-[19] flex flex-col gap-0.5 w-[300px] transform transition-all duration-300 ease-in-out bg-custom-sidebar-background-100 border-r border-custom-sidebar-border-200 p-4 shadow-md pb-6",
{ {
"translate-x-0 opacity-100": extendedSidebarCollapsed, "translate-x-0 opacity-100": extendedSidebarCollapsed,
"-translate-x-full opacity-0": !extendedSidebarCollapsed, "-translate-x-full opacity-0": !extendedSidebarCollapsed,

View file

@ -90,11 +90,9 @@ export const AppSidebar: FC = observer(() => {
})} })}
> >
<SidebarMenuItems /> <SidebarMenuItems />
<hr {sidebarCollapsed && (
className={cn("flex-shrink-0 border-custom-sidebar-border-300 h-[0.5px] w-3/5 mx-auto my-1", { <hr className="flex-shrink-0 border-custom-sidebar-border-300 h-[0.5px] w-3/5 mx-auto my-1" />
"opacity-0": !sidebarCollapsed, )}
})}
/>
{/* Favorites Menu */} {/* Favorites Menu */}
{canPerformWorkspaceMemberActions && !isFavoriteEmpty && <SidebarFavoritesMenu />} {canPerformWorkspaceMemberActions && !isFavoriteEmpty && <SidebarFavoritesMenu />}
{/* Teams List */} {/* Teams List */}

View file

@ -169,7 +169,7 @@ export const ExtendedSidebarItem: FC<TExtendedSidebarItemProps> = observer((prop
<button <button
type="button" type="button"
className={cn( className={cn(
"hidden group-hover/project-item:flex items-center justify-center absolute top-1/2 -left-3 -translate-y-1/2 rounded text-custom-sidebar-text-400 cursor-grab", "flex items-center justify-center absolute top-1/2 -left-3 -translate-y-1/2 rounded text-custom-sidebar-text-400 cursor-grab",
{ {
// "cursor-not-allowed opacity-60": project.sort_order === null, // "cursor-not-allowed opacity-60": project.sort_order === null,
"cursor-grabbing": isDragging, "cursor-grabbing": isDragging,
@ -199,14 +199,14 @@ export const ExtendedSidebarItem: FC<TExtendedSidebarItemProps> = observer((prop
{isPinned ? ( {isPinned ? (
<Tooltip tooltipContent="Hide tab"> <Tooltip tooltipContent="Hide tab">
<Eye <Eye
className="size-4 flex-shrink-0 invisible group-hover:visible text-custom-text-300 outline-none" className="size-4 flex-shrink-0 hover:text-custom-text-200 text-custom-text-300 outline-none"
onClick={() => unPinNavigationItem(workspaceSlug.toString(), item.key)} onClick={() => unPinNavigationItem(workspaceSlug.toString(), item.key)}
/> />
</Tooltip> </Tooltip>
) : ( ) : (
<Tooltip tooltipContent="Show tab"> <Tooltip tooltipContent="Show tab">
<EyeClosed <EyeClosed
className="size-4 flex-shrink-0 invisible group-hover:visible text-custom-text-400 outline-none" className="size-4 flex-shrink-0 hover:text-custom-text-200 text-custom-text-400 outline-none"
onClick={() => pinNavigationItem(workspaceSlug.toString(), item.key)} onClick={() => pinNavigationItem(workspaceSlug.toString(), item.key)}
/> />
</Tooltip> </Tooltip>

View file

@ -6,7 +6,7 @@ export const getSidebarNavigationItemIcon = (key: string, className: string = ""
switch (key) { switch (key) {
case "home": case "home":
return <Home className={cn("size-4 flex-shrink-0", className)} />; return <Home className={cn("size-4 flex-shrink-0", className)} />;
case "notifications": case "inbox":
return <Inbox className={cn("size-4 flex-shrink-0", className)} />; return <Inbox className={cn("size-4 flex-shrink-0", className)} />;
case "projects": case "projects":
return <Briefcase className={cn("size-4 flex-shrink-0", className)} />; return <Briefcase className={cn("size-4 flex-shrink-0", className)} />;

View file

@ -42,7 +42,7 @@ export const SidebarItem: FC<TSidebarItemProps> = observer((props) => {
if (extendedSidebarCollapsed) toggleExtendedSidebar(); if (extendedSidebarCollapsed) toggleExtendedSidebar();
}; };
const staticItems = ["home", "your-work", "notifications", "pi-chat", "projects"]; const staticItems = ["home", "inbox", "pi-chat", "projects"];
if (!allowPermissions(item.access as any, EUserPermissionsLevel.WORKSPACE, workspaceSlug.toString())) { if (!allowPermissions(item.access as any, EUserPermissionsLevel.WORKSPACE, workspaceSlug.toString())) {
return null; return null;
@ -74,7 +74,6 @@ export const SidebarItem: FC<TSidebarItemProps> = observer((props) => {
className={`${sidebarCollapsed ? "p-0 size-8 aspect-square justify-center mx-auto" : ""}`} className={`${sidebarCollapsed ? "p-0 size-8 aspect-square justify-center mx-auto" : ""}`}
isActive={isActive} isActive={isActive}
> >
{/* <icon className="size-4" /> */}
<div className="flex items-center gap-1.5 py-[1px]"> <div className="flex items-center gap-1.5 py-[1px]">
{icon} {icon}
{!sidebarCollapsed && <p className="text-sm leading-5 font-medium">{t(item.labelTranslationKey)}</p>} {!sidebarCollapsed && <p className="text-sm leading-5 font-medium">{t(item.labelTranslationKey)}</p>}

View file

@ -285,11 +285,9 @@ export const SidebarFavoritesMenu = observer(() => {
</Transition> </Transition>
</Disclosure> </Disclosure>
<hr {sidebarCollapsed && favoriteIds.length > 0 && (
className={cn("flex-shrink-0 border-custom-sidebar-border-300 h-[0.5px] w-3/5 mx-auto my-1", { <hr className="flex-shrink-0 border-custom-sidebar-border-300 h-[0.5px] w-3/5 mx-auto my-1" />
"opacity-0": !sidebarCollapsed || favoriteIds.length === 0, )}
})}
/>
</> </>
); );
}); });

View file

@ -238,7 +238,7 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
{ {
"cursor-not-allowed opacity-60": project.sort_order === null, "cursor-not-allowed opacity-60": project.sort_order === null,
"cursor-grabbing": isDragging, "cursor-grabbing": isDragging,
flex: isMenuActive, flex: isMenuActive || renderInExtendedSidebar,
"!hidden": isSidebarCollapsed, "!hidden": isSidebarCollapsed,
} }
)} )}

View file

@ -51,7 +51,6 @@ export const SidebarMenuItems = observer(() => {
{sortedNavigationItems.map((item, _index) => ( {sortedNavigationItems.map((item, _index) => (
<SidebarItem key={`dynamic_${_index}`} item={item} /> <SidebarItem key={`dynamic_${_index}`} item={item} />
))} ))}
</div>
<SidebarNavItem className={`${sidebarCollapsed ? "p-0 size-8 aspect-square justify-center mx-auto" : ""}`}> <SidebarNavItem className={`${sidebarCollapsed ? "p-0 size-8 aspect-square justify-center mx-auto" : ""}`}>
<button <button
onClick={() => toggleExtendedSidebar()} onClick={() => toggleExtendedSidebar()}
@ -64,6 +63,7 @@ export const SidebarMenuItems = observer(() => {
{!sidebarCollapsed && <span>More</span>} {!sidebarCollapsed && <span>More</span>}
</button> </button>
</SidebarNavItem> </SidebarNavItem>
</div>
</> </>
); );
}); });