Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -259,14 +259,14 @@ export const CustomizeNavigationDialog: FC<TCustomizeNavigationDialogProps> = ob
<div className="space-y-3">
{/* Navigation Mode Radio Buttons */}
<div className="space-y-2">
<label className="flex items-center gap-2 px-2 py-1.5 rounded-md hover:bg-custom-background-90 cursor-pointer">
<label className="flex gap-2 px-2 py-1.5 rounded-md hover:bg-custom-background-90 cursor-pointer">
<input
type="radio"
name="navigation-mode"
value="accordion"
checked={projectPreferences.navigationMode === "accordion"}
onChange={() => updateNavigationMode("accordion")}
className="size-4 text-custom-primary-100 focus:ring-custom-primary-100"
className="size-4 text-custom-primary-100 focus:ring-custom-primary-100 mt-1"
/>
<div className="flex-1">
<div className="text-sm text-custom-text-200">{t("accordion_navigation_control")}</div>
Expand All @@ -276,14 +276,14 @@ export const CustomizeNavigationDialog: FC<TCustomizeNavigationDialogProps> = ob
</div>
</label>

<label className="flex items-center gap-2 px-2 py-1.5 rounded-md hover:bg-custom-background-90 cursor-pointer">
<label className="flex gap-2 px-2 py-1.5 rounded-md hover:bg-custom-background-90 cursor-pointer">
<input
type="radio"
name="navigation-mode"
value="horizontal"
checked={projectPreferences.navigationMode === "horizontal"}
onChange={() => updateNavigationMode("horizontal")}
className="size-4 text-custom-primary-100 focus:ring-custom-primary-100"
className="size-4 text-custom-primary-100 focus:ring-custom-primary-100 mt-1"
/>
<div className="flex-1">
<div className="text-sm text-custom-text-200">{t("horizontal_navigation_bar")}</div>
Expand Down
45 changes: 20 additions & 25 deletions apps/web/core/components/workspace/sidebar/user-menu-root.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { useState, useEffect } from "react";
import { observer } from "mobx-react";
import Link from "next/link";
import { useParams } from "next/navigation";
import { useParams, useRouter } from "next/navigation";
// icons
import { LogOut, Settings, Settings2 } from "lucide-react";
// plane imports
Expand All @@ -22,6 +21,8 @@ type Props = {
export const UserMenuRoot = observer(function UserMenuRoot(props: Props) {
const { size = "sm" } = props;
const { workspaceSlug } = useParams();
// router
const router = useRouter();
// store hooks
const { toggleAnySidebarDropdown } = useAppTheme();
const { data: currentUser } = useUser();
Expand Down Expand Up @@ -76,22 +77,18 @@ export const UserMenuRoot = observer(function UserMenuRoot(props: Props) {
>
<div className="flex flex-col gap-2">
<span className="px-2 text-custom-sidebar-text-200 truncate">{currentUser?.email}</span>
<Link href={`/${workspaceSlug}/settings/account`}>
<CustomMenu.MenuItem>
<div className="flex w-full items-center gap-2 rounded text-xs">
<Settings className="h-4 w-4 stroke-[1.5]" />
<span>{t("settings")}</span>
</div>
</CustomMenu.MenuItem>
</Link>
<Link href={`/${workspaceSlug}/settings/account/preferences`}>
<CustomMenu.MenuItem>
<div className="flex w-full items-center gap-2 rounded text-xs">
<Settings2 className="h-4 w-4 stroke-[1.5]" />
<span>Preferences</span>
</div>
</CustomMenu.MenuItem>
</Link>
<CustomMenu.MenuItem onClick={() => router.push(`/${workspaceSlug}/settings/account`)}>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, read the component file to see the useParams() usage
cat -n apps/web/core/components/workspace/sidebar/user-menu-root.tsx | head -100

Repository: makeplane/plane

Length of output: 4286


🏁 Script executed:

# Find where UserMenuRoot is imported and used
rg -n "UserMenuRoot" --type tsx --type ts -C 2

Repository: makeplane/plane

Length of output: 86


🏁 Script executed:

# Check the useParams hook and its type signature in the codebase
rg -n "useParams" apps/web/core/components/workspace/sidebar/user-menu-root.tsx -A 3 -B 3

Repository: makeplane/plane

Length of output: 529


🏁 Script executed:

# Search for UserMenuRoot imports and usage
rg "UserMenuRoot" --type ts -C 3

Repository: makeplane/plane

Length of output: 2208


🏁 Script executed:

# Check the actual routes where UserMenuRoot might be used
rg "sidebar" apps/web/core/components/workspace --type ts -l | head -10

Repository: makeplane/plane

Length of output: 719


🏁 Script executed:

# Look for type definitions or custom hooks for useParams
fd "use-params" apps/web

Repository: makeplane/plane

Length of output: 41


🏁 Script executed:

# Check if there's a Next.js configuration that might affect useParams
cat tsconfig.json | head -50

Repository: makeplane/plane

Length of output: 104


🏁 Script executed:

# Check the top-navigation-root.tsx file where UserMenuRoot is used
cat -n apps/web/ce/components/navigations/top-navigation-root.tsx | head -50

Repository: makeplane/plane

Length of output: 2461


🏁 Script executed:

# Look for app router structure with workspaceSlug
find apps/web -type f -name "layout.tsx" -o -name "page.tsx" | grep -E "workspace|workspaceSlug" | head -20

Repository: makeplane/plane

Length of output: 1631


🏁 Script executed:

# Search for route structure with [workspaceSlug]
rg "\[workspaceSlug\]" --type ts -l

Repository: makeplane/plane

Length of output: 1003


🏁 Script executed:

# Check the actual route segments in the app directory
find apps/web -type d -name "\[workspaceSlug\]" 2>/dev/null

Repository: makeplane/plane

Length of output: 93


🏁 Script executed:

# Find tsconfig.json in the workspace root or apps/web
find apps/web -name "tsconfig.json" -o -name "tsconfig*.json" | head -5

Repository: makeplane/plane

Length of output: 81


🏁 Script executed:

# Check if there's a root tsconfig
find . -maxdepth 2 -name "tsconfig.json" 2>/dev/null

Repository: makeplane/plane

Length of output: 41


🏁 Script executed:

# Look at how other components in the same file handle useParams
rg "useParams()" apps/web/core/components/workspace/sidebar --type ts -A 3 -B 1

Repository: makeplane/plane

Length of output: 16068


🏁 Script executed:

# Check if workspaceSlug is used elsewhere without guards in same directory
rg "workspaceSlug" apps/web/core/components/workspace/sidebar/user-menu-root.tsx -C 5

Repository: makeplane/plane

Length of output: 1232


🏁 Script executed:

# Check TypeScript strictNullChecks setting
cat apps/web/tsconfig.json | grep -A 5 "strictNullChecks\|compilerOptions"

Repository: makeplane/plane

Length of output: 388


Add type guards for workspaceSlug and translate the "Preferences" label.

workspaceSlug from useParams() is potentially undefined under strict null checks (strictNullChecks: true in tsconfig). This would result in navigating to /${undefined}/settings/account. Add a guard or assertion (see quick-actions.tsx for the pattern in this codebase). Additionally, the "Preferences" label on line 89 is hardcoded while "settings" on line 83 is translated—apply the same t() call for consistency.

🤖 Prompt for AI Agents
In apps/web/core/components/workspace/sidebar/user-menu-root.tsx around line 80,
add a type guard/assertion for workspaceSlug (from useParams()) before using it
in router.push to avoid navigating to /undefined — follow the pattern used in
quick-actions.tsx (check for null/undefined and return early or assert with a
non-null operator where appropriate). Also replace the hardcoded "Preferences"
label on line 89 with the translated string via t() to match the translated
"settings" label usage.

<div className="flex w-full items-center gap-2 rounded text-xs">
<Settings className="h-4 w-4 stroke-[1.5]" />
<span>{t("settings")}</span>
</div>
</CustomMenu.MenuItem>
<CustomMenu.MenuItem onClick={() => router.push(`/${workspaceSlug}/settings/account/preferences`)}>
<div className="flex w-full items-center gap-2 rounded text-xs">
<Settings2 className="h-4 w-4 stroke-[1.5]" />
<span>Preferences</span>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Use translation for "Preferences" label.

Line 89 hardcodes "Preferences" while line 83 uses {t("settings")} for consistency with i18n. This prevents proper localization.

Apply this diff to use translation:

-            <span>Preferences</span>
+            <span>{t("preferences")}</span>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<span>Preferences</span>
<span>{t("preferences")}</span>
🤖 Prompt for AI Agents
In apps/web/core/components/workspace/sidebar/user-menu-root.tsx around line 89,
the "Preferences" label is hardcoded; replace the literal string with the i18n
call (e.g., use {t("preferences")}) to match the pattern used for
{t("settings")} for proper localization, and add the "preferences" key to the
locale translation files if it does not already exist.

</div>
</CustomMenu.MenuItem>
</div>
<div className="my-1 border-t border-custom-border-200" />
<div className={`${isUserInstanceAdmin ? "pb-2" : ""}`}>
Expand All @@ -110,13 +107,11 @@ export const UserMenuRoot = observer(function UserMenuRoot(props: Props) {
<>
<div className="my-1 border-t border-custom-border-200" />
<div className="px-1">
<Link href={GOD_MODE_URL}>
<CustomMenu.MenuItem>
<div className="flex w-full items-center justify-center rounded bg-custom-primary-100/20 px-2 py-1 text-xs font-medium text-custom-primary-100 hover:bg-custom-primary-100/30 hover:text-custom-primary-200">
{t("enter_god_mode")}
</div>
</CustomMenu.MenuItem>
</Link>
<CustomMenu.MenuItem onClick={() => router.push(GOD_MODE_URL)}>
<div className="flex w-full items-center justify-center rounded bg-custom-primary-100/20 px-2 py-1 text-xs font-medium text-custom-primary-100 hover:bg-custom-primary-100/30 hover:text-custom-primary-200">
{t("enter_god_mode")}
</div>
</CustomMenu.MenuItem>
</div>
</>
)}
Expand Down
Loading