chore: language support phase 2 (#6323)

* fix: adding langauge support for sidebar items

* fix: worksapce sidebar item refactor

* chore: code cleanup

---------

Co-authored-by: sriram veeraghanta <veeraghanta.sriram@gmail.com>
This commit is contained in:
Prateek Shourya 2025-01-06 15:32:11 +05:30 committed by GitHub
parent 732963b591
commit a6216beb7e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
21 changed files with 431 additions and 356 deletions

View file

@ -6,4 +6,7 @@ export * from "./projects-list";
export * from "./project-navigation";
export * from "./quick-actions";
export * from "./user-menu";
export * from "./user-menu-item";
export * from "./workspace-menu";
export * from "./workspace-menu-item";
export * from "./workspace-menu-header";

View file

@ -0,0 +1,84 @@
import { FC } from "react";
import { observer } from "mobx-react";
import Link from "next/link";
import { useParams, usePathname } from "next/navigation";
// plane imports
import { EUserPermissionsLevel, SIDEBAR_CLICKED, EUserWorkspaceRoles } from "@plane/constants";
import { usePlatformOS } from "@plane/hooks";
import { useTranslation } from "@plane/i18n";
import { Tooltip } from "@plane/ui";
// components
import { SidebarNavItem } from "@/components/sidebar";
import { NotificationAppSidebarOption } from "@/components/workspace-notifications";
// hooks
import { useAppTheme, useEventTracker, useUserPermissions } from "@/hooks/store";
export interface SidebarUserMenuItemProps {
item: {
key: string;
href: string;
access: EUserWorkspaceRoles[];
labelTranslationKey: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
Icon: any;
};
draftIssueCount: number;
}
export const SidebarUserMenuItem: FC<SidebarUserMenuItemProps> = observer((props) => {
const { item, draftIssueCount } = props;
// nextjs hooks
const { workspaceSlug } = useParams();
const pathname = usePathname();
// package hooks
const { t } = useTranslation();
// store hooks
const { captureEvent } = useEventTracker();
const { allowPermissions } = useUserPermissions();
const { toggleSidebar, sidebarCollapsed } = useAppTheme();
const { isMobile } = usePlatformOS();
const isActive = pathname === item.href;
if (item.key === "drafts" && draftIssueCount === 0) return null;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
if (!allowPermissions(item.access as any, EUserPermissionsLevel.WORKSPACE, workspaceSlug.toString())) return null;
const handleLinkClick = (itemKey: string) => {
if (window.innerWidth < 768) {
toggleSidebar();
}
captureEvent(SIDEBAR_CLICKED, {
destination: itemKey,
});
};
return (
<Tooltip
tooltipContent={t(item.labelTranslationKey)}
position="right"
className="ml-2"
disabled={!sidebarCollapsed}
isMobile={isMobile}
>
<Link href={item.href} onClick={() => handleLinkClick(item.key)}>
<SidebarNavItem
className={`${sidebarCollapsed ? "p-0 size-8 aspect-square justify-center mx-auto" : ""}`}
isActive={isActive}
>
<div className="flex items-center gap-1.5 py-[1px]">
<item.Icon className="size-4 flex-shrink-0" />
{!sidebarCollapsed && <p className="text-sm leading-5 font-medium">{t(item.labelTranslationKey)}</p>}
</div>
{item.key === "notifications" && (
<NotificationAppSidebarOption
workspaceSlug={workspaceSlug.toString()}
isSidebarCollapsed={sidebarCollapsed ?? false}
/>
)}
</SidebarNavItem>
</Link>
</Tooltip>
);
});

View file

@ -2,58 +2,54 @@
import React from "react";
import { observer } from "mobx-react";
import Link from "next/link";
import { useParams, usePathname } from "next/navigation";
import { useTranslation } from "@plane/i18n";
import { useParams } from "next/navigation";
import { Home, Inbox, PenSquare } from "lucide-react";
// plane imports
import { EUserWorkspaceRoles } from "@plane/constants";
import { UserActivityIcon } from "@plane/ui";
// components
import { Tooltip } from "@plane/ui";
import { SidebarNavItem } from "@/components/sidebar";
import { NotificationAppSidebarOption } from "@/components/workspace-notifications";
// constants
import { SIDEBAR_CLICKED } from "@/constants/event-tracker";
import { SidebarUserMenuItem } from "@/components/workspace/sidebar";
// helpers
import { cn } from "@/helpers/common.helper";
// hooks
import { useAppTheme, useEventTracker, useUser, useUserPermissions } from "@/hooks/store";
import { usePlatformOS } from "@/hooks/use-platform-os";
// plane web constants
import { SIDEBAR_USER_MENU_ITEMS } from "@/plane-web/constants/dashboard";
import { EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
// plane web helpers
import { isUserFeatureEnabled } from "@/plane-web/helpers/dashboard.helper";
import { useAppTheme, useUserPermissions, useUser } from "@/hooks/store";
export const SidebarUserMenu = observer(() => {
const { t } = useTranslation();
// store hooks
const { toggleSidebar, sidebarCollapsed } = useAppTheme();
const { captureEvent } = useEventTracker();
const { isMobile } = usePlatformOS();
const { data: currentUser } = useUser();
const { allowPermissions, workspaceUserInfo } = useUserPermissions();
// router params
const { workspaceSlug } = useParams();
// pathname
const pathname = usePathname();
// computed
const { sidebarCollapsed } = useAppTheme();
const { workspaceUserInfo } = useUserPermissions();
const { data: currentUser } = useUser();
const getHref = (link: any) =>
`/${workspaceSlug}${link.href}${link.key === "your-work" ? `/${currentUser?.id}` : ""}`;
const handleLinkClick = (itemKey: string) => {
if (window.innerWidth < 768) {
toggleSidebar();
}
captureEvent(SIDEBAR_CLICKED, {
destination: itemKey,
});
};
const notificationIndicatorElement = (
<NotificationAppSidebarOption
workspaceSlug={workspaceSlug.toString()}
isSidebarCollapsed={sidebarCollapsed ?? false}
/>
);
const SIDEBAR_USER_MENU_ITEMS = [
{
key: "home",
labelTranslationKey: "home",
href: `/${workspaceSlug.toString()}/`,
access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER, EUserWorkspaceRoles.GUEST],
Icon: Home,
},
{
key: "your-work",
labelTranslationKey: "your_work",
href: `/${workspaceSlug.toString()}/profile/${currentUser?.id}/`,
access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER],
Icon: UserActivityIcon,
},
{
key: "notifications",
labelTranslationKey: "inbox",
href: `/${workspaceSlug.toString()}/notifications/`,
access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER, EUserWorkspaceRoles.GUEST],
Icon: Inbox,
},
{
key: "drafts",
labelTranslationKey: "drafts",
href: `/${workspaceSlug.toString()}/drafts/`,
access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER],
Icon: PenSquare,
},
];
const draftIssueCount = workspaceUserInfo[workspaceSlug.toString()]?.draft_issue_count;
@ -63,35 +59,9 @@ export const SidebarUserMenu = observer(() => {
"space-y-0": sidebarCollapsed,
})}
>
{SIDEBAR_USER_MENU_ITEMS.map((link) => {
if (link.value === "drafts" && draftIssueCount === 0) return null;
if (!isUserFeatureEnabled(link.value)) return null;
return (
allowPermissions(link.access, EUserPermissionsLevel.WORKSPACE, workspaceSlug.toString()) && (
<Tooltip
key={link.value}
tooltipContent={t(link.key)}
position="right"
className="ml-2"
disabled={!sidebarCollapsed}
isMobile={isMobile}
>
<Link key={link.value} href={getHref(link)} onClick={() => handleLinkClick(link.value)}>
<SidebarNavItem
className={`${sidebarCollapsed ? "p-0 size-8 aspect-square justify-center mx-auto" : ""}`}
isActive={link.highlight(pathname, `/${workspaceSlug}`, { userId: currentUser?.id })}
>
<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">{t(link.key)}</p>}
</div>
{link.value === "notifications" && notificationIndicatorElement}
</SidebarNavItem>
</Link>
</Tooltip>
)
);
})}
{SIDEBAR_USER_MENU_ITEMS.map((item) => (
<SidebarUserMenuItem key={item.key} item={item} draftIssueCount={draftIssueCount} />
))}
</div>
);
});

View file

@ -0,0 +1,116 @@
import { FC, useState, useRef } from "react";
import { observer } from "mobx-react";
import Link from "next/link";
import { useParams } from "next/navigation";
import { MoreHorizontal, ArchiveIcon, ChevronRight, Settings } from "lucide-react";
import { Disclosure } from "@headlessui/react";
// plane imports
import { EUserWorkspaceRoles, EUserPermissionsLevel } from "@plane/constants";
import { useOutsideClickDetector } from "@plane/hooks";
import { useTranslation } from "@plane/i18n";
import { CustomMenu } from "@plane/ui";
import { cn } from "@plane/utils";
// store hooks
import { useAppTheme, useUserPermissions } from "@/hooks/store";
export type SidebarWorkspaceMenuHeaderProps = {
isWorkspaceMenuOpen: boolean;
toggleWorkspaceMenu: (value: boolean) => void;
};
export const SidebarWorkspaceMenuHeader: FC<SidebarWorkspaceMenuHeaderProps> = observer((props) => {
const { isWorkspaceMenuOpen, toggleWorkspaceMenu } = props;
// state
const [isMenuActive, setIsMenuActive] = useState(false);
// refs
const actionSectionRef = useRef<HTMLDivElement | null>(null);
// hooks
const { workspaceSlug } = useParams();
const { sidebarCollapsed } = useAppTheme();
const { allowPermissions } = useUserPermissions();
const { t } = useTranslation();
useOutsideClickDetector(actionSectionRef, () => setIsMenuActive(false));
// TODO: fix types
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const isAdmin = allowPermissions([EUserWorkspaceRoles.ADMIN] as any, EUserPermissionsLevel.WORKSPACE);
if (sidebarCollapsed) {
return <></>;
}
return (
<div
className={cn(
"flex px-2 bg-custom-sidebar-background-100 group/workspace-button hover:bg-custom-sidebar-background-90 rounded",
{
"mt-2.5": !sidebarCollapsed,
}
)}
>
<Disclosure.Button
as="button"
className="flex-1 sticky top-0 z-10 w-full py-1.5 flex items-center justify-between gap-1 text-custom-sidebar-text-400 text-xs font-semibold"
onClick={() => toggleWorkspaceMenu(!isWorkspaceMenuOpen)}
>
<span>{t("workspace").toUpperCase()}</span>
</Disclosure.Button>
<CustomMenu
customButton={
<span
ref={actionSectionRef}
className="grid place-items-center p-0.5 text-custom-sidebar-text-400 hover:bg-custom-sidebar-background-80 rounded my-auto"
onClick={() => {
setIsMenuActive(!isMenuActive);
}}
>
<MoreHorizontal className="size-4" />
</span>
}
className={cn(
"h-full flex items-center opacity-0 z-20 pointer-events-none flex-shrink-0 group-hover/workspace-button:opacity-100 group-hover/workspace-button:pointer-events-auto my-auto",
{
"opacity-100 pointer-events-auto": isMenuActive,
}
)}
customButtonClassName="grid place-items-center"
placement="bottom-start"
>
<CustomMenu.MenuItem>
<Link href={`/${workspaceSlug}/projects/archives`}>
<div className="flex items-center justify-start gap-2">
<ArchiveIcon className="h-3.5 w-3.5 stroke-[1.5]" />
<span>{t("archives")}</span>
</div>
</Link>
</CustomMenu.MenuItem>
{isAdmin && (
<CustomMenu.MenuItem>
<Link href={`/${workspaceSlug}/settings`}>
<div className="flex items-center justify-start gap-2">
<Settings className="h-3.5 w-3.5 stroke-[1.5]" />
<span>{t("settings")}</span>
</div>
</Link>
</CustomMenu.MenuItem>
)}
</CustomMenu>
<Disclosure.Button
as="button"
className="sticky top-0 z-10 group/workspace-button px-0.5 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)}
>
{" "}
<span className="flex-shrink-0 opacity-0 pointer-events-none group-hover/workspace-button:opacity-100 group-hover/workspace-button:pointer-events-auto rounded hover:bg-custom-sidebar-background-80">
<ChevronRight
className={cn("size-4 flex-shrink-0 text-custom-sidebar-text-400 transition-transform", {
"rotate-90": isWorkspaceMenuOpen,
})}
/>
</span>
</Disclosure.Button>
</div>
);
});

View file

@ -0,0 +1,82 @@
import { FC } from "react";
import { observer } from "mobx-react";
import Link from "next/link";
import { useParams, usePathname } from "next/navigation";
// plane imports
import { EUserWorkspaceRoles, EUserPermissionsLevel } from "@plane/constants";
import { usePlatformOS } from "@plane/hooks";
import { useTranslation } from "@plane/i18n";
import { Tooltip } from "@plane/ui";
import { cn } from "@plane/utils";
// components
import { SidebarNavItem } from "@/components/sidebar";
// hooks
import { useAppTheme, useUserPermissions } from "@/hooks/store";
// plane web imports
import { UpgradeBadge } from "@/plane-web/components/workspace";
export type SidebarWorkspaceMenuItemProps = {
item: {
labelTranslationKey: string;
key: string;
href: string;
Icon: any;
access: EUserWorkspaceRoles[];
};
};
export const SidebarWorkspaceMenuItem: FC<SidebarWorkspaceMenuItemProps> = observer((props) => {
const { item } = props;
const { t } = useTranslation();
// nextjs hooks
const pathname = usePathname();
const { workspaceSlug } = useParams();
const { allowPermissions } = useUserPermissions();
// store hooks
const { toggleSidebar, sidebarCollapsed } = useAppTheme();
const { isMobile } = usePlatformOS();
const handleLinkClick = () => {
if (window.innerWidth < 768) {
toggleSidebar();
}
};
if (!allowPermissions(item.access as any, EUserPermissionsLevel.WORKSPACE, workspaceSlug.toString())) {
return null;
}
const isActive = item.href === pathname;
return (
<Tooltip
tooltipContent={t(item.labelTranslationKey)}
position="right"
className="ml-2"
disabled={!sidebarCollapsed}
isMobile={isMobile}
>
<Link href={item.href} onClick={() => handleLinkClick()}>
<SidebarNavItem
className={`${sidebarCollapsed ? "p-0 size-8 aspect-square justify-center mx-auto" : ""}`}
isActive={isActive}
>
<div className="flex items-center gap-1.5 py-[1px]">
<item.Icon
className={cn("size-4", {
"rotate-180": item.key === "active-cycles",
})}
/>
{!sidebarCollapsed && <p className="text-sm leading-5 font-medium">{t(item.labelTranslationKey)}</p>}
</div>
{!sidebarCollapsed && item.key === "active-cycles" && (
<div className="flex-shrink-0">
<UpgradeBadge />
</div>
)}
</SidebarNavItem>
</Link>
</Tooltip>
);
});

View file

@ -1,150 +1,69 @@
"use client";
import React, { useEffect, useRef, useState } from "react";
import React, { useEffect } from "react";
import { observer } from "mobx-react";
import Link from "next/link";
import { useParams, usePathname } from "next/navigation";
import { ArchiveIcon, ChevronRight, MoreHorizontal, Settings } from "lucide-react";
import { useParams } from "next/navigation";
import { BarChart2, Briefcase, Layers } from "lucide-react";
import { Disclosure, Transition } from "@headlessui/react";
import { useOutsideClickDetector } from "@plane/hooks";
import { useTranslation } from "@plane/i18n";
// ui
import { CustomMenu, Tooltip } from "@plane/ui";
import { EUserWorkspaceRoles } from "@plane/constants";
import { ContrastIcon } from "@plane/ui";
// components
import { SidebarNavItem } from "@/components/sidebar";
// constants
import { SIDEBAR_CLICKED } from "@/constants/event-tracker";
import { SidebarWorkspaceMenuHeader, SidebarWorkspaceMenuItem } from "@/components/workspace/sidebar";
// helpers
import { cn } from "@/helpers/common.helper";
// hooks
import { useAppTheme, useEventTracker, useUserPermissions } from "@/hooks/store";
import { useAppTheme } from "@/hooks/store";
import useLocalStorage from "@/hooks/use-local-storage";
import { usePlatformOS } from "@/hooks/use-platform-os";
// plane web components
import { UpgradeBadge } from "@/plane-web/components/workspace";
// plane web constants
import { SIDEBAR_WORKSPACE_MENU_ITEMS } from "@/plane-web/constants/dashboard";
import { EUserPermissions, EUserPermissionsLevel } from "@/plane-web/constants/user-permissions";
// plane web hooks
import { isWorkspaceFeatureEnabled } from "@/plane-web/helpers/dashboard.helper";
export const SidebarWorkspaceMenu = observer(() => {
// state
const [isMenuActive, setIsMenuActive] = useState(false);
// refs
const actionSectionRef = useRef<HTMLDivElement | null>(null);
// router params
const { workspaceSlug } = useParams();
// pathname
const pathname = usePathname();
// store hooks
const { t } = useTranslation();
const { toggleSidebar, sidebarCollapsed } = useAppTheme();
const { captureEvent } = useEventTracker();
const { isMobile } = usePlatformOS();
const { allowPermissions } = useUserPermissions();
const { sidebarCollapsed } = useAppTheme();
// local storage
const { setValue: toggleWorkspaceMenu, storedValue } = useLocalStorage<boolean>("is_workspace_menu_open", true);
// derived values
const isWorkspaceMenuOpen = !!storedValue;
const isAdmin = allowPermissions([EUserPermissions.ADMIN], EUserPermissionsLevel.WORKSPACE);
const handleLinkClick = (itemKey: string) => {
if (window.innerWidth < 768) {
toggleSidebar();
}
captureEvent(SIDEBAR_CLICKED, {
destination: itemKey,
});
};
useEffect(() => {
if (sidebarCollapsed) toggleWorkspaceMenu(true);
}, [sidebarCollapsed, toggleWorkspaceMenu]);
useOutsideClickDetector(actionSectionRef, () => setIsMenuActive(false));
const indicatorElement = (
<div className="flex-shrink-0">
<UpgradeBadge />
</div>
);
const SIDEBAR_WORKSPACE_MENU_ITEMS = [
{
key: "projects",
labelTranslationKey: "projects",
href: `/${workspaceSlug}/projects/`,
access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER, EUserWorkspaceRoles.GUEST],
Icon: Briefcase,
},
{
key: "views",
labelTranslationKey: "views",
href: `/${workspaceSlug}/workspace-views/all-issues/`,
access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER, EUserWorkspaceRoles.GUEST],
Icon: Layers,
},
{
key: "active-cycles",
labelTranslationKey: "cycles",
href: `/${workspaceSlug}/active-cycles/`,
access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER],
Icon: ContrastIcon,
},
{
key: "analytics",
labelTranslationKey: "analytics",
href: `/${workspaceSlug}/analytics/`,
access: [EUserWorkspaceRoles.ADMIN, EUserWorkspaceRoles.MEMBER],
Icon: BarChart2,
},
];
return (
<Disclosure as="div" defaultOpen>
{!sidebarCollapsed && (
<div
className={cn(
"flex px-2 bg-custom-sidebar-background-100 group/workspace-button hover:bg-custom-sidebar-background-90 rounded",
{
"mt-2.5": !sidebarCollapsed,
}
)}
>
{" "}
<Disclosure.Button
as="button"
className="flex-1 sticky top-0 z-10 w-full py-1.5 flex items-center justify-between gap-1 text-custom-sidebar-text-400 text-xs font-semibold"
onClick={() => toggleWorkspaceMenu(!isWorkspaceMenuOpen)}
>
<span>{t("workspace").toUpperCase()}</span>
</Disclosure.Button>
<CustomMenu
customButton={
<span
ref={actionSectionRef}
className="grid place-items-center p-0.5 text-custom-sidebar-text-400 hover:bg-custom-sidebar-background-80 rounded my-auto"
onClick={() => {
setIsMenuActive(!isMenuActive);
}}
>
<MoreHorizontal className="size-4" />
</span>
}
className={cn(
"h-full flex items-center opacity-0 z-20 pointer-events-none flex-shrink-0 group-hover/workspace-button:opacity-100 group-hover/workspace-button:pointer-events-auto my-auto",
{
"opacity-100 pointer-events-auto": isMenuActive,
}
)}
customButtonClassName="grid place-items-center"
placement="bottom-start"
>
<CustomMenu.MenuItem>
<Link href={`/${workspaceSlug}/projects/archives`}>
<div className="flex items-center justify-start gap-2">
<ArchiveIcon className="h-3.5 w-3.5 stroke-[1.5]" />
<span>{t("archives")}</span>
</div>
</Link>
</CustomMenu.MenuItem>
{isAdmin && (
<CustomMenu.MenuItem>
<Link href={`/${workspaceSlug}/settings`}>
<div className="flex items-center justify-start gap-2">
<Settings className="h-3.5 w-3.5 stroke-[1.5]" />
<span>{t("settings")}</span>
</div>
</Link>
</CustomMenu.MenuItem>
)}
</CustomMenu>
<Disclosure.Button
as="button"
className="sticky top-0 z-10 group/workspace-button px-0.5 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)}
>
{" "}
<span className="flex-shrink-0 opacity-0 pointer-events-none group-hover/workspace-button:opacity-100 group-hover/workspace-button:pointer-events-auto rounded hover:bg-custom-sidebar-background-80">
<ChevronRight
className={cn("size-4 flex-shrink-0 text-custom-sidebar-text-400 transition-transform", {
"rotate-90": isWorkspaceMenuOpen,
})}
/>
</span>
</Disclosure.Button>
</div>
)}
<SidebarWorkspaceMenuHeader isWorkspaceMenuOpen={isWorkspaceMenuOpen} toggleWorkspaceMenu={toggleWorkspaceMenu} />
<Transition
show={isWorkspaceMenuOpen}
enter="transition duration-100 ease-out"
@ -162,39 +81,9 @@ export const SidebarWorkspaceMenu = observer(() => {
})}
static
>
{SIDEBAR_WORKSPACE_MENU_ITEMS.map((link) => {
if (!isWorkspaceFeatureEnabled(link.value, workspaceSlug.toString())) return null;
return (
allowPermissions(link.access, EUserPermissionsLevel.WORKSPACE, workspaceSlug.toString()) && (
<Tooltip
key={link.value}
tooltipContent={t(link.key)}
position="right"
className="ml-2"
disabled={!sidebarCollapsed}
isMobile={isMobile}
>
<Link href={`/${workspaceSlug}${link.href}`} onClick={() => handleLinkClick(link.value)}>
<SidebarNavItem
key={link.value}
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={cn("size-4", {
"rotate-180": link.value === "active-cycles",
})}
/>
{!sidebarCollapsed && <p className="text-sm leading-5 font-medium">{t(link.key)}</p>}
</div>
{!sidebarCollapsed && link.value === "active-cycles" && indicatorElement}
</SidebarNavItem>
</Link>
</Tooltip>
)
);
})}
{SIDEBAR_WORKSPACE_MENU_ITEMS.map((item) => (
<SidebarWorkspaceMenuItem key={item.key} item={item} />
))}
</Disclosure.Panel>
)}
</Transition>