From 7f5028a4f6eca76a9e5fef7f2c3c577eb0faff3c Mon Sep 17 00:00:00 2001 From: Prateek Shourya Date: Fri, 19 Jan 2024 16:05:52 +0530 Subject: [PATCH] refactor: move all sidebar links to constant file. (#3414) * chore: move all workspace, project and profile links constants into their own constants file. * chore: sidebar menu links improvement. --- .../actions/workspace-settings-actions.tsx | 84 ++++++++----------- web/components/workspace/sidebar-menu.tsx | 80 +++++++----------- web/constants/dashboard.ts | 55 ++++++++++++ web/constants/profile.ts | 40 +++++++++ web/constants/project.ts | 78 +++++++++++++++++ web/constants/workspace.ts | 30 +++++++ .../settings-layout/profile/sidebar.tsx | 33 +------- .../settings-layout/project/sidebar.tsx | 76 ++++++----------- .../settings-layout/workspace/sidebar.tsx | 4 +- 9 files changed, 296 insertions(+), 184 deletions(-) create mode 100644 web/constants/profile.ts diff --git a/web/components/command-palette/actions/workspace-settings-actions.tsx b/web/components/command-palette/actions/workspace-settings-actions.tsx index 7503343ee..1f05234f4 100644 --- a/web/components/command-palette/actions/workspace-settings-actions.tsx +++ b/web/components/command-palette/actions/workspace-settings-actions.tsx @@ -1,8 +1,10 @@ import { useRouter } from "next/router"; import { Command } from "cmdk"; -// icons -import { SettingIcon } from "components/icons"; +// hooks +import { useUser } from "hooks/store"; import Link from "next/link"; +// constants +import { EUserWorkspaceRoles, WORKSPACE_SETTINGS_LINKS } from "constants/workspace"; type Props = { closePalette: () => void; @@ -10,60 +12,40 @@ type Props = { export const CommandPaletteWorkspaceSettingsActions: React.FC = (props) => { const { closePalette } = props; - + // router const router = useRouter(); const { workspaceSlug } = router.query; + // mobx store + const { + membership: { currentWorkspaceRole }, + } = useUser(); + // derived values + const workspaceMemberInfo = currentWorkspaceRole || EUserWorkspaceRoles.GUEST; + + const redirect = (path: string) => { + closePalette(); + router.push(path); + }; return ( <> - - -
- - General -
- -
- - -
- - Members -
- -
- - -
- - Billing and Plans -
- -
- - -
- - Integrations -
- -
- - -
- - Import -
- -
- - -
- - Export -
- -
+ {WORKSPACE_SETTINGS_LINKS.map( + (setting) => + workspaceMemberInfo >= setting.access && ( + redirect(`/${workspaceSlug}${setting.href}`)} + className="focus:outline-none" + > + +
+ + {setting.label} +
+ +
+ ) + )} ); }; diff --git a/web/components/workspace/sidebar-menu.tsx b/web/components/workspace/sidebar-menu.tsx index 98f1f880d..640318980 100644 --- a/web/components/workspace/sidebar-menu.tsx +++ b/web/components/workspace/sidebar-menu.tsx @@ -2,7 +2,6 @@ import React from "react"; import Link from "next/link"; import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; -import { BarChart2, Briefcase, CheckCircle, LayoutGrid } from "lucide-react"; // hooks import { useApplication, useUser } from "hooks/store"; // components @@ -11,29 +10,7 @@ import { NotificationPopover } from "components/notifications"; import { Tooltip } from "@plane/ui"; // constants import { EUserWorkspaceRoles } from "constants/workspace"; - -const workspaceLinks = (workspaceSlug: string) => [ - { - Icon: LayoutGrid, - name: "Dashboard", - href: `/${workspaceSlug}`, - }, - { - Icon: BarChart2, - name: "Analytics", - href: `/${workspaceSlug}/analytics`, - }, - { - Icon: Briefcase, - name: "Projects", - href: `/${workspaceSlug}/projects`, - }, - { - Icon: CheckCircle, - name: "All Issues", - href: `/${workspaceSlug}/workspace-views/all-issues`, - }, -]; +import { SIDEBAR_MENU_ITEMS } from "constants/dashboard"; export const WorkspaceSidebarMenu = observer(() => { // store hooks @@ -45,37 +22,36 @@ export const WorkspaceSidebarMenu = observer(() => { const router = useRouter(); const { workspaceSlug } = router.query; // computed - const isAuthorizedUser = !!currentWorkspaceRole && currentWorkspaceRole >= EUserWorkspaceRoles.MEMBER; + const workspaceMemberInfo = currentWorkspaceRole || EUserWorkspaceRoles.GUEST; return ( -
- {workspaceLinks(workspaceSlug as string).map((link, index) => { - const isActive = link.name === "Settings" ? router.asPath.includes(link.href) : router.asPath === link.href; - if (!isAuthorizedUser && link.name === "Analytics") return; - return ( - - - -
+ {SIDEBAR_MENU_ITEMS.map( + (link) => + workspaceMemberInfo >= link.access && ( + + + - {} - {!themeStore?.sidebarCollapsed && link.name} -
-
-
- - ); - })} +
+ {} + {!themeStore?.sidebarCollapsed && link.label} +
+ + + + ) + )}
); diff --git a/web/constants/dashboard.ts b/web/constants/dashboard.ts index 61b6b5f1c..89ab0027d 100644 --- a/web/constants/dashboard.ts +++ b/web/constants/dashboard.ts @@ -14,6 +14,11 @@ import CompletedCreatedIssuesDark from "public/empty-state/dashboard/dark/comple import CompletedCreatedIssuesLight from "public/empty-state/dashboard/light/completed-created-issues.svg"; // types import { TDurationFilterOptions, TIssuesListTypes, TStateGroups } from "@plane/types"; +import { Props } from "components/icons/types"; +// constants +import { EUserWorkspaceRoles } from "./workspace"; +// icons +import { BarChart2, Briefcase, CheckCircle, LayoutGrid, SendToBack } from "lucide-react"; // gradients for issues by priority widget graph bars export const PRIORITY_GRAPH_GRADIENTS = [ @@ -246,3 +251,53 @@ export const CREATED_ISSUES_EMPTY_STATES = { lightImage: CompletedCreatedIssuesLight, }, }; + +export const SIDEBAR_MENU_ITEMS: { + key: string; + label: string; + href: string; + access: EUserWorkspaceRoles; + highlight: (pathname: string, baseUrl: string) => boolean; + Icon: React.FC; +}[] = [ + { + key: "dashboard", + label: "Dashboard", + href: ``, + access: EUserWorkspaceRoles.GUEST, + highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}`, + Icon: LayoutGrid, + }, + { + key: "analytics", + label: "Analytics", + href: `/analytics`, + access: EUserWorkspaceRoles.MEMBER, + highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/analytics`, + Icon: BarChart2, + }, + { + key: "projects", + label: "Projects", + href: `/projects`, + access: EUserWorkspaceRoles.GUEST, + highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/projects`, + Icon: Briefcase, + }, + { + key: "all-issues", + label: "All Issues", + href: `/workspace-views/all-issues`, + access: EUserWorkspaceRoles.GUEST, + highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/workspace-views/all-issues`, + Icon: CheckCircle, + }, + { + key: "active-cycles", + label: "Active cycles", + href: `/active-cycles`, + access: EUserWorkspaceRoles.GUEST, + highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/active-cycles`, + Icon: SendToBack, + }, +]; diff --git a/web/constants/profile.ts b/web/constants/profile.ts new file mode 100644 index 000000000..3f17bb329 --- /dev/null +++ b/web/constants/profile.ts @@ -0,0 +1,40 @@ +import React from "react"; +// icons +import { Activity, CircleUser, KeyRound, LucideProps, Settings2 } from "lucide-react"; + +export const PROFILE_ACTION_LINKS: { + key: string; + label: string; + href: string; + highlight: (pathname: string) => boolean; + Icon: React.FC; +}[] = [ + { + key: "profile", + label: "Profile", + href: `/profile`, + highlight: (pathname: string) => pathname === "/profile", + Icon: CircleUser, + }, + { + key: "change-password", + label: "Change password", + href: `/profile/change-password`, + highlight: (pathname: string) => pathname === "/profile/change-password", + Icon: KeyRound, + }, + { + key: "activity", + label: "Activity", + href: `/profile/activity`, + highlight: (pathname: string) => pathname === "/profile/activity", + Icon: Activity, + }, + { + key: "preferences", + label: "Preferences", + href: `/profile/preferences`, + highlight: (pathname: string) => pathname.includes("/profile/preferences"), + Icon: Settings2, + }, +]; diff --git a/web/constants/project.ts b/web/constants/project.ts index c35a6abe2..f9819c780 100644 --- a/web/constants/project.ts +++ b/web/constants/project.ts @@ -1,4 +1,8 @@ +// icons import { Globe2, Lock, LucideIcon } from "lucide-react"; +import { SettingIcon } from "components/icons"; +// types +import { Props } from "components/icons/types"; export enum EUserProjectRoles { GUEST = 5, @@ -71,3 +75,77 @@ export const PROJECT_UNSPLASH_COVERS = [ "https://images.unsplash.com/photo-1691230995681-480d86cbc135?auto=format&fit=crop&q=80&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&w=870&q=80", "https://images.unsplash.com/photo-1675351066828-6fc770b90dd2?auto=format&fit=crop&q=80&ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&w=870&q=80", ]; + +export const PROJECT_SETTINGS_LINKS: { + key: string; + label: string; + href: string; + access: EUserProjectRoles; + highlight: (pathname: string, baseUrl: string) => boolean; + Icon: React.FC; +}[] = [ + { + key: "general", + label: "General", + href: `/settings`, + access: EUserProjectRoles.MEMBER, + highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings`, + Icon: SettingIcon, + }, + { + key: "members", + label: "Members", + href: `/settings/members`, + access: EUserProjectRoles.MEMBER, + highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/members`, + Icon: SettingIcon, + }, + { + key: "features", + label: "Features", + href: `/settings/features`, + access: EUserProjectRoles.ADMIN, + highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/features`, + Icon: SettingIcon, + }, + { + key: "states", + label: "States", + href: `/settings/states`, + access: EUserProjectRoles.MEMBER, + highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/states`, + Icon: SettingIcon, + }, + { + key: "labels", + label: "Labels", + href: `/settings/labels`, + access: EUserProjectRoles.MEMBER, + highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/labels`, + Icon: SettingIcon, + }, + { + key: "integrations", + label: "Integrations", + href: `/settings/integrations`, + access: EUserProjectRoles.ADMIN, + highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/integrations`, + Icon: SettingIcon, + }, + { + key: "estimates", + label: "Estimates", + href: `/settings/estimates`, + access: EUserProjectRoles.ADMIN, + highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/estimates`, + Icon: SettingIcon, + }, + { + key: "automations", + label: "Automations", + href: `/settings/automations`, + access: EUserProjectRoles.ADMIN, + highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/automations`, + Icon: SettingIcon, + }, +]; diff --git a/web/constants/workspace.ts b/web/constants/workspace.ts index 90ccbae2d..1471de395 100644 --- a/web/constants/workspace.ts +++ b/web/constants/workspace.ts @@ -6,6 +6,9 @@ import ExcelLogo from "public/services/excel.svg"; import JSONLogo from "public/services/json.svg"; // types import { TStaticViewTypes } from "@plane/types"; +import { Props } from "components/icons/types"; +// icons +import { SettingIcon } from "components/icons"; export enum EUserWorkspaceRoles { GUEST = 5, @@ -115,48 +118,75 @@ export const RESTRICTED_URLS = [ ]; export const WORKSPACE_SETTINGS_LINKS: { + key: string; label: string; href: string; access: EUserWorkspaceRoles; + highlight: (pathname: string, baseUrl: string) => boolean; + Icon: React.FC; }[] = [ { + key: "general", label: "General", href: `/settings`, access: EUserWorkspaceRoles.GUEST, + highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings`, + Icon: SettingIcon, }, { + key: "members", label: "Members", href: `/settings/members`, access: EUserWorkspaceRoles.GUEST, + highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/members`, + Icon: SettingIcon, }, { + key: "billing-and-plans", label: "Billing and plans", href: `/settings/billing`, access: EUserWorkspaceRoles.ADMIN, + highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/billing`, + Icon: SettingIcon, }, { + key: "integrations", label: "Integrations", href: `/settings/integrations`, access: EUserWorkspaceRoles.ADMIN, + highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/integrations`, + Icon: SettingIcon, }, { + key: "import", label: "Imports", href: `/settings/imports`, access: EUserWorkspaceRoles.ADMIN, + highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/imports`, + Icon: SettingIcon, }, { + key: "export", label: "Exports", href: `/settings/exports`, access: EUserWorkspaceRoles.MEMBER, + highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/exports`, + Icon: SettingIcon, }, { + key: "webhooks", label: "Webhooks", href: `/settings/webhooks`, access: EUserWorkspaceRoles.ADMIN, + highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/webhooks`, + Icon: SettingIcon, }, { + key: "api-tokens", label: "API tokens", href: `/settings/api-tokens`, access: EUserWorkspaceRoles.ADMIN, + highlight: (pathname: string, baseUrl: string) => pathname === `${baseUrl}/settings/api-tokens`, + Icon: SettingIcon, }, ]; diff --git a/web/layouts/settings-layout/profile/sidebar.tsx b/web/layouts/settings-layout/profile/sidebar.tsx index b217c5f63..0a97b3364 100644 --- a/web/layouts/settings-layout/profile/sidebar.tsx +++ b/web/layouts/settings-layout/profile/sidebar.tsx @@ -4,39 +4,14 @@ import Link from "next/link"; import { useRouter } from "next/router"; import { observer } from "mobx-react-lite"; import { useTheme } from "next-themes"; -import { Activity, ChevronLeft, CircleUser, KeyRound, LogOut, MoveLeft, Plus, Settings2, UserPlus } from "lucide-react"; +import { ChevronLeft, LogOut, MoveLeft, Plus, UserPlus } from "lucide-react"; // hooks import { useApplication, useUser, useWorkspace } from "hooks/store"; import useToast from "hooks/use-toast"; // ui import { Tooltip } from "@plane/ui"; - -const PROFILE_ACTION_LINKS = [ - { - key: "profile", - label: "Profile", - href: `/profile`, - Icon: CircleUser, - }, - { - key: "change-password", - label: "Change password", - href: `/profile/change-password`, - Icon: KeyRound, - }, - { - key: "activity", - label: "Activity", - href: `/profile/activity`, - Icon: Activity, - }, - { - key: "preferences", - label: "Preferences", - href: `/profile/preferences`, - Icon: Settings2, - }, -]; +// constants +import { PROFILE_ACTION_LINKS } from "constants/profile"; const WORKSPACE_ACTION_LINKS = [ { @@ -130,7 +105,7 @@ export const ProfileLayoutSidebar = observer(() => {
{ const router = useRouter(); const { workspaceSlug, projectId } = router.query; + // mobx store + const { + membership: { currentProjectRole }, + } = useUser(); + + const projectMemberInfo = currentProjectRole || EUserProjectRoles.GUEST; - const projectLinks: Array<{ - label: string; - href: string; - }> = [ - { - label: "General", - href: `/${workspaceSlug}/projects/${projectId}/settings`, - }, - { - label: "Members", - href: `/${workspaceSlug}/projects/${projectId}/settings/members`, - }, - { - label: "Features", - href: `/${workspaceSlug}/projects/${projectId}/settings/features`, - }, - { - label: "States", - href: `/${workspaceSlug}/projects/${projectId}/settings/states`, - }, - { - label: "Labels", - href: `/${workspaceSlug}/projects/${projectId}/settings/labels`, - }, - { - label: "Integrations", - href: `/${workspaceSlug}/projects/${projectId}/settings/integrations`, - }, - { - label: "Estimates", - href: `/${workspaceSlug}/projects/${projectId}/settings/estimates`, - }, - { - label: "Automations", - href: `/${workspaceSlug}/projects/${projectId}/settings/automations`, - }, - ]; return (
SETTINGS
- {projectLinks.map((link) => ( - -
- {link.label} -
- - ))} + {PROJECT_SETTINGS_LINKS.map( + (link) => + projectMemberInfo >= link.access && ( + +
+ {link.label} +
+ + ) + )}
diff --git a/web/layouts/settings-layout/workspace/sidebar.tsx b/web/layouts/settings-layout/workspace/sidebar.tsx index 2be9a416d..c8d4718c7 100644 --- a/web/layouts/settings-layout/workspace/sidebar.tsx +++ b/web/layouts/settings-layout/workspace/sidebar.tsx @@ -25,11 +25,11 @@ export const WorkspaceSettingsSidebar = () => { {WORKSPACE_SETTINGS_LINKS.map( (link) => workspaceMemberInfo >= link.access && ( - +