mirror of
https://github.com/makeplane/plane
synced 2025-08-07 19:59:33 +00:00
[WEB-1716] chore: sidebar improvements for guests/viewers (#4941)
* chore: sidebar improvements for guests/viewers * chore: store workspace menu open state in local storage
This commit is contained in:
committed by
GitHub
parent
eda1599c0d
commit
67784b45fd
@@ -351,11 +351,11 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
|
||||
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" />
|
||||
<MoreHorizontal className="size-3.5" />
|
||||
</span>
|
||||
}
|
||||
className={cn(
|
||||
"opacity-0 pointer-events-none flex-shrink-0 mr-1 group-hover/project-item:opacity-100 group-hover/project-item:pointer-events-auto",
|
||||
"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,
|
||||
}
|
||||
@@ -404,7 +404,6 @@ export const SidebarProjectsListItem: React.FC<Props> = observer((props) => {
|
||||
<span>Copy link</span>
|
||||
</span>
|
||||
</CustomMenu.MenuItem>
|
||||
|
||||
{!isViewerOrGuest && (
|
||||
<CustomMenu.MenuItem>
|
||||
<Link href={`/${workspaceSlug}/projects/${project?.id}/archives/issues`}>
|
||||
|
||||
@@ -220,21 +220,23 @@ export const SidebarProjectsList: FC = observer(() => {
|
||||
<span>{isCollapsed ? <section.icon className="flex-shrink-0 size-3" /> : section.title}</span>
|
||||
</Tooltip>
|
||||
</Disclosure.Button>
|
||||
{!isCollapsed && isAuthorizedUser && (
|
||||
{!isCollapsed && (
|
||||
<div className="flex items-center opacity-0 group-hover:opacity-100">
|
||||
<Tooltip tooltipHeading="Create project" tooltipContent="">
|
||||
<button
|
||||
type="button"
|
||||
className="p-0.5 rounded hover:bg-custom-sidebar-background-80 flex-shrink-0"
|
||||
onClick={() => {
|
||||
setTrackElement(`APP_SIDEBAR_${section.type}_BLOCK`);
|
||||
setIsFavoriteProjectCreate(section.key === "favorite");
|
||||
setIsProjectModalOpen(true);
|
||||
}}
|
||||
>
|
||||
<Plus className="size-3" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
{isAuthorizedUser && (
|
||||
<Tooltip tooltipHeading="Create project" tooltipContent="">
|
||||
<button
|
||||
type="button"
|
||||
className="p-0.5 rounded hover:bg-custom-sidebar-background-80 flex-shrink-0"
|
||||
onClick={() => {
|
||||
setTrackElement(`APP_SIDEBAR_${section.type}_BLOCK`);
|
||||
setIsFavoriteProjectCreate(section.key === "favorite");
|
||||
setIsProjectModalOpen(true);
|
||||
}}
|
||||
>
|
||||
<Plus className="size-3" />
|
||||
</button>
|
||||
</Tooltip>
|
||||
)}
|
||||
<Disclosure.Button
|
||||
as="button"
|
||||
type="button"
|
||||
@@ -296,14 +298,14 @@ export const SidebarProjectsList: FC = observer(() => {
|
||||
{isAuthorizedUser && joinedProjects?.length === 0 && (
|
||||
<button
|
||||
type="button"
|
||||
className="flex w-full items-center gap-2 px-3 text-sm text-custom-sidebar-text-200"
|
||||
className="w-full flex items-center gap-1.5 px-2 py-1.5 text-sm leading-5 font-medium text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-90 rounded-md"
|
||||
onClick={() => {
|
||||
setTrackElement("Sidebar");
|
||||
toggleCreateProjectModal(true);
|
||||
}}
|
||||
>
|
||||
<Plus className="size-5" />
|
||||
{!isCollapsed && "Add Project"}
|
||||
<Plus className="flex-shrink-0 size-4" />
|
||||
{!isCollapsed && "Add project"}
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -8,11 +8,10 @@ import { TIssue } from "@plane/types";
|
||||
import { CreateUpdateIssueModal } from "@/components/issues";
|
||||
// constants
|
||||
import { EIssuesStoreType } from "@/constants/issue";
|
||||
import { EUserWorkspaceRoles } from "@/constants/workspace";
|
||||
// helpers
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
// hooks
|
||||
import { useAppTheme, useCommandPalette, useEventTracker, useProject, useUser } from "@/hooks/store";
|
||||
import { useAppTheme, useCommandPalette, useEventTracker, useProject } from "@/hooks/store";
|
||||
import useLocalStorage from "@/hooks/use-local-storage";
|
||||
|
||||
export const SidebarQuickActions = observer(() => {
|
||||
@@ -30,16 +29,11 @@ export const SidebarQuickActions = observer(() => {
|
||||
const { sidebarCollapsed: isSidebarCollapsed } = useAppTheme();
|
||||
const { setTrackElement } = useEventTracker();
|
||||
const { joinedProjectIds } = useProject();
|
||||
const {
|
||||
membership: { currentWorkspaceRole },
|
||||
} = useUser();
|
||||
// local storage
|
||||
const { storedValue, setValue } = useLocalStorage<Record<string, Partial<TIssue>>>("draftedIssue", {});
|
||||
// derived values
|
||||
const disabled = joinedProjectIds.length === 0;
|
||||
const workspaceDraftIssue = workspaceSlug ? storedValue?.[workspaceSlug] ?? undefined : undefined;
|
||||
// auth
|
||||
const isAuthorizedUser = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER;
|
||||
|
||||
const handleMouseEnter = () => {
|
||||
// if enter before time out clear the timeout
|
||||
@@ -74,66 +68,64 @@ export const SidebarQuickActions = observer(() => {
|
||||
"flex-col gap-0": isSidebarCollapsed,
|
||||
})}
|
||||
>
|
||||
{isAuthorizedUser && (
|
||||
<div
|
||||
<div
|
||||
className={cn(
|
||||
"relative flex-grow flex items-center justify-between gap-1 rounded h-8 hover:bg-custom-sidebar-background-90",
|
||||
{
|
||||
"size-8 aspect-square": isSidebarCollapsed,
|
||||
"px-3 border-[0.5px] border-custom-sidebar-border-300": !isSidebarCollapsed,
|
||||
}
|
||||
)}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
"relative flex-grow flex items-center justify-between gap-1 rounded h-8 hover:bg-custom-sidebar-background-90",
|
||||
"relative flex flex-shrink-0 flex-grow items-center gap-2 text-custom-sidebar-text-300 rounded outline-none",
|
||||
{
|
||||
"size-8 aspect-square": isSidebarCollapsed,
|
||||
"px-3 border-[0.5px] border-custom-sidebar-border-300": !isSidebarCollapsed,
|
||||
}
|
||||
)}
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className={cn("relative flex flex-shrink-0 flex-grow items-center gap-2 rounded outline-none", {
|
||||
"justify-center": isSidebarCollapsed,
|
||||
"cursor-not-allowed opacity-50": disabled,
|
||||
})}
|
||||
onClick={() => {
|
||||
setTrackElement("APP_SIDEBAR_QUICK_ACTIONS");
|
||||
toggleCreateIssueModal(true, EIssuesStoreType.PROJECT);
|
||||
}}
|
||||
disabled={disabled}
|
||||
>
|
||||
<PenSquare className="size-4 text-custom-sidebar-text-300" />
|
||||
{!isSidebarCollapsed && <span className="text-sm font-medium">New issue</span>}
|
||||
</button>
|
||||
{!disabled && workspaceDraftIssue && (
|
||||
<>
|
||||
{!isSidebarCollapsed && (
|
||||
<button type="button" className="grid place-items-center">
|
||||
<ChevronUp
|
||||
className={cn(
|
||||
"size-4 transform !text-custom-sidebar-text-300 transition-transform duration-300",
|
||||
{
|
||||
"rotate-180": isDraftButtonOpen,
|
||||
}
|
||||
)}
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
{isDraftButtonOpen && (
|
||||
<div
|
||||
className={`fixed left-4 mt-0 h-10 w-[203px] pt-2 ${isSidebarCollapsed ? "top-[5.5rem]" : "top-24"}`}
|
||||
>
|
||||
<div className="h-full w-full">
|
||||
<button
|
||||
onClick={() => setIsDraftIssueModalOpen(true)}
|
||||
className="flex w-full flex-shrink-0 items-center rounded border-[0.5px] border-custom-border-300 bg-custom-background-100 px-3 py-[10px] text-sm text-custom-text-300 shadow"
|
||||
>
|
||||
<PenSquare size={16} className="mr-2 !text-lg !leading-4 text-custom-sidebar-text-300" />
|
||||
Last Drafted Issue
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
onClick={() => {
|
||||
setTrackElement("APP_SIDEBAR_QUICK_ACTIONS");
|
||||
toggleCreateIssueModal(true, EIssuesStoreType.PROJECT);
|
||||
}}
|
||||
disabled={disabled}
|
||||
>
|
||||
<PenSquare className="size-4" />
|
||||
{!isSidebarCollapsed && <span className="text-sm font-medium">New issue</span>}
|
||||
</button>
|
||||
{!disabled && workspaceDraftIssue && (
|
||||
<>
|
||||
{!isSidebarCollapsed && (
|
||||
<button type="button" className="grid place-items-center">
|
||||
<ChevronUp
|
||||
className={cn("size-4 transform !text-custom-sidebar-text-300 transition-transform duration-300", {
|
||||
"rotate-180": isDraftButtonOpen,
|
||||
})}
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
{isDraftButtonOpen && (
|
||||
<div
|
||||
className={`fixed left-4 mt-0 h-10 w-[203px] pt-2 ${isSidebarCollapsed ? "top-[5.5rem]" : "top-24"}`}
|
||||
>
|
||||
<div className="h-full w-full">
|
||||
<button
|
||||
onClick={() => setIsDraftIssueModalOpen(true)}
|
||||
className="flex w-full flex-shrink-0 items-center rounded border-[0.5px] border-custom-border-300 bg-custom-background-100 px-3 py-[10px] text-sm text-custom-text-300 shadow"
|
||||
>
|
||||
<PenSquare size={16} className="mr-2 !text-lg !leading-4 text-custom-sidebar-text-300" />
|
||||
Last Drafted Issue
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
<button
|
||||
className={cn(
|
||||
"flex-shrink-0 size-8 aspect-square grid place-items-center rounded hover:bg-custom-sidebar-background-90 outline-none",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React, { useEffect } from "react";
|
||||
import { observer } from "mobx-react";
|
||||
import Link from "next/link";
|
||||
import { useParams, usePathname } from "next/navigation";
|
||||
@@ -16,11 +16,10 @@ import { EUserWorkspaceRoles } from "@/constants/workspace";
|
||||
import { cn } from "@/helpers/common.helper";
|
||||
// hooks
|
||||
import { useAppTheme, useEventTracker, useUser } from "@/hooks/store";
|
||||
import useLocalStorage from "@/hooks/use-local-storage";
|
||||
import { usePlatformOS } from "@/hooks/use-platform-os";
|
||||
|
||||
export const SidebarWorkspaceMenu = observer(() => {
|
||||
// states
|
||||
const [isWorkspaceMenuOpen, setIsWorkspaceMenuOpen] = useState(true);
|
||||
// store hooks
|
||||
const { toggleSidebar, sidebarCollapsed } = useAppTheme();
|
||||
const { captureEvent } = useEventTracker();
|
||||
@@ -32,7 +31,11 @@ export const SidebarWorkspaceMenu = observer(() => {
|
||||
const { workspaceSlug } = useParams();
|
||||
// pathname
|
||||
const pathname = usePathname();
|
||||
// computed
|
||||
// local storage
|
||||
const { setValue: toggleWorkspaceMenu, storedValue } = useLocalStorage<boolean>("is_workspace_menu_open", true);
|
||||
// derived values
|
||||
const isWorkspaceMenuOpen = !!storedValue;
|
||||
// auth
|
||||
const workspaceMemberInfo = currentWorkspaceRole || EUserWorkspaceRoles.GUEST;
|
||||
|
||||
const handleLinkClick = (itemKey: string) => {
|
||||
@@ -45,8 +48,8 @@ export const SidebarWorkspaceMenu = observer(() => {
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (sidebarCollapsed) setIsWorkspaceMenuOpen(true);
|
||||
}, [sidebarCollapsed]);
|
||||
if (sidebarCollapsed) toggleWorkspaceMenu(true);
|
||||
}, [sidebarCollapsed, toggleWorkspaceMenu]);
|
||||
|
||||
return (
|
||||
<Disclosure as="div" defaultOpen>
|
||||
@@ -54,20 +57,16 @@ export const SidebarWorkspaceMenu = observer(() => {
|
||||
<Disclosure.Button
|
||||
as="button"
|
||||
className="group/workspace-button w-full px-2 py-0.5 flex items-center justify-between gap-1 text-custom-sidebar-text-400 hover:bg-custom-sidebar-background-90 rounded text-sm font-semibold"
|
||||
onClick={() => setIsWorkspaceMenuOpen((prev) => !prev)}
|
||||
onClick={() => toggleWorkspaceMenu(!isWorkspaceMenuOpen)}
|
||||
>
|
||||
{({ open }) => (
|
||||
<>
|
||||
<span>Workspace</span>
|
||||
<span className="flex-shrink-0 hidden group-hover/workspace-button:inline-block rounded p-0.5 hover:bg-custom-sidebar-background-80">
|
||||
<ChevronRight
|
||||
className={cn("size-3.5 flex-shrink-0 text-custom-sidebar-text-400 transition-transform", {
|
||||
"rotate-90": open,
|
||||
})}
|
||||
/>
|
||||
</span>
|
||||
</>
|
||||
)}
|
||||
<span>Workspace</span>
|
||||
<span className="flex-shrink-0 hidden group-hover/workspace-button:inline-block rounded p-0.5 hover:bg-custom-sidebar-background-80">
|
||||
<ChevronRight
|
||||
className={cn("size-3.5 flex-shrink-0 text-custom-sidebar-text-400 transition-transform", {
|
||||
"rotate-90": isWorkspaceMenuOpen,
|
||||
})}
|
||||
/>
|
||||
</span>
|
||||
</Disclosure.Button>
|
||||
)}
|
||||
<Transition
|
||||
|
||||
Reference in New Issue
Block a user