forked from github/plane
style: app sidebar revamp (#1120)
* style: app sidebar, sidebar workspace dropdown and help section styling * style: consistent padding and spacing * feat: material icon global component * style: icons updated and tooltip added * style: project list spacing and project name truncate * style: sidebar padding and theming --------- Co-authored-by: Aaryan Khandelwal <aaryankhandu123@gmail.com>
This commit is contained in:
parent
3f3fb373cc
commit
60e96bcb72
@ -70,8 +70,7 @@ const TOUR_STEPS: {
|
||||
{
|
||||
key: "pages",
|
||||
title: "Document with pages",
|
||||
description:
|
||||
"Modules break your big think into Projects or Features, to help you organize better.",
|
||||
description: "Use Pages to quickly jot down issues when you're in a meeting or starting a day.",
|
||||
image: PagesTour,
|
||||
prevStep: "views",
|
||||
},
|
||||
|
@ -151,11 +151,11 @@ export const ProjectSidebarList: FC = () => {
|
||||
data={projectToDelete}
|
||||
user={user}
|
||||
/>
|
||||
<div className="mt-2.5 h-full overflow-y-auto border-t border-custom-sidebar-border-100 bg-custom-sidebar-background-100 pt-2.5">
|
||||
<div className="h-full overflow-y-auto mt-5 px-4">
|
||||
{favoriteProjects && favoriteProjects.length > 0 && (
|
||||
<div className="mt-3 flex flex-col space-y-2 px-3">
|
||||
<div className="flex flex-col space-y-2">
|
||||
{!sidebarCollapse && (
|
||||
<h5 className="text-sm font-semibold text-custom-sidebar-text-200">Favorites</h5>
|
||||
<h5 className="text-sm font-medium text-custom-sidebar-text-200">Favorites</h5>
|
||||
)}
|
||||
{favoriteProjects.map((favoriteProject) => {
|
||||
const project = favoriteProject.project_detail;
|
||||
@ -173,9 +173,9 @@ export const ProjectSidebarList: FC = () => {
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
<div className="flex flex-col space-y-2 p-3">
|
||||
<div className="flex flex-col space-y-2 mt-5">
|
||||
{!sidebarCollapse && (
|
||||
<h5 className="text-sm font-semibold text-custom-sidebar-text-200">Projects</h5>
|
||||
<h5 className="text-sm font-medium text-custom-sidebar-text-200">Projects</h5>
|
||||
)}
|
||||
{projects ? (
|
||||
<>
|
||||
|
@ -4,22 +4,9 @@ import { useRouter } from "next/router";
|
||||
// headless ui
|
||||
import { Disclosure, Transition } from "@headlessui/react";
|
||||
// ui
|
||||
import { CustomMenu, Icon } from "components/ui";
|
||||
import { CustomMenu, Icon, Tooltip } from "components/ui";
|
||||
// icons
|
||||
import {
|
||||
ChevronDownIcon,
|
||||
DocumentTextIcon,
|
||||
LinkIcon,
|
||||
StarIcon,
|
||||
TrashIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import {
|
||||
ContrastIcon,
|
||||
LayerDiagonalIcon,
|
||||
PeopleGroupIcon,
|
||||
SettingIcon,
|
||||
ViewListIcon,
|
||||
} from "components/icons";
|
||||
import { LinkIcon, StarIcon, TrashIcon } from "@heroicons/react/24/outline";
|
||||
// helpers
|
||||
import { truncateText } from "helpers/string.helper";
|
||||
import { renderEmoji } from "helpers/emoji.helper";
|
||||
@ -39,32 +26,32 @@ const navigation = (workspaceSlug: string, projectId: string) => [
|
||||
{
|
||||
name: "Issues",
|
||||
href: `/${workspaceSlug}/projects/${projectId}/issues`,
|
||||
icon: LayerDiagonalIcon,
|
||||
icon: "stack",
|
||||
},
|
||||
{
|
||||
name: "Cycles",
|
||||
href: `/${workspaceSlug}/projects/${projectId}/cycles`,
|
||||
icon: ContrastIcon,
|
||||
icon: "contrast",
|
||||
},
|
||||
{
|
||||
name: "Modules",
|
||||
href: `/${workspaceSlug}/projects/${projectId}/modules`,
|
||||
icon: PeopleGroupIcon,
|
||||
icon: "dataset",
|
||||
},
|
||||
{
|
||||
name: "Views",
|
||||
href: `/${workspaceSlug}/projects/${projectId}/views`,
|
||||
icon: ViewListIcon,
|
||||
icon: "photo_filter",
|
||||
},
|
||||
{
|
||||
name: "Pages",
|
||||
href: `/${workspaceSlug}/projects/${projectId}/pages`,
|
||||
icon: DocumentTextIcon,
|
||||
icon: "article",
|
||||
},
|
||||
{
|
||||
name: "Settings",
|
||||
href: `/${workspaceSlug}/projects/${projectId}/settings`,
|
||||
icon: SettingIcon,
|
||||
icon: "settings",
|
||||
},
|
||||
];
|
||||
|
||||
@ -84,44 +71,56 @@ export const SingleSidebarProject: React.FC<Props> = ({
|
||||
{({ open }) => (
|
||||
<>
|
||||
<div className="flex items-center gap-x-1 text-custom-sidebar-text-100">
|
||||
<Disclosure.Button
|
||||
as="div"
|
||||
className={`flex w-full cursor-pointer select-none items-center rounded-md py-2 text-left text-sm font-medium ${
|
||||
sidebarCollapse ? "justify-center" : "justify-between"
|
||||
}`}
|
||||
<Tooltip
|
||||
tooltipContent={`${project?.name}`}
|
||||
position="right"
|
||||
className="ml-2"
|
||||
disabled={!sidebarCollapse}
|
||||
>
|
||||
<div className="flex items-center gap-x-2">
|
||||
{project.emoji ? (
|
||||
<span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded uppercase">
|
||||
{renderEmoji(project.emoji)}
|
||||
</span>
|
||||
) : project.icon_prop ? (
|
||||
<div className="h-7 w-7 grid place-items-center">
|
||||
<span
|
||||
style={{ color: project.icon_prop.color }}
|
||||
className="material-symbols-rounded text-lg"
|
||||
>
|
||||
{project.icon_prop.name}
|
||||
<Disclosure.Button
|
||||
as="div"
|
||||
className={`flex w-full cursor-pointer select-none items-center rounded-sm py-1 text-left text-sm font-medium ${
|
||||
sidebarCollapse ? "justify-center" : "justify-between"
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-x-2">
|
||||
{project.emoji ? (
|
||||
<span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded uppercase">
|
||||
{renderEmoji(project.emoji)}
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded bg-gray-700 uppercase text-white">
|
||||
{project?.name.charAt(0)}
|
||||
</span>
|
||||
)}
|
||||
) : project.icon_prop ? (
|
||||
<div className="h-7 w-7 grid place-items-center">
|
||||
<span
|
||||
style={{ color: project.icon_prop.color }}
|
||||
className="material-symbols-rounded text-lg"
|
||||
>
|
||||
{project.icon_prop.name}
|
||||
</span>
|
||||
</div>
|
||||
) : (
|
||||
<span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded bg-gray-700 uppercase text-white">
|
||||
{project?.name.charAt(0)}
|
||||
</span>
|
||||
)}
|
||||
|
||||
{!sidebarCollapse && (
|
||||
<p
|
||||
className={`overflow-hidden text-ellipsis ${
|
||||
open ? "" : "text-custom-sidebar-text-200"
|
||||
}`}
|
||||
>
|
||||
{truncateText(project?.name, 14)}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
{!sidebarCollapse && (
|
||||
<h5 className={`overflow-hidden text-sm ${open ? "" : "text-custom-text-200"}`}>
|
||||
{truncateText(project?.name, 20)}
|
||||
</h5>
|
||||
<Icon
|
||||
iconName="expand_more"
|
||||
className={`${open ? "rotate-180" : ""} text-custom-text-200 duration-300`}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{!sidebarCollapse && (
|
||||
<span>
|
||||
<ChevronDownIcon className={`h-4 w-4 duration-300 ${open ? "rotate-180" : ""}`} />
|
||||
</span>
|
||||
)}
|
||||
</Disclosure.Button>
|
||||
</Disclosure.Button>
|
||||
</Tooltip>
|
||||
|
||||
{!sidebarCollapse && (
|
||||
<CustomMenu ellipsis>
|
||||
@ -177,9 +176,7 @@ export const SingleSidebarProject: React.FC<Props> = ({
|
||||
leaveFrom="transform scale-100 opacity-100"
|
||||
leaveTo="transform scale-95 opacity-0"
|
||||
>
|
||||
<Disclosure.Panel
|
||||
className={`${sidebarCollapse ? "" : "ml-[2.25rem]"} flex flex-col gap-y-1`}
|
||||
>
|
||||
<Disclosure.Panel className={`space-y-2 ${sidebarCollapse ? "" : "ml-[2.25rem]"}`}>
|
||||
{navigation(workspaceSlug as string, project?.id).map((item) => {
|
||||
if (
|
||||
(item.name === "Cycles" && !project.cycle_view) ||
|
||||
@ -191,25 +188,24 @@ export const SingleSidebarProject: React.FC<Props> = ({
|
||||
|
||||
return (
|
||||
<Link key={item.name} href={item.href}>
|
||||
<a
|
||||
className={`group flex items-center rounded-md p-2 text-xs font-medium outline-none ${
|
||||
router.asPath.includes(item.href)
|
||||
? "bg-custom-sidebar-background-90 text-custom-sidebar-text-100"
|
||||
: "text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-90 focus:bg-custom-sidebar-background-90"
|
||||
} ${sidebarCollapse ? "justify-center" : ""}`}
|
||||
>
|
||||
<div className="grid place-items-center">
|
||||
<item.icon
|
||||
className={`h-5 w-5 flex-shrink-0 ${!sidebarCollapse ? "mr-3" : ""}`}
|
||||
color={
|
||||
<a className="block w-full">
|
||||
<Tooltip
|
||||
tooltipContent={`${project?.name}: ${item.name}`}
|
||||
position="right"
|
||||
className="ml-2"
|
||||
disabled={!sidebarCollapse}
|
||||
>
|
||||
<div
|
||||
className={`group flex items-center rounded-md px-2 py-1.5 gap-2 text-xs font-medium outline-none ${
|
||||
router.asPath.includes(item.href)
|
||||
? "rgb(var(--color-sidebar-text-100))"
|
||||
: "rgb(var(--color-sidebar-text-200))"
|
||||
}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
{!sidebarCollapse && item.name}
|
||||
? "bg-custom-primary-100/10 text-custom-primary-100"
|
||||
: "text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-80 focus:bg-custom-sidebar-background-80"
|
||||
} ${sidebarCollapse ? "justify-center" : ""}`}
|
||||
>
|
||||
<Icon iconName={item.icon} />
|
||||
{!sidebarCollapse && item.name}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</a>
|
||||
</Link>
|
||||
);
|
||||
|
@ -5,6 +5,7 @@ import Link from "next/link";
|
||||
import { Menu, Transition } from "@headlessui/react";
|
||||
// icons
|
||||
import { ChevronDownIcon, EllipsisHorizontalIcon } from "@heroicons/react/24/outline";
|
||||
import { Icon } from "./icon";
|
||||
|
||||
type Props = {
|
||||
children: React.ReactNode;
|
||||
@ -61,8 +62,9 @@ const CustomMenu = ({
|
||||
type="button"
|
||||
className="relative grid place-items-center rounded p-1 text-custom-text-200 hover:bg-custom-background-80 outline-none"
|
||||
>
|
||||
<EllipsisHorizontalIcon
|
||||
className={`h-4 w-4 ${verticalEllipsis ? "rotate-90" : ""}`}
|
||||
<Icon
|
||||
iconName="more_horiz"
|
||||
className={`${verticalEllipsis ? "rotate-90" : ""} text-brand-secondary`}
|
||||
/>
|
||||
</Menu.Button>
|
||||
) : (
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { useState, useRef, FC } from "react";
|
||||
import React, { useRef, useState } from "react";
|
||||
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
@ -8,9 +8,16 @@ import useSWR from "swr";
|
||||
|
||||
// headless ui
|
||||
import { Transition } from "@headlessui/react";
|
||||
// services
|
||||
import workspaceService from "services/workspace.service";
|
||||
// hooks
|
||||
import useTheme from "hooks/use-theme";
|
||||
import useUser from "hooks/use-user";
|
||||
import useOutsideClickDetector from "hooks/use-outside-click-detector";
|
||||
// components
|
||||
import UpgradeToProModal from "./upgrade-to-pro-modal";
|
||||
// ui
|
||||
import { CircularProgress } from "components/ui";
|
||||
// icons
|
||||
import {
|
||||
ArrowLongLeftIcon,
|
||||
@ -20,15 +27,8 @@ import {
|
||||
XMarkIcon,
|
||||
} from "@heroicons/react/24/outline";
|
||||
import { QuestionMarkCircleIcon, DocumentIcon, DiscordIcon, GithubIcon } from "components/icons";
|
||||
// services
|
||||
import workspaceService from "services/workspace.service";
|
||||
// fetch-keys
|
||||
import { WORKSPACE_DETAILS } from "constants/fetch-keys";
|
||||
// ui
|
||||
import { CircularProgress } from "components/ui";
|
||||
// components
|
||||
import UpgradeToProModal from "./upgrade-to-pro-modal";
|
||||
import useUser from "hooks/use-user";
|
||||
|
||||
const helpOptions = [
|
||||
{
|
||||
@ -58,36 +58,28 @@ export interface WorkspaceHelpSectionProps {
|
||||
setSidebarActive: React.Dispatch<React.SetStateAction<boolean>>;
|
||||
}
|
||||
|
||||
type progress = {
|
||||
progress: number;
|
||||
};
|
||||
export const WorkspaceHelpSection: FC<WorkspaceHelpSectionProps> = (props) => {
|
||||
// router
|
||||
export const WorkspaceHelpSection: React.FC<WorkspaceHelpSectionProps> = ({ setSidebarActive }) => {
|
||||
const [alert, setAlert] = useState(false);
|
||||
const [isNeedHelpOpen, setIsNeedHelpOpen] = useState(false);
|
||||
|
||||
const helpOptionsRef = useRef<HTMLDivElement | null>(null);
|
||||
|
||||
const router = useRouter();
|
||||
const { workspaceSlug } = router.query;
|
||||
|
||||
const { setSidebarActive } = props;
|
||||
// theme
|
||||
const { collapsed: sidebarCollapse, toggleCollapsed } = useTheme();
|
||||
// states
|
||||
const [isNeedHelpOpen, setIsNeedHelpOpen] = useState(false);
|
||||
// refs
|
||||
const helpOptionsRef = useRef<HTMLDivElement | null>(null);
|
||||
// hooks
|
||||
|
||||
useOutsideClickDetector(helpOptionsRef, () => setIsNeedHelpOpen(false));
|
||||
|
||||
const { user } = useUser();
|
||||
|
||||
const helpOptionMode = sidebarCollapse ? "left-full" : "left-[-75px]";
|
||||
|
||||
const [alert, setAlert] = useState(false);
|
||||
|
||||
const [upgradeModal, setUpgradeModal] = useState(false);
|
||||
|
||||
const { data: workspaceDetails } = useSWR(
|
||||
workspaceSlug ? WORKSPACE_DETAILS(workspaceSlug as string) : null,
|
||||
workspaceSlug ? () => workspaceService.getWorkspace(workspaceSlug as string) : null
|
||||
);
|
||||
|
||||
const issueNumber = workspaceDetails?.total_issues || 0;
|
||||
|
||||
return (
|
||||
@ -98,61 +90,58 @@ export const WorkspaceHelpSection: FC<WorkspaceHelpSectionProps> = (props) => {
|
||||
user={user}
|
||||
issueNumber={issueNumber}
|
||||
/>
|
||||
{!sidebarCollapse && (alert || (issueNumber && issueNumber >= 750)) ? (
|
||||
<>
|
||||
<div
|
||||
className={`border-t p-4 ${
|
||||
issueNumber >= 750
|
||||
? "bg-red-50 text-red-600 border-red-200"
|
||||
: issueNumber >= 500
|
||||
? "bg-yellow-50 text-yellow-600 border-yellow-200"
|
||||
: "text-green-600"
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-2 w-full">
|
||||
<CircularProgress progress={(issueNumber / 1024) * 100} />
|
||||
<div className="">Free Plan</div>
|
||||
{issueNumber < 750 && (
|
||||
<div className="ml-auto text-custom-text-200" onClick={() => setAlert(false)}>
|
||||
<XMarkIcon className="h-4 w-4" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="text-custom-text-200 text-xs mt-2">
|
||||
This workspace has used {issueNumber} of its 1024 issues creation limit (
|
||||
{((issueNumber / 1024) * 100).toFixed(0)}
|
||||
%).
|
||||
</div>
|
||||
{!sidebarCollapse && (alert || issueNumber >= 750) && (
|
||||
<div
|
||||
className={`border-t border-custom-sidebar-border-100 p-4 ${
|
||||
issueNumber >= 750
|
||||
? "bg-red-500/10 text-red-600"
|
||||
: issueNumber >= 500
|
||||
? "bg-yellow-500/10 text-yellow-600"
|
||||
: "text-green-600"
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<CircularProgress progress={(issueNumber / 1024) * 100} />
|
||||
<div>Free Plan</div>
|
||||
{issueNumber < 750 && (
|
||||
<div
|
||||
className="ml-auto text-custom-text-200 cursor-pointer"
|
||||
onClick={() => setAlert(false)}
|
||||
>
|
||||
<XMarkIcon className="h-4 w-4" />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
""
|
||||
<div className="text-custom-text-200 text-xs mt-2">
|
||||
This workspace has used {issueNumber} of its 1024 issues creation limit (
|
||||
{((issueNumber / 1024) * 100).toFixed(0)}
|
||||
%).
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={`flex w-full items-center justify-between self-baseline border-t border-custom-border-100 bg-custom-sidebar-background-100 px-6 py-2 ${
|
||||
className={`flex w-full items-center justify-between gap-1 self-baseline border-t border-custom-border-100 bg-custom-sidebar-background-100 px-4 py-2 ${
|
||||
sidebarCollapse ? "flex-col" : ""
|
||||
}`}
|
||||
>
|
||||
{alert || (issueNumber && issueNumber >= 750) ? (
|
||||
{alert || issueNumber >= 750 ? (
|
||||
<button
|
||||
type="button"
|
||||
className={`flex items-center gap-x-1 rounded-md px-2 py-2 font-medium outline-none text-sm
|
||||
${
|
||||
className={`flex items-center gap-2 rounded-md px-2.5 py-1.5 font-medium outline-none text-sm ${
|
||||
issueNumber >= 750
|
||||
? "bg-custom-primary-100 text-white"
|
||||
? "bg-red-500/10 text-red-500"
|
||||
: "bg-blue-500/10 text-custom-primary-100"
|
||||
}
|
||||
${sidebarCollapse ? "w-full justify-center" : ""}`}
|
||||
} ${sidebarCollapse ? "w-full justify-center" : ""}`}
|
||||
title="Shortcuts"
|
||||
onClick={() => setUpgradeModal(true)}
|
||||
>
|
||||
<ArrowUpCircleIcon className="h-4 w-4 " />
|
||||
{!sidebarCollapse && <span> Learn more</span>}
|
||||
<ArrowUpCircleIcon className="h-4 w-4" />
|
||||
{!sidebarCollapse && <span>Learn more</span>}
|
||||
</button>
|
||||
) : (
|
||||
<button
|
||||
type="button"
|
||||
className={`flex items-center gap-x-1 rounded-md px-2 py-2 font-medium outline-none text-sm ${
|
||||
className={`flex items-center gap-2 rounded-md px-2.5 py-1.5 font-medium outline-none text-sm ${
|
||||
issueNumber >= 750
|
||||
? "bg-red-500/10 text-red-600"
|
||||
: issueNumber >= 500
|
||||
@ -169,7 +158,6 @@ export const WorkspaceHelpSection: FC<WorkspaceHelpSectionProps> = (props) => {
|
||||
{!sidebarCollapse && <span>Free Plan</span>}
|
||||
</button>
|
||||
)}
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className={`flex items-center gap-x-1 rounded-md px-2 py-2 text-xs font-medium text-custom-text-200 outline-none hover:bg-custom-background-90 hover:text-custom-text-100 ${
|
||||
@ -184,7 +172,6 @@ export const WorkspaceHelpSection: FC<WorkspaceHelpSectionProps> = (props) => {
|
||||
title="Shortcuts"
|
||||
>
|
||||
<RocketLaunchIcon className="h-4 w-4 text-custom-text-200" />
|
||||
{/* {!sidebarCollapse && <span>Shortcuts</span>} */}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@ -195,7 +182,6 @@ export const WorkspaceHelpSection: FC<WorkspaceHelpSectionProps> = (props) => {
|
||||
title="Help"
|
||||
>
|
||||
<QuestionMarkCircleIcon className="h-4 w-4 text-custom-text-200" />
|
||||
{/* {!sidebarCollapse && <span>Help</span>} */}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
@ -229,7 +215,9 @@ export const WorkspaceHelpSection: FC<WorkspaceHelpSectionProps> = (props) => {
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<div
|
||||
className={`absolute bottom-2 ${helpOptionMode} space-y-2 rounded-sm bg-custom-background-80 p-1 shadow-md`}
|
||||
className={`absolute bottom-2 ${
|
||||
sidebarCollapse ? "left-full" : "left-[-75px]"
|
||||
} space-y-2 rounded-sm bg-custom-background-80 p-1 shadow-md`}
|
||||
ref={helpOptionsRef}
|
||||
>
|
||||
{helpOptions.map(({ name, Icon, href, onClick }) => {
|
||||
|
@ -86,157 +86,155 @@ export const WorkspaceSidebarDropdown = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<Menu as="div" className="col-span-4 inline-block w-full p-3 text-left">
|
||||
<div className="flex items-center justify-between gap-2.5">
|
||||
<Menu.Button className="text-custom-sidebar-text-200 flex w-full items-center rounded-md py-2 text-sm font-semibold focus:outline-none">
|
||||
<div
|
||||
className={`flex w-full items-center gap-x-2 rounded-md bg-custom-sidebar-background-80 px-2 py-1.5 ${
|
||||
sidebarCollapse ? "justify-center" : ""
|
||||
}`}
|
||||
>
|
||||
<div className="relative grid h-6 w-6 place-items-center rounded bg-gray-700 uppercase text-white">
|
||||
{activeWorkspace?.logo && activeWorkspace.logo !== "" ? (
|
||||
<img
|
||||
src={activeWorkspace.logo}
|
||||
className="absolute top-0 left-0 h-full w-full object-cover rounded"
|
||||
alt="Workspace Logo"
|
||||
/>
|
||||
) : (
|
||||
activeWorkspace?.name?.charAt(0) ?? "..."
|
||||
)}
|
||||
</div>
|
||||
|
||||
{!sidebarCollapse && (
|
||||
<h4 className="text-custom-text-100">
|
||||
{activeWorkspace?.name ? truncateText(activeWorkspace.name, 14) : "Loading..."}
|
||||
</h4>
|
||||
)}
|
||||
</div>
|
||||
</Menu.Button>
|
||||
|
||||
{!sidebarCollapse && (
|
||||
<Link href={`/${workspaceSlug}/me/profile`}>
|
||||
<a>
|
||||
<div className="flex flex-grow justify-end">
|
||||
<Avatar user={user} height="32px" width="32px" fontSize="14px" />
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-100"
|
||||
enterFrom="transform opacity-0 scale-95"
|
||||
enterTo="transform opacity-100 scale-100"
|
||||
leave="transition ease-in duration-75"
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items
|
||||
className="fixed left-2 z-20 mt-1 flex w-full max-w-[17rem] origin-top-left flex-col rounded-md
|
||||
border border-custom-sidebar-border-100 bg-custom-sidebar-background-90 shadow-lg focus:outline-none"
|
||||
<Menu as="div" className="relative col-span-4 inline-block w-full px-4 pt-4 text-left">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<Menu.Button className="text-custom-sidebar-text-200 flex w-full items-center rounded-sm text-sm font-medium focus:outline-none">
|
||||
<div
|
||||
className={`flex w-full items-center gap-x-2 rounded-sm bg-custom-sidebar-background-80 p-1 ${
|
||||
sidebarCollapse ? "justify-center" : ""
|
||||
}`}
|
||||
>
|
||||
<div className="flex flex-col items-start justify-start gap-3 p-3">
|
||||
<div className="text-sm text-custom-sidebar-text-200">{user?.email}</div>
|
||||
<span className="text-sm font-semibold text-custom-sidebar-text-200">Workspace</span>
|
||||
{workspaces ? (
|
||||
<div className="flex h-full w-full flex-col items-start justify-start gap-3.5">
|
||||
{workspaces.length > 0 ? (
|
||||
workspaces.map((workspace) => (
|
||||
<Menu.Item key={workspace.id}>
|
||||
{({ active }) => (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleWorkspaceNavigation(workspace)}
|
||||
className="flex w-full items-center justify-between gap-1 rounded-md text-sm text-custom-sidebar-text-100"
|
||||
>
|
||||
<div className="flex items-center justify-start gap-2.5">
|
||||
<span className="relative flex h-6 w-6 items-center justify-center rounded bg-gray-700 p-2 text-xs uppercase text-white">
|
||||
{workspace?.logo && workspace.logo !== "" ? (
|
||||
<img
|
||||
src={workspace.logo}
|
||||
className="absolute top-0 left-0 h-full w-full object-cover rounded"
|
||||
alt="Workspace Logo"
|
||||
/>
|
||||
) : (
|
||||
workspace?.name?.charAt(0) ?? "..."
|
||||
)}
|
||||
</span>
|
||||
|
||||
<h5
|
||||
className={`text-sm ${
|
||||
workspaceSlug === workspace.slug ? "" : "text-custom-text-200"
|
||||
}`}
|
||||
>
|
||||
{truncateText(workspace.name, 18)}
|
||||
</h5>
|
||||
</div>
|
||||
<span className="p-1">
|
||||
<CheckIcon
|
||||
className={`h-3 w-3.5 text-custom-sidebar-text-100 ${
|
||||
active || workspace.id === activeWorkspace?.id
|
||||
? "opacity-100"
|
||||
: "opacity-0"
|
||||
}`}
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
</Menu.Item>
|
||||
))
|
||||
) : (
|
||||
<p>No workspace found!</p>
|
||||
)}
|
||||
<Menu.Item
|
||||
as="button"
|
||||
type="button"
|
||||
onClick={() => {
|
||||
router.push("/create-workspace");
|
||||
}}
|
||||
className="flex w-full items-center gap-1 text-sm text-custom-sidebar-text-200"
|
||||
>
|
||||
<PlusIcon className="h-3 w-3" />
|
||||
Create Workspace
|
||||
</Menu.Item>
|
||||
</div>
|
||||
<div className="relative grid h-6 w-6 place-items-center rounded bg-gray-700 uppercase text-white">
|
||||
{activeWorkspace?.logo && activeWorkspace.logo !== "" ? (
|
||||
<img
|
||||
src={activeWorkspace.logo}
|
||||
className="absolute top-0 left-0 h-full w-full object-cover rounded"
|
||||
alt="Workspace Logo"
|
||||
/>
|
||||
) : (
|
||||
<div className="w-full">
|
||||
<Loader className="space-y-2">
|
||||
<Loader.Item height="30px" />
|
||||
<Loader.Item height="30px" />
|
||||
</Loader>
|
||||
</div>
|
||||
activeWorkspace?.name?.charAt(0) ?? "..."
|
||||
)}
|
||||
</div>
|
||||
<div className="flex w-full flex-col items-start justify-start gap-2 border-t border-custom-sidebar-border-100 px-3 py-2 text-sm">
|
||||
{userLinks(workspaceSlug as string).map((link, index) => (
|
||||
|
||||
{!sidebarCollapse && (
|
||||
<h4 className="text-custom-text-100">
|
||||
{activeWorkspace?.name ? truncateText(activeWorkspace.name, 14) : "Loading..."}
|
||||
</h4>
|
||||
)}
|
||||
</div>
|
||||
</Menu.Button>
|
||||
|
||||
{!sidebarCollapse && (
|
||||
<Link href={`/${workspaceSlug}/me/profile`}>
|
||||
<a>
|
||||
<div className="flex flex-grow justify-end">
|
||||
<Avatar user={user} height="28px" width="28px" fontSize="14px" />
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Transition
|
||||
as={Fragment}
|
||||
enter="transition ease-out duration-100"
|
||||
enterFrom="transform opacity-0 scale-95"
|
||||
enterTo="transform opacity-100 scale-100"
|
||||
leave="transition ease-in duration-75"
|
||||
leaveFrom="transform opacity-100 scale-100"
|
||||
leaveTo="transform opacity-0 scale-95"
|
||||
>
|
||||
<Menu.Items
|
||||
className="fixed left-2 z-20 mt-1 flex w-full max-w-[17rem] origin-top-left flex-col rounded-md
|
||||
border border-custom-sidebar-border-100 bg-custom-sidebar-background-90 shadow-lg focus:outline-none"
|
||||
>
|
||||
<div className="flex flex-col items-start justify-start gap-3 p-3">
|
||||
<div className="text-sm text-custom-sidebar-text-200">{user?.email}</div>
|
||||
<span className="text-sm font-semibold text-custom-sidebar-text-200">Workspace</span>
|
||||
{workspaces ? (
|
||||
<div className="flex h-full w-full flex-col items-start justify-start gap-3.5">
|
||||
{workspaces.length > 0 ? (
|
||||
workspaces.map((workspace) => (
|
||||
<Menu.Item key={workspace.id}>
|
||||
{({ active }) => (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => handleWorkspaceNavigation(workspace)}
|
||||
className="flex w-full items-center justify-between gap-1 rounded-md text-sm text-custom-sidebar-text-100"
|
||||
>
|
||||
<div className="flex items-center justify-start gap-2.5">
|
||||
<span className="relative flex h-6 w-6 items-center justify-center rounded bg-gray-700 p-2 text-xs uppercase text-white">
|
||||
{workspace?.logo && workspace.logo !== "" ? (
|
||||
<img
|
||||
src={workspace.logo}
|
||||
className="absolute top-0 left-0 h-full w-full object-cover rounded"
|
||||
alt="Workspace Logo"
|
||||
/>
|
||||
) : (
|
||||
workspace?.name?.charAt(0) ?? "..."
|
||||
)}
|
||||
</span>
|
||||
|
||||
<h5
|
||||
className={`text-sm ${
|
||||
workspaceSlug === workspace.slug ? "" : "text-custom-text-200"
|
||||
}`}
|
||||
>
|
||||
{truncateText(workspace.name, 18)}
|
||||
</h5>
|
||||
</div>
|
||||
<span className="p-1">
|
||||
<CheckIcon
|
||||
className={`h-3 w-3.5 text-custom-sidebar-text-100 ${
|
||||
active || workspace.id === activeWorkspace?.id
|
||||
? "opacity-100"
|
||||
: "opacity-0"
|
||||
}`}
|
||||
/>
|
||||
</span>
|
||||
</button>
|
||||
)}
|
||||
</Menu.Item>
|
||||
))
|
||||
) : (
|
||||
<p>No workspace found!</p>
|
||||
)}
|
||||
<Menu.Item
|
||||
key={index}
|
||||
as="div"
|
||||
className="flex w-full items-center justify-start rounded px-2 py-1 text-sm text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-80"
|
||||
as="button"
|
||||
type="button"
|
||||
onClick={() => {
|
||||
router.push("/create-workspace");
|
||||
}}
|
||||
className="flex w-full items-center gap-1 text-sm text-custom-sidebar-text-200"
|
||||
>
|
||||
<Link href={link.href}>
|
||||
<a className="w-full">{link.name}</a>
|
||||
</Link>
|
||||
<PlusIcon className="h-3 w-3" />
|
||||
Create Workspace
|
||||
</Menu.Item>
|
||||
))}
|
||||
</div>
|
||||
<div className="w-full border-t border-t-custom-sidebar-border-100 px-3 py-2">
|
||||
</div>
|
||||
) : (
|
||||
<div className="w-full">
|
||||
<Loader className="space-y-2">
|
||||
<Loader.Item height="30px" />
|
||||
<Loader.Item height="30px" />
|
||||
</Loader>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex w-full flex-col items-start justify-start gap-2 border-t border-custom-sidebar-border-100 px-3 py-2 text-sm">
|
||||
{userLinks(workspaceSlug as string).map((link, index) => (
|
||||
<Menu.Item
|
||||
as="button"
|
||||
type="button"
|
||||
className="flex w-full items-center justify-start rounded px-2 py-1 text-sm text-red-600 hover:bg-custom-sidebar-background-80"
|
||||
onClick={handleSignOut}
|
||||
key={index}
|
||||
as="div"
|
||||
className="flex w-full items-center justify-start rounded px-2 py-1 text-sm text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-80"
|
||||
>
|
||||
Sign out
|
||||
<Link href={link.href}>
|
||||
<a className="w-full">{link.name}</a>
|
||||
</Link>
|
||||
</Menu.Item>
|
||||
</div>
|
||||
</Menu.Items>
|
||||
</Transition>
|
||||
</Menu>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<div className="w-full border-t border-t-custom-sidebar-border-100 px-3 py-2">
|
||||
<Menu.Item
|
||||
as="button"
|
||||
type="button"
|
||||
className="flex w-full items-center justify-start rounded px-2 py-1 text-sm text-red-600 hover:bg-custom-sidebar-background-80"
|
||||
onClick={handleSignOut}
|
||||
>
|
||||
Sign out
|
||||
</Menu.Item>
|
||||
</div>
|
||||
</Menu.Items>
|
||||
</Transition>
|
||||
</Menu>
|
||||
);
|
||||
};
|
||||
|
@ -5,9 +5,8 @@ import Link from "next/link";
|
||||
|
||||
// hooks
|
||||
import useTheme from "hooks/use-theme";
|
||||
// icons
|
||||
import { ChartBarIcon } from "@heroicons/react/24/outline";
|
||||
import { GridViewIcon, AssignmentClipboardIcon, TickMarkIcon, SettingIcon } from "components/icons";
|
||||
// components
|
||||
import { Icon, Tooltip } from "components/ui";
|
||||
|
||||
export const WorkspaceSidebarMenu = () => {
|
||||
const router = useRouter();
|
||||
@ -18,34 +17,34 @@ export const WorkspaceSidebarMenu = () => {
|
||||
|
||||
const workspaceLinks = (workspaceSlug: string) => [
|
||||
{
|
||||
icon: GridViewIcon,
|
||||
icon: "grid_view",
|
||||
name: "Dashboard",
|
||||
href: `/${workspaceSlug}`,
|
||||
},
|
||||
{
|
||||
icon: ChartBarIcon,
|
||||
icon: "bar_chart",
|
||||
name: "Analytics",
|
||||
href: `/${workspaceSlug}/analytics`,
|
||||
},
|
||||
{
|
||||
icon: AssignmentClipboardIcon,
|
||||
icon: "work",
|
||||
name: "Projects",
|
||||
href: `/${workspaceSlug}/projects`,
|
||||
},
|
||||
{
|
||||
icon: TickMarkIcon,
|
||||
icon: "task_alt",
|
||||
name: "My Issues",
|
||||
href: `/${workspaceSlug}/me/my-issues`,
|
||||
},
|
||||
{
|
||||
icon: SettingIcon,
|
||||
icon: "settings",
|
||||
name: "Settings",
|
||||
href: `/${workspaceSlug}/settings`,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<div className="flex w-full flex-col items-start justify-start gap-2 px-3 py-1">
|
||||
<div className="w-full cursor-pointer space-y-2 px-4 mt-5">
|
||||
{workspaceLinks(workspaceSlug as string).map((link, index) => {
|
||||
const isActive =
|
||||
link.name === "Settings"
|
||||
@ -54,28 +53,24 @@ export const WorkspaceSidebarMenu = () => {
|
||||
|
||||
return (
|
||||
<Link key={index} href={link.href}>
|
||||
<a
|
||||
className={`${
|
||||
isActive
|
||||
? "bg-custom-sidebar-background-90 text-custom-sidebar-text-100"
|
||||
: "text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-90 focus:bg-custom-sidebar-background-90"
|
||||
} group flex w-full items-center gap-3 rounded-md p-2 text-sm font-medium outline-none ${
|
||||
sidebarCollapse ? "justify-center" : ""
|
||||
}`}
|
||||
>
|
||||
<span className="grid h-5 w-5 flex-shrink-0 place-items-center">
|
||||
<link.icon
|
||||
color={
|
||||
<a className="block w-full">
|
||||
<Tooltip
|
||||
tooltipContent={link.name}
|
||||
position="right"
|
||||
className="ml-2"
|
||||
disabled={!sidebarCollapse}
|
||||
>
|
||||
<div
|
||||
className={`group flex w-full items-center gap-2.5 rounded-md px-3 py-2 text-sm font-medium outline-none ${
|
||||
isActive
|
||||
? "rgb(var(--color-sidebar-text-100))"
|
||||
: "rgb(var(--color-sidebar-text-200))"
|
||||
}
|
||||
aria-hidden="true"
|
||||
height="20"
|
||||
width="20"
|
||||
/>
|
||||
</span>
|
||||
{!sidebarCollapse && link.name}
|
||||
? "bg-custom-primary-100/10 text-custom-primary-100"
|
||||
: "text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-80 focus:bg-custom-sidebar-background-80"
|
||||
} ${sidebarCollapse ? "justify-center" : ""}`}
|
||||
>
|
||||
<Icon iconName={`${link.icon}`} />
|
||||
{!sidebarCollapse && link.name}
|
||||
</div>
|
||||
</Tooltip>
|
||||
</a>
|
||||
</Link>
|
||||
);
|
||||
|
@ -19,13 +19,11 @@ const Sidebar: React.FC<SidebarProps> = ({ toggleSidebar, setToggleSidebar }) =>
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`z-20 h-full flex-shrink-0 border-r border-custom-sidebar-border-100 ${
|
||||
sidebarCollapse ? "" : "w-auto md:w-[17rem]"
|
||||
} fixed inset-y-0 top-0 ${
|
||||
toggleSidebar ? "left-0" : "-left-full md:left-0"
|
||||
} flex h-full flex-col bg-custom-sidebar-background-100 duration-300 md:relative`}
|
||||
className={`fixed md:relative inset-y-0 flex flex-col bg-custom-sidebar-background-100 h-full flex-shrink-0 flex-grow-0 border-r border-custom-sidebar-border-100 z-20 duration-300 ${
|
||||
sidebarCollapse ? "" : "md:w-72"
|
||||
} ${toggleSidebar ? "left-0" : "-left-full md:left-0"}`}
|
||||
>
|
||||
<div className="flex h-full flex-1 flex-col">
|
||||
<div className="flex h-full w-full flex-1 flex-col">
|
||||
<WorkspaceSidebarDropdown />
|
||||
<WorkspaceSidebarMenu />
|
||||
<ProjectSidebarList />
|
||||
|
2
apps/app/types/workspace.d.ts
vendored
2
apps/app/types/workspace.d.ts
vendored
@ -14,7 +14,7 @@ export interface IWorkspace {
|
||||
readonly created_by: string;
|
||||
readonly updated_by: string;
|
||||
organization_size: string;
|
||||
total_issues: number | null;
|
||||
total_issues: number;
|
||||
}
|
||||
|
||||
export interface IWorkspaceLite {
|
||||
|
Loading…
Reference in New Issue
Block a user