mirror of
https://github.com/makeplane/plane.git
synced 2025-12-23 15:19:37 +01:00
[WEB-1920] dev: app sidebar revamp (#5150)
* chore: user activity icon added * dev: sidebar navigation component added * chore: dashboard constant file updated * chore: unread notification indicator position * chore: app sidebar project section * chore: app sidebar User and Workspace section updated * chore: notification to inbox transition * chore: code refactor * chore: code refactor
This commit is contained in:
committed by
GitHub
parent
996192b9bf
commit
a7ecfade98
@@ -23,3 +23,4 @@ export * from "./transfer-icon";
|
|||||||
export * from "./info-icon";
|
export * from "./info-icon";
|
||||||
export * from "./dropdown-icon";
|
export * from "./dropdown-icon";
|
||||||
export * from "./intake";
|
export * from "./intake";
|
||||||
|
export * from "./user-activity-icon";
|
||||||
|
|||||||
21
packages/ui/src/icons/user-activity-icon.tsx
Normal file
21
packages/ui/src/icons/user-activity-icon.tsx
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { ISvgIcons } from "./type";
|
||||||
|
|
||||||
|
export const UserActivityIcon: React.FC<ISvgIcons> = ({ className = "text-current", ...rest }) => (
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
className={`${className} stroke-2`}
|
||||||
|
stroke="currentColor"
|
||||||
|
fill="none"
|
||||||
|
strokeWidth="1.5"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
{...rest}
|
||||||
|
>
|
||||||
|
<path d="M10.5 13C12.9853 13 15 10.9853 15 8.5C15 6.01472 12.9853 4 10.5 4C8.01472 4 6 6.01472 6 8.5C6 10.9853 8.01472 13 10.5 13Z" />
|
||||||
|
<path d="M13.9062 13.5903C12.8368 13.1001 11.661 12.8876 10.4877 12.9725C9.31437 13.0574 8.18144 13.437 7.19379 14.0761C6.20613 14.7152 5.39567 15.5931 4.83744 16.6286C4.2792 17.6641 3.99124 18.8237 4.0002 20" />
|
||||||
|
<path d="M21 16.5H19.6L18.2 20L16.8 13L15.4 16.5H14" />
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
1
web/core/components/sidebar/index.ts
Normal file
1
web/core/components/sidebar/index.ts
Normal file
@@ -0,0 +1 @@
|
|||||||
|
export * from "./sidebar-navigation";
|
||||||
29
web/core/components/sidebar/sidebar-navigation.tsx
Normal file
29
web/core/components/sidebar/sidebar-navigation.tsx
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
"use client";
|
||||||
|
import React, { FC } from "react";
|
||||||
|
// helpers
|
||||||
|
import { cn } from "@/helpers/common.helper";
|
||||||
|
|
||||||
|
type TSidebarNavItem = {
|
||||||
|
className?: string;
|
||||||
|
isActive?: boolean;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SidebarNavItem: FC<TSidebarNavItem> = (props) => {
|
||||||
|
const { className, isActive, children } = props;
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
className={cn(
|
||||||
|
"cursor-pointer relative group w-full flex items-center justify-between gap-1.5 rounded px-2 py-1 outline-none",
|
||||||
|
{
|
||||||
|
"text-custom-primary-200 bg-custom-primary-100/10": isActive,
|
||||||
|
"text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-90 active:bg-custom-sidebar-background-90":
|
||||||
|
!isActive,
|
||||||
|
},
|
||||||
|
className
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
@@ -34,7 +34,7 @@ export const NotificationAppSidebarOption: FC<TNotificationAppSidebarOption> = o
|
|||||||
if (totalNotifications <= 0) return <></>;
|
if (totalNotifications <= 0) return <></>;
|
||||||
|
|
||||||
if (isSidebarCollapsed)
|
if (isSidebarCollapsed)
|
||||||
return <div className="absolute right-3.5 top-2 h-2 w-2 rounded-full bg-custom-primary-300" />;
|
return <div className="absolute right-2 top-1.5 h-2 w-2 rounded-full bg-custom-primary-300" />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="ml-auto">
|
<div className="ml-auto">
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export const NotificationFilter: FC = observer(() => {
|
|||||||
<PopoverMenu
|
<PopoverMenu
|
||||||
data={FILTER_TYPE_OPTIONS}
|
data={FILTER_TYPE_OPTIONS}
|
||||||
button={
|
button={
|
||||||
<Tooltip tooltipContent="Notification Filters" isMobile={isMobile} position="bottom">
|
<Tooltip tooltipContent="Inbox Filters" isMobile={isMobile} position="bottom">
|
||||||
<div className="flex-shrink-0 w-5 h-5 flex justify-center items-center overflow-hidden cursor-pointer transition-all hover:bg-custom-background-80 rounded-sm outline-none">
|
<div className="flex-shrink-0 w-5 h-5 flex justify-center items-center overflow-hidden cursor-pointer transition-all hover:bg-custom-background-80 rounded-sm outline-none">
|
||||||
<ListFilter className="h-3 w-3" />
|
<ListFilter className="h-3 w-3" />
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
import { FC } from "react";
|
import { FC } from "react";
|
||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import { Bell } from "lucide-react";
|
import { Inbox } from "lucide-react";
|
||||||
import { Breadcrumbs } from "@plane/ui";
|
import { Breadcrumbs } from "@plane/ui";
|
||||||
// components
|
// components
|
||||||
import { BreadcrumbLink } from "@/components/common";
|
import { BreadcrumbLink } from "@/components/common";
|
||||||
@@ -27,11 +27,7 @@ export const NotificationSidebarHeader: FC<TNotificationSidebarHeader> = observe
|
|||||||
<Breadcrumbs.BreadcrumbItem
|
<Breadcrumbs.BreadcrumbItem
|
||||||
type="text"
|
type="text"
|
||||||
link={
|
link={
|
||||||
<BreadcrumbLink
|
<BreadcrumbLink label="Inbox" icon={<Inbox className="h-4 w-4 text-custom-text-300" />} disableTooltip />
|
||||||
label="Notifications"
|
|
||||||
icon={<Bell className="h-4 w-4 text-custom-text-300" />}
|
|
||||||
disableTooltip
|
|
||||||
/>
|
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
</Breadcrumbs>
|
</Breadcrumbs>
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import Link from "next/link";
|
|||||||
import { useParams } from "next/navigation";
|
import { useParams } from "next/navigation";
|
||||||
import { usePopper } from "react-popper";
|
import { usePopper } from "react-popper";
|
||||||
// icons
|
// icons
|
||||||
import { Activity, Check, ChevronDown, LogOut, Mails, PlusSquare, Settings } from "lucide-react";
|
import { Check, ChevronDown, LogOut, Mails, PlusSquare, Settings } from "lucide-react";
|
||||||
// ui
|
// ui
|
||||||
import { Menu, Transition } from "@headlessui/react";
|
import { Menu, Transition } from "@headlessui/react";
|
||||||
// types
|
// types
|
||||||
@@ -34,19 +34,6 @@ const userLinks = (workspaceSlug: string) => [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const profileLinks = (workspaceSlug: string, userId: string) => [
|
|
||||||
{
|
|
||||||
name: "My activity",
|
|
||||||
icon: Activity,
|
|
||||||
link: `/${workspaceSlug}/profile/${userId}`,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "Settings",
|
|
||||||
icon: Settings,
|
|
||||||
link: "/profile",
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
export const SidebarDropdown = observer(() => {
|
export const SidebarDropdown = observer(() => {
|
||||||
// router params
|
// router params
|
||||||
const { workspaceSlug } = useParams();
|
const { workspaceSlug } = useParams();
|
||||||
@@ -285,22 +272,14 @@ export const SidebarDropdown = observer(() => {
|
|||||||
>
|
>
|
||||||
<div className="flex flex-col gap-2.5 pb-2">
|
<div className="flex flex-col gap-2.5 pb-2">
|
||||||
<span className="px-2 text-custom-sidebar-text-200">{currentUser?.email}</span>
|
<span className="px-2 text-custom-sidebar-text-200">{currentUser?.email}</span>
|
||||||
{profileLinks(workspaceSlug?.toString() ?? "", currentUser?.id ?? "").map((link, index) => (
|
<Link href="/profile">
|
||||||
<Link
|
<Menu.Item as="div">
|
||||||
key={index}
|
|
||||||
href={link.link}
|
|
||||||
onClick={() => {
|
|
||||||
if (index == 0) handleItemClick();
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Menu.Item key={index} as="div">
|
|
||||||
<span className="flex w-full items-center gap-2 rounded px-2 py-1 hover:bg-custom-sidebar-background-80">
|
<span className="flex w-full items-center gap-2 rounded px-2 py-1 hover:bg-custom-sidebar-background-80">
|
||||||
<link.icon className="h-4 w-4 stroke-[1.5]" />
|
<Settings className="h-4 w-4 stroke-[1.5]" />
|
||||||
{link.name}
|
<span>Settings</span>
|
||||||
</span>
|
</span>
|
||||||
</Menu.Item>
|
</Menu.Item>
|
||||||
</Link>
|
</Link>
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
<div className={`pt-2 ${isUserInstanceAdmin || false ? "pb-2" : ""}`}>
|
<div className={`pt-2 ${isUserInstanceAdmin || false ? "pb-2" : ""}`}>
|
||||||
<Menu.Item
|
<Menu.Item
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ import {
|
|||||||
// components
|
// components
|
||||||
import { Logo } from "@/components/common";
|
import { Logo } from "@/components/common";
|
||||||
import { LeaveProjectModal, PublishProjectModal } from "@/components/project";
|
import { LeaveProjectModal, PublishProjectModal } from "@/components/project";
|
||||||
|
import { SidebarNavItem } from "@/components/sidebar";
|
||||||
// constants
|
// constants
|
||||||
import { EUserProjectRoles } from "@/constants/project";
|
import { EUserProjectRoles } from "@/constants/project";
|
||||||
// helpers
|
// helpers
|
||||||
@@ -95,11 +96,6 @@ const navigation = (workspaceSlug: string, projectId: string) => [
|
|||||||
href: `/${workspaceSlug}/projects/${projectId}/inbox`,
|
href: `/${workspaceSlug}/projects/${projectId}/inbox`,
|
||||||
Icon: Intake,
|
Icon: Intake,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: "Settings",
|
|
||||||
href: `/${workspaceSlug}/projects/${projectId}/settings`,
|
|
||||||
Icon: Settings,
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
|
export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
|
||||||
@@ -467,7 +463,7 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
|
|||||||
leaveFrom="transform scale-100 opacity-100"
|
leaveFrom="transform scale-100 opacity-100"
|
||||||
leaveTo="transform scale-95 opacity-0"
|
leaveTo="transform scale-95 opacity-0"
|
||||||
>
|
>
|
||||||
<Disclosure.Panel as="div" className="mt-1 space-y-1">
|
<Disclosure.Panel as="div" className="flex flex-col gap-0.5 mt-1">
|
||||||
{navigation(workspaceSlug?.toString(), project?.id).map((item) => {
|
{navigation(workspaceSlug?.toString(), project?.id).map((item) => {
|
||||||
if (
|
if (
|
||||||
(item.name === "Cycles" && !project.cycle_view) ||
|
(item.name === "Cycles" && !project.cycle_view) ||
|
||||||
@@ -479,31 +475,29 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Link key={item.name} href={item.href} onClick={handleProjectClick}>
|
|
||||||
<Tooltip
|
<Tooltip
|
||||||
|
key={item.name}
|
||||||
isMobile={isMobile}
|
isMobile={isMobile}
|
||||||
tooltipContent={`${project?.name}: ${item.name}`}
|
tooltipContent={`${project?.name}: ${item.name}`}
|
||||||
position="right"
|
position="right"
|
||||||
className="ml-2"
|
className="ml-2"
|
||||||
disabled={!isSidebarCollapsed}
|
disabled={!isSidebarCollapsed}
|
||||||
>
|
>
|
||||||
<div
|
<Link key={item.name} href={item.href} onClick={handleProjectClick}>
|
||||||
className={cn(
|
<SidebarNavItem
|
||||||
"flex items-center gap-1.5 rounded-md pl-[18px] pr-2 py-1.5 outline-none text-custom-sidebar-text-300 hover:bg-custom-sidebar-background-90 focus:bg-custom-sidebar-background-90",
|
key={item.name}
|
||||||
{
|
className={`pl-[18px] ${isSidebarCollapsed ? "p-0 size-7 justify-center mx-auto" : ""}`}
|
||||||
"text-custom-primary-100 bg-custom-primary-100/10 hover:bg-custom-primary-100/10":
|
isActive={pathname.includes(item.href)}
|
||||||
pathname.includes(item.href),
|
|
||||||
"p-0 size-7 justify-center mx-auto": isSidebarCollapsed,
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
|
<div className="flex items-center gap-1.5 py-[1px]">
|
||||||
<item.Icon
|
<item.Icon
|
||||||
className={`flex-shrink-0 size-4 ${item.name === "Intake" ? "stroke-1" : "stroke-[1.5]"}`}
|
className={`flex-shrink-0 size-4 ${item.name === "Intake" ? "stroke-1" : "stroke-[1.5]"}`}
|
||||||
/>
|
/>
|
||||||
{!isSidebarCollapsed && <span className="text-xs font-medium">{item.name}</span>}
|
{!isSidebarCollapsed && <span className="text-xs font-medium">{item.name}</span>}
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</SidebarNavItem>
|
||||||
</Link>
|
</Link>
|
||||||
|
</Tooltip>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</Disclosure.Panel>
|
</Disclosure.Panel>
|
||||||
|
|||||||
@@ -153,7 +153,7 @@ export const SidebarProjectsList: FC = observer(() => {
|
|||||||
{
|
{
|
||||||
key: "favorite",
|
key: "favorite",
|
||||||
type: "FAVORITES",
|
type: "FAVORITES",
|
||||||
title: "Favorites",
|
title: "FAVORITES",
|
||||||
icon: Star,
|
icon: Star,
|
||||||
projects: favoriteProjects,
|
projects: favoriteProjects,
|
||||||
isOpen: isFavoriteProjectsListOpen,
|
isOpen: isFavoriteProjectsListOpen,
|
||||||
@@ -161,7 +161,7 @@ export const SidebarProjectsList: FC = observer(() => {
|
|||||||
{
|
{
|
||||||
key: "all",
|
key: "all",
|
||||||
type: "JOINED",
|
type: "JOINED",
|
||||||
title: "My projects",
|
title: "MY PROJECTS",
|
||||||
icon: Briefcase,
|
icon: Briefcase,
|
||||||
projects: joinedProjects,
|
projects: joinedProjects,
|
||||||
isOpen: isAllProjectsListOpen,
|
isOpen: isAllProjectsListOpen,
|
||||||
@@ -217,7 +217,13 @@ export const SidebarProjectsList: FC = observer(() => {
|
|||||||
position="right"
|
position="right"
|
||||||
disabled={!isCollapsed}
|
disabled={!isCollapsed}
|
||||||
>
|
>
|
||||||
<span>{isCollapsed ? <section.icon className="flex-shrink-0 size-3" /> : section.title}</span>
|
<>
|
||||||
|
{isCollapsed ? (
|
||||||
|
<section.icon className="flex-shrink-0 size-3" />
|
||||||
|
) : (
|
||||||
|
<span className="text-xs font-semibold">{section.title}</span>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Disclosure.Button>
|
</Disclosure.Button>
|
||||||
{!isCollapsed && (
|
{!isCollapsed && (
|
||||||
@@ -264,7 +270,7 @@ export const SidebarProjectsList: FC = observer(() => {
|
|||||||
{section.isOpen && (
|
{section.isOpen && (
|
||||||
<Disclosure.Panel
|
<Disclosure.Panel
|
||||||
as="div"
|
as="div"
|
||||||
className={cn("mt-2 ml-1 space-y-1", {
|
className={cn("space-y-1", {
|
||||||
"space-y-0 ml-0": isCollapsed,
|
"space-y-0 ml-0": isCollapsed,
|
||||||
})}
|
})}
|
||||||
static
|
static
|
||||||
|
|||||||
@@ -4,9 +4,9 @@ import React from "react";
|
|||||||
import { observer } from "mobx-react";
|
import { observer } from "mobx-react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useParams, usePathname } from "next/navigation";
|
import { useParams, usePathname } from "next/navigation";
|
||||||
// ui
|
|
||||||
import { Tooltip } from "@plane/ui";
|
|
||||||
// components
|
// components
|
||||||
|
import { Tooltip } from "@plane/ui";
|
||||||
|
import { SidebarNavItem } from "@/components/sidebar";
|
||||||
import { NotificationAppSidebarOption } from "@/components/workspace-notifications";
|
import { NotificationAppSidebarOption } from "@/components/workspace-notifications";
|
||||||
// constants
|
// constants
|
||||||
import { SIDEBAR_USER_MENU_ITEMS } from "@/constants/dashboard";
|
import { SIDEBAR_USER_MENU_ITEMS } from "@/constants/dashboard";
|
||||||
@@ -25,6 +25,7 @@ export const SidebarUserMenu = observer(() => {
|
|||||||
const { isMobile } = usePlatformOS();
|
const { isMobile } = usePlatformOS();
|
||||||
const {
|
const {
|
||||||
membership: { currentWorkspaceRole },
|
membership: { currentWorkspaceRole },
|
||||||
|
data: currentUser,
|
||||||
} = useUser();
|
} = useUser();
|
||||||
// router params
|
// router params
|
||||||
const { workspaceSlug } = useParams();
|
const { workspaceSlug } = useParams();
|
||||||
@@ -42,48 +43,47 @@ export const SidebarUserMenu = observer(() => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const notificationIndicatorElement = (
|
||||||
|
<NotificationAppSidebarOption
|
||||||
|
workspaceSlug={workspaceSlug.toString()}
|
||||||
|
isSidebarCollapsed={sidebarCollapsed ?? false}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={cn("w-full space-y-1", {
|
className={cn("flex flex-col gap-0.5", {
|
||||||
"space-y-0": sidebarCollapsed,
|
"space-y-0": sidebarCollapsed,
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
{SIDEBAR_USER_MENU_ITEMS.map(
|
{SIDEBAR_USER_MENU_ITEMS.map(
|
||||||
(link) =>
|
(link) =>
|
||||||
workspaceMemberInfo >= link.access && (
|
workspaceMemberInfo >= link.access && (
|
||||||
<Link key={link.key} href={`/${workspaceSlug}${link.href}`} onClick={() => handleLinkClick(link.key)}>
|
|
||||||
<Tooltip
|
<Tooltip
|
||||||
|
key={link.key}
|
||||||
tooltipContent={link.label}
|
tooltipContent={link.label}
|
||||||
position="right"
|
position="right"
|
||||||
className="ml-2"
|
className="ml-2"
|
||||||
disabled={!sidebarCollapsed}
|
disabled={!sidebarCollapsed}
|
||||||
isMobile={isMobile}
|
isMobile={isMobile}
|
||||||
>
|
>
|
||||||
<div
|
<Link
|
||||||
className={cn(
|
href={`/${workspaceSlug}${link.href}${link.key === "my-work" ? `/${currentUser?.id}` : ""}`}
|
||||||
"relative group w-full flex items-center gap-1.5 rounded-md px-2 py-1.5 outline-none text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-90 focus:bg-custom-sidebar-background-90",
|
onClick={() => handleLinkClick(link.key)}
|
||||||
{
|
|
||||||
"text-custom-primary-100 bg-custom-primary-100/10 hover:bg-custom-primary-100/10": link.highlight(
|
|
||||||
pathname,
|
|
||||||
`/${workspaceSlug}`
|
|
||||||
),
|
|
||||||
"p-0 size-8 aspect-square justify-center mx-auto": sidebarCollapsed,
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<span className="flex-shrink-0 size-4 grid place-items-center">
|
<SidebarNavItem
|
||||||
<link.Icon className="size-4" />
|
key={link.key}
|
||||||
</span>
|
className={`${sidebarCollapsed ? "p-0 size-8 aspect-square justify-center mx-auto" : ""}`}
|
||||||
|
isActive={link.highlight(pathname, `/${workspaceSlug}`)}
|
||||||
|
>
|
||||||
|
<div className="flex items-center gap-1.5 py-[1px]">
|
||||||
|
<link.Icon className="size-4 flex-shrink-0" />
|
||||||
{!sidebarCollapsed && <p className="text-sm leading-5 font-medium">{link.label}</p>}
|
{!sidebarCollapsed && <p className="text-sm leading-5 font-medium">{link.label}</p>}
|
||||||
{link.key === "notifications" && (
|
|
||||||
<NotificationAppSidebarOption
|
|
||||||
workspaceSlug={workspaceSlug.toString()}
|
|
||||||
isSidebarCollapsed={sidebarCollapsed ?? false}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
{link.key === "notifications" && notificationIndicatorElement}
|
||||||
|
</SidebarNavItem>
|
||||||
</Link>
|
</Link>
|
||||||
|
</Tooltip>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -8,6 +8,8 @@ import { ChevronRight } from "lucide-react";
|
|||||||
import { Disclosure, Transition } from "@headlessui/react";
|
import { Disclosure, Transition } from "@headlessui/react";
|
||||||
// ui
|
// ui
|
||||||
import { Tooltip } from "@plane/ui";
|
import { Tooltip } from "@plane/ui";
|
||||||
|
// components
|
||||||
|
import { SidebarNavItem } from "@/components/sidebar";
|
||||||
// constants
|
// constants
|
||||||
import { SIDEBAR_WORKSPACE_MENU_ITEMS } from "@/constants/dashboard";
|
import { SIDEBAR_WORKSPACE_MENU_ITEMS } from "@/constants/dashboard";
|
||||||
import { SIDEBAR_CLICKED } from "@/constants/event-tracker";
|
import { SIDEBAR_CLICKED } from "@/constants/event-tracker";
|
||||||
@@ -53,15 +55,21 @@ export const SidebarWorkspaceMenu = observer(() => {
|
|||||||
if (sidebarCollapsed) toggleWorkspaceMenu(true);
|
if (sidebarCollapsed) toggleWorkspaceMenu(true);
|
||||||
}, [sidebarCollapsed, toggleWorkspaceMenu]);
|
}, [sidebarCollapsed, toggleWorkspaceMenu]);
|
||||||
|
|
||||||
|
const indicatorElement = (
|
||||||
|
<div className="flex-shrink-0">
|
||||||
|
<UpgradeBadge />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Disclosure as="div" defaultOpen>
|
<Disclosure as="div" defaultOpen>
|
||||||
{!sidebarCollapsed && (
|
{!sidebarCollapsed && (
|
||||||
<Disclosure.Button
|
<Disclosure.Button
|
||||||
as="button"
|
as="button"
|
||||||
className="group/workspace-button w-full px-2 py-1.5 flex items-center justify-between gap-1 text-custom-sidebar-text-400 hover:bg-custom-sidebar-background-90 rounded text-sm font-semibold"
|
className="group/workspace-button w-full px-2 py-1.5 flex items-center justify-between gap-1 text-custom-sidebar-text-400 hover:bg-custom-sidebar-background-90 rounded text-xs font-semibold"
|
||||||
onClick={() => toggleWorkspaceMenu(!isWorkspaceMenuOpen)}
|
onClick={() => toggleWorkspaceMenu(!isWorkspaceMenuOpen)}
|
||||||
>
|
>
|
||||||
<span>Workspace</span>
|
<span>WORKSPACE</span>
|
||||||
<span className="flex-shrink-0 opacity-0 pointer-events-none group-hover/workspace-button:opacity-100 group-hover/workspace-button:pointer-events-auto rounded p-0.5 hover:bg-custom-sidebar-background-80">
|
<span className="flex-shrink-0 opacity-0 pointer-events-none group-hover/workspace-button:opacity-100 group-hover/workspace-button:pointer-events-auto rounded p-0.5 hover:bg-custom-sidebar-background-80">
|
||||||
<ChevronRight
|
<ChevronRight
|
||||||
className={cn("size-4 flex-shrink-0 text-custom-sidebar-text-400 transition-transform", {
|
className={cn("size-4 flex-shrink-0 text-custom-sidebar-text-400 transition-transform", {
|
||||||
@@ -83,7 +91,7 @@ export const SidebarWorkspaceMenu = observer(() => {
|
|||||||
{isWorkspaceMenuOpen && (
|
{isWorkspaceMenuOpen && (
|
||||||
<Disclosure.Panel
|
<Disclosure.Panel
|
||||||
as="div"
|
as="div"
|
||||||
className={cn("mt-2 ml-1 space-y-1", {
|
className={cn("flex flex-col mt-0.5 gap-0.5", {
|
||||||
"space-y-0 mt-0 ml-0": sidebarCollapsed,
|
"space-y-0 mt-0 ml-0": sidebarCollapsed,
|
||||||
})}
|
})}
|
||||||
static
|
static
|
||||||
@@ -91,47 +99,32 @@ export const SidebarWorkspaceMenu = observer(() => {
|
|||||||
{SIDEBAR_WORKSPACE_MENU_ITEMS.map(
|
{SIDEBAR_WORKSPACE_MENU_ITEMS.map(
|
||||||
(link) =>
|
(link) =>
|
||||||
workspaceMemberInfo >= link.access && (
|
workspaceMemberInfo >= link.access && (
|
||||||
<Link
|
|
||||||
key={link.key}
|
|
||||||
href={`/${workspaceSlug}${link.href}`}
|
|
||||||
onClick={() => handleLinkClick(link.key)}
|
|
||||||
className="block"
|
|
||||||
>
|
|
||||||
<Tooltip
|
<Tooltip
|
||||||
|
key={link.key}
|
||||||
tooltipContent={link.label}
|
tooltipContent={link.label}
|
||||||
position="right"
|
position="right"
|
||||||
className="ml-2"
|
className="ml-2"
|
||||||
disabled={!sidebarCollapsed}
|
disabled={!sidebarCollapsed}
|
||||||
isMobile={isMobile}
|
isMobile={isMobile}
|
||||||
>
|
>
|
||||||
<div
|
<Link href={`/${workspaceSlug}${link.href}`} onClick={() => handleLinkClick(link.key)}>
|
||||||
className={cn(
|
<SidebarNavItem
|
||||||
"group w-full flex items-center gap-1.5 rounded-md px-2 py-1.5 outline-none text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-90 focus:bg-custom-sidebar-background-90",
|
key={link.key}
|
||||||
{
|
className={`${sidebarCollapsed ? "p-0 size-8 aspect-square justify-center mx-auto" : ""}`}
|
||||||
"text-custom-primary-100 bg-custom-primary-100/10 hover:bg-custom-primary-100/10":
|
isActive={link.highlight(pathname, `/${workspaceSlug}`)}
|
||||||
link.highlight(pathname, `/${workspaceSlug}`),
|
|
||||||
"p-0 size-8 aspect-square justify-center mx-auto": sidebarCollapsed,
|
|
||||||
}
|
|
||||||
)}
|
|
||||||
>
|
>
|
||||||
<div className={cn("grow flex items-center gap-1.5", { "justify-center": sidebarCollapsed })}>
|
<div className="flex items-center gap-1.5 py-[1px]">
|
||||||
<span className="flex-shrink-0 size-4 grid place-items-center">
|
|
||||||
<link.Icon
|
<link.Icon
|
||||||
className={cn("size-4", {
|
className={cn("size-4", {
|
||||||
"rotate-180": link.key === "active-cycles",
|
"rotate-180": link.key === "active-cycles",
|
||||||
})}
|
})}
|
||||||
/>
|
/>
|
||||||
</span>
|
|
||||||
{!sidebarCollapsed && <p className="text-sm leading-5 font-medium">{link.label}</p>}
|
{!sidebarCollapsed && <p className="text-sm leading-5 font-medium">{link.label}</p>}
|
||||||
</div>
|
</div>
|
||||||
{!sidebarCollapsed && link.key === "active-cycles" && (
|
{!sidebarCollapsed && link.key === "active-cycles" && indicatorElement}
|
||||||
<div className="flex-shrink-0">
|
</SidebarNavItem>
|
||||||
<UpgradeBadge />
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
</Tooltip>
|
|
||||||
</Link>
|
</Link>
|
||||||
|
</Tooltip>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
</Disclosure.Panel>
|
</Disclosure.Panel>
|
||||||
|
|||||||
@@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
import { linearGradientDef } from "@nivo/core";
|
import { linearGradientDef } from "@nivo/core";
|
||||||
// icons
|
// icons
|
||||||
import { BarChart2, Bell, Briefcase, CheckCircle, Home, Settings } from "lucide-react";
|
import { BarChart2, Briefcase, Home, Inbox, Layers } from "lucide-react";
|
||||||
// types
|
// types
|
||||||
import { TIssuesListTypes, TStateGroups } from "@plane/types";
|
import { TIssuesListTypes, TStateGroups } from "@plane/types";
|
||||||
// ui
|
// ui
|
||||||
import { ContrastIcon } from "@plane/ui";
|
import { ContrastIcon, UserActivityIcon } from "@plane/ui";
|
||||||
import { Props } from "@/components/icons/types";
|
import { Props } from "@/components/icons/types";
|
||||||
// assets
|
// assets
|
||||||
import CompletedIssuesDark from "@/public/empty-state/dashboard/dark/completed-issues.svg";
|
import CompletedIssuesDark from "@/public/empty-state/dashboard/dark/completed-issues.svg";
|
||||||
@@ -259,14 +259,6 @@ export const SIDEBAR_WORKSPACE_MENU_ITEMS: {
|
|||||||
highlight: (pathname: string, baseUrl: string) => boolean;
|
highlight: (pathname: string, baseUrl: string) => boolean;
|
||||||
Icon: React.FC<Props>;
|
Icon: React.FC<Props>;
|
||||||
}[] = [
|
}[] = [
|
||||||
{
|
|
||||||
key: "all-issues",
|
|
||||||
label: "All Issues",
|
|
||||||
href: `/workspace-views/all-issues`,
|
|
||||||
access: EUserWorkspaceRoles.GUEST,
|
|
||||||
highlight: (pathname: string, baseUrl: string) => pathname.includes(`${baseUrl}/workspace-views/`),
|
|
||||||
Icon: CheckCircle,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
key: "projects",
|
key: "projects",
|
||||||
label: "Projects",
|
label: "Projects",
|
||||||
@@ -275,9 +267,17 @@ export const SIDEBAR_WORKSPACE_MENU_ITEMS: {
|
|||||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/projects/`,
|
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/projects/`,
|
||||||
Icon: Briefcase,
|
Icon: Briefcase,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "all-issues",
|
||||||
|
label: "Views",
|
||||||
|
href: `/workspace-views/all-issues`,
|
||||||
|
access: EUserWorkspaceRoles.GUEST,
|
||||||
|
highlight: (pathname: string, baseUrl: string) => pathname.includes(`${baseUrl}/workspace-views/`),
|
||||||
|
Icon: Layers,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "active-cycles",
|
key: "active-cycles",
|
||||||
label: "Active Cycles",
|
label: "Cycles",
|
||||||
href: `/active-cycles`,
|
href: `/active-cycles`,
|
||||||
access: EUserWorkspaceRoles.GUEST,
|
access: EUserWorkspaceRoles.GUEST,
|
||||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/active-cycles/`,
|
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/active-cycles/`,
|
||||||
@@ -291,14 +291,6 @@ export const SIDEBAR_WORKSPACE_MENU_ITEMS: {
|
|||||||
highlight: (pathname: string, baseUrl: string) => pathname.includes(`${baseUrl}/analytics/`),
|
highlight: (pathname: string, baseUrl: string) => pathname.includes(`${baseUrl}/analytics/`),
|
||||||
Icon: BarChart2,
|
Icon: BarChart2,
|
||||||
},
|
},
|
||||||
{
|
|
||||||
key: "settings",
|
|
||||||
label: "Settings",
|
|
||||||
href: `/settings`,
|
|
||||||
access: EUserWorkspaceRoles.GUEST,
|
|
||||||
highlight: (pathname: string, baseUrl: string) => pathname.includes(`${baseUrl}/settings/`),
|
|
||||||
Icon: Settings,
|
|
||||||
},
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export const SIDEBAR_USER_MENU_ITEMS: {
|
export const SIDEBAR_USER_MENU_ITEMS: {
|
||||||
@@ -317,12 +309,20 @@ export const SIDEBAR_USER_MENU_ITEMS: {
|
|||||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/`,
|
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/`,
|
||||||
Icon: Home,
|
Icon: Home,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: "my-work",
|
||||||
|
label: "My Work",
|
||||||
|
href: "/profile",
|
||||||
|
access: EUserWorkspaceRoles.GUEST,
|
||||||
|
highlight: (pathname: string, baseUrl: string) => pathname.includes(`${baseUrl}/profile/`),
|
||||||
|
Icon: UserActivityIcon,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
key: "notifications",
|
key: "notifications",
|
||||||
label: "Notifications",
|
label: "Inbox",
|
||||||
href: `/notifications`,
|
href: `/notifications`,
|
||||||
access: EUserWorkspaceRoles.GUEST,
|
access: EUserWorkspaceRoles.GUEST,
|
||||||
highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/notifications`,
|
highlight: (pathname: string, baseUrl: string) => pathname.includes(`${baseUrl}/notifications/`),
|
||||||
Icon: Bell,
|
Icon: Inbox,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
Reference in New Issue
Block a user