diff --git a/apps/web/ce/components/navigations/top-navigation-root.tsx b/apps/web/ce/components/navigations/top-navigation-root.tsx index 448fa2599c5..c6b1de0d7f6 100644 --- a/apps/web/ce/components/navigations/top-navigation-root.tsx +++ b/apps/web/ce/components/navigations/top-navigation-root.tsx @@ -14,20 +14,20 @@ export const TopNavigationRoot = observer(() => { return (
{/* Workspace Menu */} -
+
{/* Power K Search */} -
+
{/* Additional Actions */} -
+
diff --git a/apps/web/ce/components/workspace/sidebar/helper.tsx b/apps/web/ce/components/workspace/sidebar/helper.tsx index 316f77b5d72..50323f3202f 100644 --- a/apps/web/ce/components/workspace/sidebar/helper.tsx +++ b/apps/web/ce/components/workspace/sidebar/helper.tsx @@ -5,6 +5,7 @@ import { DraftIcon, HomeIcon, InboxIcon, + MultipleStickyIcon, ProjectIcon, ViewsIcon, YourWorkIcon, @@ -31,5 +32,7 @@ export const getSidebarNavigationItemIcon = (key: string, className: string = "" return ; case "archives": return ; + case "stickies": + return ; } }; diff --git a/apps/web/core/components/navigation/customize-navigation-dialog.tsx b/apps/web/core/components/navigation/customize-navigation-dialog.tsx index 103650a1196..a44a88cb351 100644 --- a/apps/web/core/components/navigation/customize-navigation-dialog.tsx +++ b/apps/web/core/components/navigation/customize-navigation-dialog.tsx @@ -71,7 +71,7 @@ export const CustomizeNavigationDialog: FC = ob const filteredPersonalItems = PERSONAL_ITEMS; // Filter workspace items by permissions and feature flags, then get pinned/unpinned items - const { pinnedItems, unpinnedItems } = useMemo(() => { + const workspaceItems = useMemo(() => { const items = WORKSPACE_SIDEBAR_DYNAMIC_NAVIGATION_ITEMS_LINKS.filter((item) => { // Permission check const hasPermission = allowPermissions( @@ -94,11 +94,7 @@ export const CustomizeNavigationDialog: FC = ob }; }); - // Sort pinned items by sort_order - const pinned = items.filter((item) => item.isPinned).sort((a, b) => a.sortOrder - b.sortOrder); - const unpinned = items.filter((item) => !item.isPinned); - - return { pinnedItems: pinned, unpinnedItems: unpinned }; + return items.sort((a, b) => a.sortOrder - b.sortOrder); }, [workspaceSlug, allowPermissions, workspacePreferences]); // Handle checkbox toggle @@ -134,7 +130,7 @@ export const CustomizeNavigationDialog: FC = ob ); // Separate personal items into enabled/disabled - const { enabledPersonalItems, disabledPersonalItems } = useMemo(() => { + const personalItems = useMemo(() => { const items = filteredPersonalItems.map((item) => { const itemState = personalPreferences.items[item.key]; const isEnabled = typeof itemState === "boolean" ? itemState : (itemState?.enabled ?? true); @@ -147,10 +143,7 @@ export const CustomizeNavigationDialog: FC = ob }; }); - const enabled = items.filter((item) => item.isEnabled).sort((a, b) => a.sortOrder - b.sortOrder); - const disabled = items.filter((item) => !item.isEnabled); - - return { enabledPersonalItems: enabled, disabledPersonalItems: disabled }; + return items.sort((a, b) => a.sortOrder - b.sortOrder); }, [personalPreferences, filteredPersonalItems]); // Prevent typing invalid characters in number input @@ -203,18 +196,19 @@ export const CustomizeNavigationDialog: FC = ob {/* Personal Section */}

{t("personal")}

- - {/* Enabled Items - Sortable */}
item.key} id="personal-enabled-items" render={(item) => (
- togglePersonalItem(item.key, e.target.checked)} /> + togglePersonalItem(item.key, e.target.checked)} + />
{getSidebarNavigationItemIcon(item.key)}
)} /> - - {/* Disabled Items */} - {disabledPersonalItems.length > 0 && ( -
0 && "mt-1")}> - {disabledPersonalItems.map((item) => ( -
- - togglePersonalItem(item.key, e.target.checked)} /> -
- {getSidebarNavigationItemIcon(item.key)} - -
-
- ))} -
- )}
@@ -254,7 +227,7 @@ export const CustomizeNavigationDialog: FC = ob
{/* Pinned Items - Draggable */} item.key} id="workspace-pinned-items" @@ -263,7 +236,10 @@ export const CustomizeNavigationDialog: FC = ob return (
- handleWorkspaceItemToggle(item.key, e.target.checked)} /> + handleWorkspaceItemToggle(item.key, e.target.checked)} + />
{icon} {t(item.labelTranslationKey)} @@ -272,31 +248,6 @@ export const CustomizeNavigationDialog: FC = ob ); }} /> - - {/* Unpinned Items */} - {unpinnedItems.length > 0 && ( -
- {unpinnedItems.map((item) => { - const icon = getSidebarNavigationItemIcon(item.key); - return ( -
- - handleWorkspaceItemToggle(item.key, e.target.checked)} - /> -
- {icon} - {t(item.labelTranslationKey)} -
-
- ); - })} -
- )}
diff --git a/apps/web/core/components/navigation/project-actions-menu.tsx b/apps/web/core/components/navigation/project-actions-menu.tsx index 1986a8e1a9a..12c960f6b71 100644 --- a/apps/web/core/components/navigation/project-actions-menu.tsx +++ b/apps/web/core/components/navigation/project-actions-menu.tsx @@ -1,14 +1,15 @@ "use client"; import type { FC } from "react"; -import React, { useState, useRef } from "react"; +import { useState, useRef } from "react"; import { useNavigate } from "react-router"; import { LinkIcon, LogOut, MoreHorizontal, Settings, Share2, ArchiveIcon } from "lucide-react"; +// plane imports import { MEMBER_TRACKER_ELEMENTS } from "@plane/constants"; import { useTranslation } from "@plane/i18n"; import { CustomMenu } from "@plane/ui"; -type ProjectActionsMenuProps = { +type Props = { workspaceSlug: string; project: { id: string; @@ -20,7 +21,7 @@ type ProjectActionsMenuProps = { onPublishModal: () => void; }; -export const ProjectActionsMenu: FC = ({ +export const ProjectActionsMenu: FC = ({ workspaceSlug, project, isAdmin, @@ -29,10 +30,14 @@ export const ProjectActionsMenu: FC = ({ onLeaveProject, onPublishModal, }) => { + // states + const [isMenuActive, setIsMenuActive] = useState(false); + // translation const { t } = useTranslation(); - const navigate = useNavigate(); + // refs const actionSectionRef = useRef(null); - const [isMenuActive, setIsMenuActive] = useState(false); + // router + const navigate = useNavigate(); return ( = ({ project }) => ( -
+
diff --git a/apps/web/core/components/navigation/tab-navigation-overflow-menu.tsx b/apps/web/core/components/navigation/tab-navigation-overflow-menu.tsx index 1c91521fd06..37caae7ac1e 100644 --- a/apps/web/core/components/navigation/tab-navigation-overflow-menu.tsx +++ b/apps/web/core/components/navigation/tab-navigation-overflow-menu.tsx @@ -1,14 +1,17 @@ import React from "react"; import { Link } from "react-router"; -import { MoreHorizontal, Star, Pin } from "lucide-react"; +import { MoreHorizontal, Pin } from "lucide-react"; +// plane imports import { useTranslation } from "@plane/i18n"; +import { SetAsDefaultIcon } from "@plane/propel/icons"; import { Menu } from "@plane/propel/menu"; -import { TabNavigationItem } from "@plane/propel/tab-navigation"; +import { Tooltip } from "@plane/propel/tooltip"; import { cn } from "@plane/utils"; +// local imports import type { TNavigationItem } from "./tab-navigation-root"; import type { TTabPreferences } from "./tab-navigation-utils"; -export type TTabNavigationOverflowMenuProps = { +type Props = { overflowItems: TNavigationItem[]; isActive: (item: TNavigationItem) => boolean; tabPreferences: TTabPreferences; @@ -19,9 +22,9 @@ export type TTabNavigationOverflowMenuProps = { /** * Overflow menu for tab navigation items * Displays items that don't fit in the visible area, with action icons - * Shows "Eye" icon for user-hidden items, "Star" icon for all items + * Shows "Eye" icon for user-hidden items, "Set as default" icon for all items */ -export const TabNavigationOverflowMenu: React.FC = ({ +export const TabNavigationOverflowMenu: React.FC = ({ overflowItems, isActive, tabPreferences, @@ -48,23 +51,12 @@ export const TabNavigationOverflowMenu: React.FC -
- - - {t(item.i18n_key)} - + +
+ + {t(item.i18n_key)} -
+
{/* Show Eye icon ONLY for user-hidden items */} {isHidden && ( )} - + + +
diff --git a/apps/web/core/components/navigation/tab-navigation-root.tsx b/apps/web/core/components/navigation/tab-navigation-root.tsx index 732633b5e26..c18d45128fd 100644 --- a/apps/web/core/components/navigation/tab-navigation-root.tsx +++ b/apps/web/core/components/navigation/tab-navigation-root.tsx @@ -167,21 +167,23 @@ export const TabNavigationRoot: FC = observer((props) = /> {/* container for the tab navigation */} -
-
+
+
- handlePublishModal(true)} - /> +
+ handlePublishModal(true)} + /> +
-
+
diff --git a/apps/web/core/components/navigation/tab-navigation-visible-item.tsx b/apps/web/core/components/navigation/tab-navigation-visible-item.tsx index 1b5492ee6fa..7ed35fca6b8 100644 --- a/apps/web/core/components/navigation/tab-navigation-visible-item.tsx +++ b/apps/web/core/components/navigation/tab-navigation-visible-item.tsx @@ -1,8 +1,12 @@ import React from "react"; import { Link } from "react-router"; +import { PinOff } from "lucide-react"; +// plane imports import { useTranslation } from "@plane/i18n"; import { ContextMenu } from "@plane/propel/context-menu"; +import { SetAsDefaultIcon } from "@plane/propel/icons"; import { TabNavigationItem } from "@plane/propel/tab-navigation"; +// local imports import type { TNavigationItem } from "./tab-navigation-root"; import type { TTabPreferences } from "./tab-navigation-utils"; @@ -51,7 +55,9 @@ export const TabNavigationVisibleItem: React.FC e.stopPropagation(); onToggleDefault(item.key); }} + className="flex items-center gap-2 text-custom-text-200 transition-colors cursor-pointer" > + {isDefault ? "Clear default" : "Set as default"} e.stopPropagation(); onHide(item.key); }} + className="flex items-center gap-2 text-custom-text-200 transition-colors cursor-pointer" > + Hide in more menu diff --git a/apps/web/core/components/navigation/top-nav-power-k.tsx b/apps/web/core/components/navigation/top-nav-power-k.tsx index cb4acf6e432..6ca9af0a4a0 100644 --- a/apps/web/core/components/navigation/top-nav-power-k.tsx +++ b/apps/web/core/components/navigation/top-nav-power-k.tsx @@ -207,19 +207,21 @@ export const TopNavPowerK = observer(() => { ); return ( -
+
inputRef.current?.focus()} + role="button" > { )}
-
{ if (!memberId) return; - try { - await updateProjectMemberPreferences(workspaceSlug, projectId, memberId, { - default_tab: newPreferences.defaultTab, - hide_in_more_menu: newPreferences.hiddenTabs, - }); - } catch (error) { - console.error("Error updating tab preferences:", error); - setToast({ - type: TOAST_TYPE.ERROR, - title: "Error!", - message: "Something went wrong. Please try again later.", - }); - } + await updateProjectMemberPreferences(workspaceSlug, projectId, memberId, { + default_tab: newPreferences.defaultTab, + hide_in_more_menu: newPreferences.hiddenTabs, + }); }; /** @@ -79,7 +70,21 @@ export const useTabPreferences = (workspaceSlug: string, projectId: string): TTa const handleToggleDefaultTab = (tabKey: string) => { const newDefaultTab = tabKey === tabPreferences.defaultTab ? DEFAULT_TAB_KEY : tabKey; const newPreferences = { ...tabPreferences, defaultTab: newDefaultTab }; - updatePreferences(newPreferences); + updatePreferences(newPreferences) + .then(() => { + setToast({ + type: TOAST_TYPE.SUCCESS, + title: "Success!", + message: "Default tab updated successfully.", + }); + }) + .catch(() => { + setToast({ + type: TOAST_TYPE.ERROR, + title: "Error!", + message: "Failed to update default tab. Please try again later.", + }); + }); }; /** @@ -90,7 +95,16 @@ export const useTabPreferences = (workspaceSlug: string, projectId: string): TTa ...tabPreferences, hiddenTabs: [...tabPreferences.hiddenTabs, tabKey], }; - updatePreferences(newPreferences); + try { + updatePreferences(newPreferences); + } catch (error) { + console.error("Error hiding tab:", error); + setToast({ + type: TOAST_TYPE.ERROR, + title: "Error!", + message: "Failed to hide tab. Please try again later.", + }); + } }; /** @@ -101,7 +115,16 @@ export const useTabPreferences = (workspaceSlug: string, projectId: string): TTa ...tabPreferences, hiddenTabs: tabPreferences.hiddenTabs.filter((key) => key !== tabKey), }; - updatePreferences(newPreferences); + try { + updatePreferences(newPreferences); + } catch (error) { + console.error("Error showing tab:", error); + setToast({ + type: TOAST_TYPE.ERROR, + title: "Error!", + message: "Something went wrong. Please try again later.", + }); + } }; return { diff --git a/apps/web/core/components/workspace/sidebar/workspace-menu-root.tsx b/apps/web/core/components/workspace/sidebar/workspace-menu-root.tsx index ccdec707fc8..14f48f5a779 100644 --- a/apps/web/core/components/workspace/sidebar/workspace-menu-root.tsx +++ b/apps/web/core/components/workspace/sidebar/workspace-menu-root.tsx @@ -65,14 +65,13 @@ export const WorkspaceMenuRoot = observer(function WorkspaceMenuRoot(props: Work // Toggle sidebar dropdown state when either menu is open useEffect(() => { - if (isWorkspaceMenuOpen) toggleAnySidebarDropdown(true); - else toggleAnySidebarDropdown(false); - }, [isWorkspaceMenuOpen]); + toggleAnySidebarDropdown(isWorkspaceMenuOpen); + }, [isWorkspaceMenuOpen, toggleAnySidebarDropdown]); return ( + + + ); +}