diff --git a/apps/admin/package.json b/apps/admin/package.json
index 83c01a57e..6aa37876f 100644
--- a/apps/admin/package.json
+++ b/apps/admin/package.json
@@ -1,7 +1,7 @@
{
"name": "admin",
"description": "Admin UI for Plane",
- "version": "0.27.0",
+ "version": "0.27.1",
"license": "AGPL-3.0",
"private": true,
"scripts": {
diff --git a/apps/live/package.json b/apps/live/package.json
index 54899d691..b9de661bb 100644
--- a/apps/live/package.json
+++ b/apps/live/package.json
@@ -1,6 +1,6 @@
{
"name": "live",
- "version": "0.27.0",
+ "version": "0.27.1",
"license": "AGPL-3.0",
"description": "A realtime collaborative server powers Plane's rich text editor",
"main": "./src/server.ts",
diff --git a/apps/server/package.json b/apps/server/package.json
index 6b3118020..46d5fc5e6 100644
--- a/apps/server/package.json
+++ b/apps/server/package.json
@@ -1,6 +1,6 @@
{
"name": "plane-api",
- "version": "0.27.0",
+ "version": "0.27.1",
"license": "AGPL-3.0",
"private": true,
"description": "API server powering Plane's backend"
diff --git a/apps/space/package.json b/apps/space/package.json
index 2159adf56..13356047e 100644
--- a/apps/space/package.json
+++ b/apps/space/package.json
@@ -1,6 +1,6 @@
{
"name": "space",
- "version": "0.27.0",
+ "version": "0.27.1",
"private": true,
"license": "AGPL-3.0",
"scripts": {
diff --git a/apps/web/core/components/settings/sidebar/nav-item.tsx b/apps/web/core/components/settings/sidebar/nav-item.tsx
index 6dcc36fa3..af1d139d6 100644
--- a/apps/web/core/components/settings/sidebar/nav-item.tsx
+++ b/apps/web/core/components/settings/sidebar/nav-item.tsx
@@ -6,7 +6,7 @@ import { Disclosure } from "@headlessui/react";
// plane imports
import { useTranslation } from "@plane/i18n";
import { EUserWorkspaceRoles } from "@plane/types";
-import { cn } from "@plane/utils";
+import { cn, joinUrlPath } from "@plane/utils";
// hooks
import { useUserSettings } from "@/hooks/store";
@@ -72,7 +72,11 @@ const SettingsSidebarNavItem = observer((props: TSettingsSidebarNavItemProps) =>
{renderChildren ? (
{titleElement}
) : (
- toggleSidebar(true)}>
+ toggleSidebar(true)}
+ >
{titleElement}
)}
diff --git a/apps/web/core/components/settings/sidebar/root.tsx b/apps/web/core/components/settings/sidebar/root.tsx
index b29425f9e..c681a8cc1 100644
--- a/apps/web/core/components/settings/sidebar/root.tsx
+++ b/apps/web/core/components/settings/sidebar/root.tsx
@@ -46,10 +46,11 @@ export const SettingsSidebar = observer((props: SettingsSidebarProps) => {
{/* Navigation */}
- {categories.map((category) => (
-
-
{t(category)}
- {groupedSettings[category].length > 0 && (
+ {categories.map((category) => {
+ if (groupedSettings[category].length === 0) return null;
+ return (
+
+
{t(category)}
{groupedSettings[category].map(
(setting) =>
@@ -66,9 +67,9 @@ export const SettingsSidebar = observer((props: SettingsSidebarProps) => {
)
)}
- )}
-
- ))}
+
+ );
+ })}
);
diff --git a/apps/web/package.json b/apps/web/package.json
index c1f4d1d2c..74ab1dde2 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -1,6 +1,6 @@
{
"name": "web",
- "version": "0.27.0",
+ "version": "0.27.1",
"private": true,
"license": "AGPL-3.0",
"scripts": {
diff --git a/package.json b/package.json
index 6faf02651..c29c323a2 100644
--- a/package.json
+++ b/package.json
@@ -2,7 +2,7 @@
"name": "plane",
"description": "Open-source project management that unlocks customer value",
"repository": "https://github.com/makeplane/plane.git",
- "version": "0.27.0",
+ "version": "0.27.1",
"license": "AGPL-3.0",
"private": true,
"workspaces": [
diff --git a/packages/constants/package.json b/packages/constants/package.json
index f0de91401..f3e176772 100644
--- a/packages/constants/package.json
+++ b/packages/constants/package.json
@@ -1,6 +1,6 @@
{
"name": "@plane/constants",
- "version": "0.27.0",
+ "version": "0.27.1",
"private": true,
"license": "AGPL-3.0",
"type": "module",
diff --git a/packages/editor/package.json b/packages/editor/package.json
index d1d854336..ed1794c5c 100644
--- a/packages/editor/package.json
+++ b/packages/editor/package.json
@@ -1,6 +1,6 @@
{
"name": "@plane/editor",
- "version": "0.27.0",
+ "version": "0.27.1",
"description": "Core Editor that powers Plane",
"license": "AGPL-3.0",
"private": true,
diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json
index 0e7e2382b..98a831bbc 100644
--- a/packages/eslint-config/package.json
+++ b/packages/eslint-config/package.json
@@ -1,7 +1,7 @@
{
"name": "@plane/eslint-config",
"private": true,
- "version": "0.27.0",
+ "version": "0.27.1",
"license": "AGPL-3.0",
"files": [
"library.js",
diff --git a/packages/hooks/package.json b/packages/hooks/package.json
index 81484513d..52b57de7e 100644
--- a/packages/hooks/package.json
+++ b/packages/hooks/package.json
@@ -1,6 +1,6 @@
{
"name": "@plane/hooks",
- "version": "0.27.0",
+ "version": "0.27.1",
"license": "AGPL-3.0",
"description": "React hooks that are shared across multiple apps internally",
"private": true,
diff --git a/packages/i18n/package.json b/packages/i18n/package.json
index d4faaf017..ce07c6c86 100644
--- a/packages/i18n/package.json
+++ b/packages/i18n/package.json
@@ -1,6 +1,6 @@
{
"name": "@plane/i18n",
- "version": "0.27.0",
+ "version": "0.27.1",
"license": "AGPL-3.0",
"description": "I18n shared across multiple apps internally",
"private": true,
diff --git a/packages/logger/package.json b/packages/logger/package.json
index ca2a0dbe2..24dc3c789 100644
--- a/packages/logger/package.json
+++ b/packages/logger/package.json
@@ -1,6 +1,6 @@
{
"name": "@plane/logger",
- "version": "0.27.0",
+ "version": "0.27.1",
"license": "AGPL-3.0",
"description": "Logger shared across multiple apps internally",
"private": true,
diff --git a/packages/propel/package.json b/packages/propel/package.json
index e6922c718..2a683a1e5 100644
--- a/packages/propel/package.json
+++ b/packages/propel/package.json
@@ -1,6 +1,6 @@
{
"name": "@plane/propel",
- "version": "0.27.0",
+ "version": "0.27.1",
"private": true,
"license": "AGPL-3.0",
"scripts": {
diff --git a/packages/services/package.json b/packages/services/package.json
index 449e9efed..43c44dc95 100644
--- a/packages/services/package.json
+++ b/packages/services/package.json
@@ -1,6 +1,6 @@
{
"name": "@plane/services",
- "version": "0.27.0",
+ "version": "0.27.1",
"license": "AGPL-3.0",
"private": true,
"main": "./src/index.ts",
diff --git a/packages/shared-state/package.json b/packages/shared-state/package.json
index 167c1794a..a68adb403 100644
--- a/packages/shared-state/package.json
+++ b/packages/shared-state/package.json
@@ -1,6 +1,6 @@
{
"name": "@plane/shared-state",
- "version": "0.27.0",
+ "version": "0.27.1",
"license": "AGPL-3.0",
"description": "Shared state shared across multiple apps internally",
"private": true,
diff --git a/packages/tailwind-config/package.json b/packages/tailwind-config/package.json
index 8e6f51822..524062c5e 100644
--- a/packages/tailwind-config/package.json
+++ b/packages/tailwind-config/package.json
@@ -1,6 +1,6 @@
{
"name": "@plane/tailwind-config",
- "version": "0.27.0",
+ "version": "0.27.1",
"license": "AGPL-3.0",
"description": "common tailwind configuration across monorepo",
"main": "tailwind.config.js",
diff --git a/packages/types/package.json b/packages/types/package.json
index f4ae50831..5fc431692 100644
--- a/packages/types/package.json
+++ b/packages/types/package.json
@@ -1,6 +1,6 @@
{
"name": "@plane/types",
- "version": "0.27.0",
+ "version": "0.27.1",
"license": "AGPL-3.0",
"private": true,
"type": "module",
diff --git a/packages/typescript-config/package.json b/packages/typescript-config/package.json
index f5b3d1a85..58fe486eb 100644
--- a/packages/typescript-config/package.json
+++ b/packages/typescript-config/package.json
@@ -1,6 +1,6 @@
{
"name": "@plane/typescript-config",
- "version": "0.27.0",
+ "version": "0.27.1",
"license": "AGPL-3.0",
"private": true,
"files": [
diff --git a/packages/ui/package.json b/packages/ui/package.json
index bf388c8fb..720b9e0aa 100644
--- a/packages/ui/package.json
+++ b/packages/ui/package.json
@@ -2,7 +2,7 @@
"name": "@plane/ui",
"description": "UI components shared across multiple apps internally",
"private": true,
- "version": "0.27.0",
+ "version": "0.27.1",
"main": "./dist/index.js",
"module": "./dist/index.mjs",
"types": "./dist/index.d.ts",
diff --git a/packages/utils/package.json b/packages/utils/package.json
index ca53a18a0..f4d329494 100644
--- a/packages/utils/package.json
+++ b/packages/utils/package.json
@@ -1,6 +1,6 @@
{
"name": "@plane/utils",
- "version": "0.27.0",
+ "version": "0.27.1",
"description": "Helper functions shared across multiple apps internally",
"license": "AGPL-3.0",
"private": true,
diff --git a/packages/utils/src/string.ts b/packages/utils/src/string.ts
index 1bb23c81d..58822e1e9 100644
--- a/packages/utils/src/string.ts
+++ b/packages/utils/src/string.ts
@@ -318,3 +318,57 @@ export const copyTextToClipboard = async (text: string): Promise => {
}
await navigator.clipboard.writeText(text);
};
+
+
+/**
+ * @description Joins URL path segments properly, removing duplicate slashes using URL encoding
+ * @param {...string} segments - URL path segments to join
+ * @returns {string} Properly joined URL path
+ * @example
+ * joinUrlPath("/workspace", "/projects") => "/workspace/projects"
+ * joinUrlPath("/workspace", "projects") => "/workspace/projects"
+ * joinUrlPath("workspace", "projects") => "/workspace/projects"
+ * joinUrlPath("/workspace/", "/projects/") => "/workspace/projects/"
+ */
+export const joinUrlPath = (...segments: string[]): string => {
+ if (segments.length === 0) return "";
+
+ // Filter out empty segments
+ const validSegments = segments.filter((segment) => segment !== "");
+ if (validSegments.length === 0) return "";
+
+ // Process segments to normalize slashes
+ const processedSegments = validSegments.map((segment, index) => {
+ let processed = segment;
+
+ // Remove leading slashes from all segments except the first
+ if (index > 0) {
+ while (processed.startsWith("/")) {
+ processed = processed.substring(1);
+ }
+ }
+
+ // Remove trailing slashes from all segments except the last
+ if (index < validSegments.length - 1) {
+ while (processed.endsWith("/")) {
+ processed = processed.substring(0, processed.length - 1);
+ }
+ }
+
+ return processed;
+ });
+
+ // Join segments with single slash
+ const joined = processedSegments.join("/");
+
+ // Use URL constructor to normalize the path and handle double slashes
+ try {
+ // Create a dummy URL to leverage browser's URL normalization
+ const dummyUrl = new URL(`http://example.com/${joined}`);
+ return dummyUrl.pathname;
+ } catch {
+ // Fallback: manually handle double slashes by splitting and filtering
+ const pathParts = joined.split("/").filter((part) => part !== "");
+ return pathParts.length > 0 ? `/${pathParts.join("/")}` : "";
+ }
+};
\ No newline at end of file