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:
Anmol Singh Bhatia 2023-07-13 18:35:43 +05:30 committed by GitHub
parent 3f3fb373cc
commit 60e96bcb72
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 305 additions and 329 deletions

View File

@ -70,8 +70,7 @@ const TOUR_STEPS: {
{ {
key: "pages", key: "pages",
title: "Document with pages", title: "Document with pages",
description: description: "Use Pages to quickly jot down issues when you're in a meeting or starting a day.",
"Modules break your big think into Projects or Features, to help you organize better.",
image: PagesTour, image: PagesTour,
prevStep: "views", prevStep: "views",
}, },

View File

@ -151,11 +151,11 @@ export const ProjectSidebarList: FC = () => {
data={projectToDelete} data={projectToDelete}
user={user} 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 && ( {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 && ( {!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) => { {favoriteProjects.map((favoriteProject) => {
const project = favoriteProject.project_detail; const project = favoriteProject.project_detail;
@ -173,9 +173,9 @@ export const ProjectSidebarList: FC = () => {
})} })}
</div> </div>
)} )}
<div className="flex flex-col space-y-2 p-3"> <div className="flex flex-col space-y-2 mt-5">
{!sidebarCollapse && ( {!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 ? ( {projects ? (
<> <>

View File

@ -4,22 +4,9 @@ import { useRouter } from "next/router";
// headless ui // headless ui
import { Disclosure, Transition } from "@headlessui/react"; import { Disclosure, Transition } from "@headlessui/react";
// ui // ui
import { CustomMenu, Icon } from "components/ui"; import { CustomMenu, Icon, Tooltip } from "components/ui";
// icons // icons
import { import { LinkIcon, StarIcon, TrashIcon } from "@heroicons/react/24/outline";
ChevronDownIcon,
DocumentTextIcon,
LinkIcon,
StarIcon,
TrashIcon,
} from "@heroicons/react/24/outline";
import {
ContrastIcon,
LayerDiagonalIcon,
PeopleGroupIcon,
SettingIcon,
ViewListIcon,
} from "components/icons";
// helpers // helpers
import { truncateText } from "helpers/string.helper"; import { truncateText } from "helpers/string.helper";
import { renderEmoji } from "helpers/emoji.helper"; import { renderEmoji } from "helpers/emoji.helper";
@ -39,32 +26,32 @@ const navigation = (workspaceSlug: string, projectId: string) => [
{ {
name: "Issues", name: "Issues",
href: `/${workspaceSlug}/projects/${projectId}/issues`, href: `/${workspaceSlug}/projects/${projectId}/issues`,
icon: LayerDiagonalIcon, icon: "stack",
}, },
{ {
name: "Cycles", name: "Cycles",
href: `/${workspaceSlug}/projects/${projectId}/cycles`, href: `/${workspaceSlug}/projects/${projectId}/cycles`,
icon: ContrastIcon, icon: "contrast",
}, },
{ {
name: "Modules", name: "Modules",
href: `/${workspaceSlug}/projects/${projectId}/modules`, href: `/${workspaceSlug}/projects/${projectId}/modules`,
icon: PeopleGroupIcon, icon: "dataset",
}, },
{ {
name: "Views", name: "Views",
href: `/${workspaceSlug}/projects/${projectId}/views`, href: `/${workspaceSlug}/projects/${projectId}/views`,
icon: ViewListIcon, icon: "photo_filter",
}, },
{ {
name: "Pages", name: "Pages",
href: `/${workspaceSlug}/projects/${projectId}/pages`, href: `/${workspaceSlug}/projects/${projectId}/pages`,
icon: DocumentTextIcon, icon: "article",
}, },
{ {
name: "Settings", name: "Settings",
href: `/${workspaceSlug}/projects/${projectId}/settings`, href: `/${workspaceSlug}/projects/${projectId}/settings`,
icon: SettingIcon, icon: "settings",
}, },
]; ];
@ -84,44 +71,56 @@ export const SingleSidebarProject: React.FC<Props> = ({
{({ open }) => ( {({ open }) => (
<> <>
<div className="flex items-center gap-x-1 text-custom-sidebar-text-100"> <div className="flex items-center gap-x-1 text-custom-sidebar-text-100">
<Disclosure.Button <Tooltip
as="div" tooltipContent={`${project?.name}`}
className={`flex w-full cursor-pointer select-none items-center rounded-md py-2 text-left text-sm font-medium ${ position="right"
sidebarCollapse ? "justify-center" : "justify-between" className="ml-2"
}`} disabled={!sidebarCollapse}
> >
<div className="flex items-center gap-x-2"> <Disclosure.Button
{project.emoji ? ( as="div"
<span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded uppercase"> className={`flex w-full cursor-pointer select-none items-center rounded-sm py-1 text-left text-sm font-medium ${
{renderEmoji(project.emoji)} sidebarCollapse ? "justify-center" : "justify-between"
</span> }`}
) : project.icon_prop ? ( >
<div className="h-7 w-7 grid place-items-center"> <div className="flex items-center gap-x-2">
<span {project.emoji ? (
style={{ color: project.icon_prop.color }} <span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded uppercase">
className="material-symbols-rounded text-lg" {renderEmoji(project.emoji)}
>
{project.icon_prop.name}
</span> </span>
</div> ) : project.icon_prop ? (
) : ( <div className="h-7 w-7 grid place-items-center">
<span className="grid h-7 w-7 flex-shrink-0 place-items-center rounded bg-gray-700 uppercase text-white"> <span
{project?.name.charAt(0)} style={{ color: project.icon_prop.color }}
</span> 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 && ( {!sidebarCollapse && (
<h5 className={`overflow-hidden text-sm ${open ? "" : "text-custom-text-200"}`}> <Icon
{truncateText(project?.name, 20)} iconName="expand_more"
</h5> className={`${open ? "rotate-180" : ""} text-custom-text-200 duration-300`}
/>
)} )}
</div> </Disclosure.Button>
{!sidebarCollapse && ( </Tooltip>
<span>
<ChevronDownIcon className={`h-4 w-4 duration-300 ${open ? "rotate-180" : ""}`} />
</span>
)}
</Disclosure.Button>
{!sidebarCollapse && ( {!sidebarCollapse && (
<CustomMenu ellipsis> <CustomMenu ellipsis>
@ -177,9 +176,7 @@ export const SingleSidebarProject: React.FC<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 <Disclosure.Panel className={`space-y-2 ${sidebarCollapse ? "" : "ml-[2.25rem]"}`}>
className={`${sidebarCollapse ? "" : "ml-[2.25rem]"} flex flex-col gap-y-1`}
>
{navigation(workspaceSlug as string, project?.id).map((item) => { {navigation(workspaceSlug as string, project?.id).map((item) => {
if ( if (
(item.name === "Cycles" && !project.cycle_view) || (item.name === "Cycles" && !project.cycle_view) ||
@ -191,25 +188,24 @@ export const SingleSidebarProject: React.FC<Props> = ({
return ( return (
<Link key={item.name} href={item.href}> <Link key={item.name} href={item.href}>
<a <a className="block w-full">
className={`group flex items-center rounded-md p-2 text-xs font-medium outline-none ${ <Tooltip
router.asPath.includes(item.href) tooltipContent={`${project?.name}: ${item.name}`}
? "bg-custom-sidebar-background-90 text-custom-sidebar-text-100" position="right"
: "text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-90 focus:bg-custom-sidebar-background-90" className="ml-2"
} ${sidebarCollapse ? "justify-center" : ""}`} disabled={!sidebarCollapse}
> >
<div className="grid place-items-center"> <div
<item.icon className={`group flex items-center rounded-md px-2 py-1.5 gap-2 text-xs font-medium outline-none ${
className={`h-5 w-5 flex-shrink-0 ${!sidebarCollapse ? "mr-3" : ""}`}
color={
router.asPath.includes(item.href) router.asPath.includes(item.href)
? "rgb(var(--color-sidebar-text-100))" ? "bg-custom-primary-100/10 text-custom-primary-100"
: "rgb(var(--color-sidebar-text-200))" : "text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-80 focus:bg-custom-sidebar-background-80"
} } ${sidebarCollapse ? "justify-center" : ""}`}
aria-hidden="true" >
/> <Icon iconName={item.icon} />
</div> {!sidebarCollapse && item.name}
{!sidebarCollapse && item.name} </div>
</Tooltip>
</a> </a>
</Link> </Link>
); );

View File

@ -5,6 +5,7 @@ import Link from "next/link";
import { Menu, Transition } from "@headlessui/react"; import { Menu, Transition } from "@headlessui/react";
// icons // icons
import { ChevronDownIcon, EllipsisHorizontalIcon } from "@heroicons/react/24/outline"; import { ChevronDownIcon, EllipsisHorizontalIcon } from "@heroicons/react/24/outline";
import { Icon } from "./icon";
type Props = { type Props = {
children: React.ReactNode; children: React.ReactNode;
@ -61,8 +62,9 @@ const CustomMenu = ({
type="button" type="button"
className="relative grid place-items-center rounded p-1 text-custom-text-200 hover:bg-custom-background-80 outline-none" className="relative grid place-items-center rounded p-1 text-custom-text-200 hover:bg-custom-background-80 outline-none"
> >
<EllipsisHorizontalIcon <Icon
className={`h-4 w-4 ${verticalEllipsis ? "rotate-90" : ""}`} iconName="more_horiz"
className={`${verticalEllipsis ? "rotate-90" : ""} text-brand-secondary`}
/> />
</Menu.Button> </Menu.Button>
) : ( ) : (

View File

@ -1,4 +1,4 @@
import { useState, useRef, FC } from "react"; import React, { useRef, useState } from "react";
import { useRouter } from "next/router"; import { useRouter } from "next/router";
@ -8,9 +8,16 @@ import useSWR from "swr";
// headless ui // headless ui
import { Transition } from "@headlessui/react"; import { Transition } from "@headlessui/react";
// services
import workspaceService from "services/workspace.service";
// hooks // hooks
import useTheme from "hooks/use-theme"; import useTheme from "hooks/use-theme";
import useUser from "hooks/use-user";
import useOutsideClickDetector from "hooks/use-outside-click-detector"; import useOutsideClickDetector from "hooks/use-outside-click-detector";
// components
import UpgradeToProModal from "./upgrade-to-pro-modal";
// ui
import { CircularProgress } from "components/ui";
// icons // icons
import { import {
ArrowLongLeftIcon, ArrowLongLeftIcon,
@ -20,15 +27,8 @@ import {
XMarkIcon, XMarkIcon,
} from "@heroicons/react/24/outline"; } from "@heroicons/react/24/outline";
import { QuestionMarkCircleIcon, DocumentIcon, DiscordIcon, GithubIcon } from "components/icons"; import { QuestionMarkCircleIcon, DocumentIcon, DiscordIcon, GithubIcon } from "components/icons";
// services
import workspaceService from "services/workspace.service";
// fetch-keys // fetch-keys
import { WORKSPACE_DETAILS } from "constants/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 = [ const helpOptions = [
{ {
@ -58,36 +58,28 @@ export interface WorkspaceHelpSectionProps {
setSidebarActive: React.Dispatch<React.SetStateAction<boolean>>; setSidebarActive: React.Dispatch<React.SetStateAction<boolean>>;
} }
type progress = { export const WorkspaceHelpSection: React.FC<WorkspaceHelpSectionProps> = ({ setSidebarActive }) => {
progress: number; const [alert, setAlert] = useState(false);
}; const [isNeedHelpOpen, setIsNeedHelpOpen] = useState(false);
export const WorkspaceHelpSection: FC<WorkspaceHelpSectionProps> = (props) => {
// router const helpOptionsRef = useRef<HTMLDivElement | null>(null);
const router = useRouter(); const router = useRouter();
const { workspaceSlug } = router.query; const { workspaceSlug } = router.query;
const { setSidebarActive } = props;
// theme
const { collapsed: sidebarCollapse, toggleCollapsed } = useTheme(); const { collapsed: sidebarCollapse, toggleCollapsed } = useTheme();
// states
const [isNeedHelpOpen, setIsNeedHelpOpen] = useState(false);
// refs
const helpOptionsRef = useRef<HTMLDivElement | null>(null);
// hooks
useOutsideClickDetector(helpOptionsRef, () => setIsNeedHelpOpen(false)); useOutsideClickDetector(helpOptionsRef, () => setIsNeedHelpOpen(false));
const { user } = useUser(); const { user } = useUser();
const helpOptionMode = sidebarCollapse ? "left-full" : "left-[-75px]";
const [alert, setAlert] = useState(false);
const [upgradeModal, setUpgradeModal] = useState(false); const [upgradeModal, setUpgradeModal] = useState(false);
const { data: workspaceDetails } = useSWR( const { data: workspaceDetails } = useSWR(
workspaceSlug ? WORKSPACE_DETAILS(workspaceSlug as string) : null, workspaceSlug ? WORKSPACE_DETAILS(workspaceSlug as string) : null,
workspaceSlug ? () => workspaceService.getWorkspace(workspaceSlug as string) : null workspaceSlug ? () => workspaceService.getWorkspace(workspaceSlug as string) : null
); );
const issueNumber = workspaceDetails?.total_issues || 0; const issueNumber = workspaceDetails?.total_issues || 0;
return ( return (
@ -98,61 +90,58 @@ export const WorkspaceHelpSection: FC<WorkspaceHelpSectionProps> = (props) => {
user={user} user={user}
issueNumber={issueNumber} issueNumber={issueNumber}
/> />
{!sidebarCollapse && (alert || (issueNumber && issueNumber >= 750)) ? ( {!sidebarCollapse && (alert || issueNumber >= 750) && (
<> <div
<div className={`border-t border-custom-sidebar-border-100 p-4 ${
className={`border-t p-4 ${ issueNumber >= 750
issueNumber >= 750 ? "bg-red-500/10 text-red-600"
? "bg-red-50 text-red-600 border-red-200" : issueNumber >= 500
: issueNumber >= 500 ? "bg-yellow-500/10 text-yellow-600"
? "bg-yellow-50 text-yellow-600 border-yellow-200" : "text-green-600"
: "text-green-600" }`}
}`} >
> <div className="flex items-center gap-2">
<div className="flex items-center gap-2 w-full"> <CircularProgress progress={(issueNumber / 1024) * 100} />
<CircularProgress progress={(issueNumber / 1024) * 100} /> <div>Free Plan</div>
<div className="">Free Plan</div> {issueNumber < 750 && (
{issueNumber < 750 && ( <div
<div className="ml-auto text-custom-text-200" onClick={() => setAlert(false)}> className="ml-auto text-custom-text-200 cursor-pointer"
<XMarkIcon className="h-4 w-4" /> onClick={() => setAlert(false)}
</div> >
)} <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>
</> <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 <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" : "" sidebarCollapse ? "flex-col" : ""
}`} }`}
> >
{alert || (issueNumber && issueNumber >= 750) ? ( {alert || issueNumber >= 750 ? (
<button <button
type="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 issueNumber >= 750
? "bg-custom-primary-100 text-white" ? "bg-red-500/10 text-red-500"
: "bg-blue-500/10 text-custom-primary-100" : "bg-blue-500/10 text-custom-primary-100"
} } ${sidebarCollapse ? "w-full justify-center" : ""}`}
${sidebarCollapse ? "w-full justify-center" : ""}`}
title="Shortcuts" title="Shortcuts"
onClick={() => setUpgradeModal(true)} onClick={() => setUpgradeModal(true)}
> >
<ArrowUpCircleIcon className="h-4 w-4 " /> <ArrowUpCircleIcon className="h-4 w-4" />
{!sidebarCollapse && <span> Learn more</span>} {!sidebarCollapse && <span>Learn more</span>}
</button> </button>
) : ( ) : (
<button <button
type="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 issueNumber >= 750
? "bg-red-500/10 text-red-600" ? "bg-red-500/10 text-red-600"
: issueNumber >= 500 : issueNumber >= 500
@ -169,7 +158,6 @@ export const WorkspaceHelpSection: FC<WorkspaceHelpSectionProps> = (props) => {
{!sidebarCollapse && <span>Free Plan</span>} {!sidebarCollapse && <span>Free Plan</span>}
</button> </button>
)} )}
<button <button
type="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 ${ 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" title="Shortcuts"
> >
<RocketLaunchIcon className="h-4 w-4 text-custom-text-200" /> <RocketLaunchIcon className="h-4 w-4 text-custom-text-200" />
{/* {!sidebarCollapse && <span>Shortcuts</span>} */}
</button> </button>
<button <button
type="button" type="button"
@ -195,7 +182,6 @@ export const WorkspaceHelpSection: FC<WorkspaceHelpSectionProps> = (props) => {
title="Help" title="Help"
> >
<QuestionMarkCircleIcon className="h-4 w-4 text-custom-text-200" /> <QuestionMarkCircleIcon className="h-4 w-4 text-custom-text-200" />
{/* {!sidebarCollapse && <span>Help</span>} */}
</button> </button>
<button <button
type="button" type="button"
@ -229,7 +215,9 @@ export const WorkspaceHelpSection: FC<WorkspaceHelpSectionProps> = (props) => {
leaveTo="transform opacity-0 scale-95" leaveTo="transform opacity-0 scale-95"
> >
<div <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} ref={helpOptionsRef}
> >
{helpOptions.map(({ name, Icon, href, onClick }) => { {helpOptions.map(({ name, Icon, href, onClick }) => {

View File

@ -86,157 +86,155 @@ export const WorkspaceSidebarDropdown = () => {
}; };
return ( return (
<div className="relative"> <Menu as="div" className="relative col-span-4 inline-block w-full px-4 pt-4 text-left">
<Menu as="div" className="col-span-4 inline-block w-full p-3 text-left"> <div className="flex items-center justify-between gap-2">
<div className="flex items-center justify-between gap-2.5"> <Menu.Button className="text-custom-sidebar-text-200 flex w-full items-center rounded-sm text-sm font-medium focus:outline-none">
<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
<div className={`flex w-full items-center gap-x-2 rounded-sm bg-custom-sidebar-background-80 p-1 ${
className={`flex w-full items-center gap-x-2 rounded-md bg-custom-sidebar-background-80 px-2 py-1.5 ${ sidebarCollapse ? "justify-center" : ""
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"
> >
<div className="flex flex-col items-start justify-start gap-3 p-3"> <div className="relative grid h-6 w-6 place-items-center rounded bg-gray-700 uppercase text-white">
<div className="text-sm text-custom-sidebar-text-200">{user?.email}</div> {activeWorkspace?.logo && activeWorkspace.logo !== "" ? (
<span className="text-sm font-semibold text-custom-sidebar-text-200">Workspace</span> <img
{workspaces ? ( src={activeWorkspace.logo}
<div className="flex h-full w-full flex-col items-start justify-start gap-3.5"> className="absolute top-0 left-0 h-full w-full object-cover rounded"
{workspaces.length > 0 ? ( alt="Workspace Logo"
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="w-full"> activeWorkspace?.name?.charAt(0) ?? "..."
<Loader className="space-y-2">
<Loader.Item height="30px" />
<Loader.Item height="30px" />
</Loader>
</div>
)} )}
</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) => ( {!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 <Menu.Item
key={index} as="button"
as="div" type="button"
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" onClick={() => {
router.push("/create-workspace");
}}
className="flex w-full items-center gap-1 text-sm text-custom-sidebar-text-200"
> >
<Link href={link.href}> <PlusIcon className="h-3 w-3" />
<a className="w-full">{link.name}</a> Create Workspace
</Link>
</Menu.Item> </Menu.Item>
))} </div>
</div> ) : (
<div className="w-full border-t border-t-custom-sidebar-border-100 px-3 py-2"> <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 <Menu.Item
as="button" key={index}
type="button" as="div"
className="flex w-full items-center justify-start rounded px-2 py-1 text-sm text-red-600 hover:bg-custom-sidebar-background-80" 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"
onClick={handleSignOut}
> >
Sign out <Link href={link.href}>
<a className="w-full">{link.name}</a>
</Link>
</Menu.Item> </Menu.Item>
</div> ))}
</Menu.Items> </div>
</Transition> <div className="w-full border-t border-t-custom-sidebar-border-100 px-3 py-2">
</Menu> <Menu.Item
</div> 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>
); );
}; };

View File

@ -5,9 +5,8 @@ import Link from "next/link";
// hooks // hooks
import useTheme from "hooks/use-theme"; import useTheme from "hooks/use-theme";
// icons // components
import { ChartBarIcon } from "@heroicons/react/24/outline"; import { Icon, Tooltip } from "components/ui";
import { GridViewIcon, AssignmentClipboardIcon, TickMarkIcon, SettingIcon } from "components/icons";
export const WorkspaceSidebarMenu = () => { export const WorkspaceSidebarMenu = () => {
const router = useRouter(); const router = useRouter();
@ -18,34 +17,34 @@ export const WorkspaceSidebarMenu = () => {
const workspaceLinks = (workspaceSlug: string) => [ const workspaceLinks = (workspaceSlug: string) => [
{ {
icon: GridViewIcon, icon: "grid_view",
name: "Dashboard", name: "Dashboard",
href: `/${workspaceSlug}`, href: `/${workspaceSlug}`,
}, },
{ {
icon: ChartBarIcon, icon: "bar_chart",
name: "Analytics", name: "Analytics",
href: `/${workspaceSlug}/analytics`, href: `/${workspaceSlug}/analytics`,
}, },
{ {
icon: AssignmentClipboardIcon, icon: "work",
name: "Projects", name: "Projects",
href: `/${workspaceSlug}/projects`, href: `/${workspaceSlug}/projects`,
}, },
{ {
icon: TickMarkIcon, icon: "task_alt",
name: "My Issues", name: "My Issues",
href: `/${workspaceSlug}/me/my-issues`, href: `/${workspaceSlug}/me/my-issues`,
}, },
{ {
icon: SettingIcon, icon: "settings",
name: "Settings", name: "Settings",
href: `/${workspaceSlug}/settings`, href: `/${workspaceSlug}/settings`,
}, },
]; ];
return ( 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) => { {workspaceLinks(workspaceSlug as string).map((link, index) => {
const isActive = const isActive =
link.name === "Settings" link.name === "Settings"
@ -54,28 +53,24 @@ export const WorkspaceSidebarMenu = () => {
return ( return (
<Link key={index} href={link.href}> <Link key={index} href={link.href}>
<a <a className="block w-full">
className={`${ <Tooltip
isActive tooltipContent={link.name}
? "bg-custom-sidebar-background-90 text-custom-sidebar-text-100" position="right"
: "text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-90 focus:bg-custom-sidebar-background-90" className="ml-2"
} group flex w-full items-center gap-3 rounded-md p-2 text-sm font-medium outline-none ${ disabled={!sidebarCollapse}
sidebarCollapse ? "justify-center" : "" >
}`} <div
> className={`group flex w-full items-center gap-2.5 rounded-md px-3 py-2 text-sm font-medium outline-none ${
<span className="grid h-5 w-5 flex-shrink-0 place-items-center">
<link.icon
color={
isActive isActive
? "rgb(var(--color-sidebar-text-100))" ? "bg-custom-primary-100/10 text-custom-primary-100"
: "rgb(var(--color-sidebar-text-200))" : "text-custom-sidebar-text-200 hover:bg-custom-sidebar-background-80 focus:bg-custom-sidebar-background-80"
} } ${sidebarCollapse ? "justify-center" : ""}`}
aria-hidden="true" >
height="20" <Icon iconName={`${link.icon}`} />
width="20" {!sidebarCollapse && link.name}
/> </div>
</span> </Tooltip>
{!sidebarCollapse && link.name}
</a> </a>
</Link> </Link>
); );

View File

@ -19,13 +19,11 @@ const Sidebar: React.FC<SidebarProps> = ({ toggleSidebar, setToggleSidebar }) =>
return ( return (
<div <div
className={`z-20 h-full flex-shrink-0 border-r border-custom-sidebar-border-100 ${ 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 ? "" : "w-auto md:w-[17rem]" sidebarCollapse ? "" : "md:w-72"
} fixed inset-y-0 top-0 ${ } ${toggleSidebar ? "left-0" : "-left-full md:left-0"}`}
toggleSidebar ? "left-0" : "-left-full md:left-0"
} flex h-full flex-col bg-custom-sidebar-background-100 duration-300 md:relative`}
> >
<div className="flex h-full flex-1 flex-col"> <div className="flex h-full w-full flex-1 flex-col">
<WorkspaceSidebarDropdown /> <WorkspaceSidebarDropdown />
<WorkspaceSidebarMenu /> <WorkspaceSidebarMenu />
<ProjectSidebarList /> <ProjectSidebarList />

View File

@ -14,7 +14,7 @@ export interface IWorkspace {
readonly created_by: string; readonly created_by: string;
readonly updated_by: string; readonly updated_by: string;
organization_size: string; organization_size: string;
total_issues: number | null; total_issues: number;
} }
export interface IWorkspaceLite { export interface IWorkspaceLite {