sync: canary cahnges to preview

This commit is contained in:
sriramveeraghanta 2025-07-04 13:57:40 +05:30
commit 4f5272c8af
23 changed files with 88 additions and 29 deletions

View file

@ -1,7 +1,7 @@
{ {
"name": "admin", "name": "admin",
"description": "Admin UI for Plane", "description": "Admin UI for Plane",
"version": "0.27.0", "version": "0.27.1",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"private": true, "private": true,
"scripts": { "scripts": {

View file

@ -1,6 +1,6 @@
{ {
"name": "live", "name": "live",
"version": "0.27.0", "version": "0.27.1",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"description": "A realtime collaborative server powers Plane's rich text editor", "description": "A realtime collaborative server powers Plane's rich text editor",
"main": "./src/server.ts", "main": "./src/server.ts",

View file

@ -1,6 +1,6 @@
{ {
"name": "plane-api", "name": "plane-api",
"version": "0.27.0", "version": "0.27.1",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"private": true, "private": true,
"description": "API server powering Plane's backend" "description": "API server powering Plane's backend"

View file

@ -1,6 +1,6 @@
{ {
"name": "space", "name": "space",
"version": "0.27.0", "version": "0.27.1",
"private": true, "private": true,
"license": "AGPL-3.0", "license": "AGPL-3.0",
"scripts": { "scripts": {

View file

@ -6,7 +6,7 @@ import { Disclosure } from "@headlessui/react";
// plane imports // plane imports
import { useTranslation } from "@plane/i18n"; import { useTranslation } from "@plane/i18n";
import { EUserWorkspaceRoles } from "@plane/types"; import { EUserWorkspaceRoles } from "@plane/types";
import { cn } from "@plane/utils"; import { cn, joinUrlPath } from "@plane/utils";
// hooks // hooks
import { useUserSettings } from "@/hooks/store"; import { useUserSettings } from "@/hooks/store";
@ -72,7 +72,11 @@ const SettingsSidebarNavItem = observer((props: TSettingsSidebarNavItemProps) =>
{renderChildren ? ( {renderChildren ? (
<div className={buttonClass}>{titleElement}</div> <div className={buttonClass}>{titleElement}</div>
) : ( ) : (
<Link href={`/${workspaceSlug}/${setting.href}`} className={buttonClass} onClick={() => toggleSidebar(true)}> <Link
href={joinUrlPath(workspaceSlug, setting.href)}
className={buttonClass}
onClick={() => toggleSidebar(true)}
>
{titleElement} {titleElement}
</Link> </Link>
)} )}

View file

@ -46,10 +46,11 @@ export const SettingsSidebar = observer((props: SettingsSidebarProps) => {
<SettingsSidebarHeader customHeader={customHeader} /> <SettingsSidebarHeader customHeader={customHeader} />
{/* Navigation */} {/* Navigation */}
<div className="divide-y divide-custom-border-100 overflow-x-hidden w-full h-full overflow-y-scroll"> <div className="divide-y divide-custom-border-100 overflow-x-hidden w-full h-full overflow-y-scroll">
{categories.map((category) => ( {categories.map((category) => {
<div key={category} className="py-3"> if (groupedSettings[category].length === 0) return null;
<span className="text-sm font-semibold text-custom-text-350 capitalize mb-2">{t(category)}</span> return (
{groupedSettings[category].length > 0 && ( <div key={category} className="py-3">
<span className="text-sm font-semibold text-custom-text-350 capitalize mb-2">{t(category)}</span>
<div className="relative flex flex-col gap-0.5 h-full mt-2"> <div className="relative flex flex-col gap-0.5 h-full mt-2">
{groupedSettings[category].map( {groupedSettings[category].map(
(setting) => (setting) =>
@ -66,9 +67,9 @@ export const SettingsSidebar = observer((props: SettingsSidebarProps) => {
) )
)} )}
</div> </div>
)} </div>
</div> );
))} })}
</div> </div>
</div> </div>
); );

View file

@ -1,6 +1,6 @@
{ {
"name": "web", "name": "web",
"version": "0.27.0", "version": "0.27.1",
"private": true, "private": true,
"license": "AGPL-3.0", "license": "AGPL-3.0",
"scripts": { "scripts": {

View file

@ -2,7 +2,7 @@
"name": "plane", "name": "plane",
"description": "Open-source project management that unlocks customer value", "description": "Open-source project management that unlocks customer value",
"repository": "https://github.com/makeplane/plane.git", "repository": "https://github.com/makeplane/plane.git",
"version": "0.27.0", "version": "0.27.1",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"private": true, "private": true,
"workspaces": [ "workspaces": [

View file

@ -1,6 +1,6 @@
{ {
"name": "@plane/constants", "name": "@plane/constants",
"version": "0.27.0", "version": "0.27.1",
"private": true, "private": true,
"license": "AGPL-3.0", "license": "AGPL-3.0",
"type": "module", "type": "module",

View file

@ -1,6 +1,6 @@
{ {
"name": "@plane/editor", "name": "@plane/editor",
"version": "0.27.0", "version": "0.27.1",
"description": "Core Editor that powers Plane", "description": "Core Editor that powers Plane",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"private": true, "private": true,

View file

@ -1,7 +1,7 @@
{ {
"name": "@plane/eslint-config", "name": "@plane/eslint-config",
"private": true, "private": true,
"version": "0.27.0", "version": "0.27.1",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"files": [ "files": [
"library.js", "library.js",

View file

@ -1,6 +1,6 @@
{ {
"name": "@plane/hooks", "name": "@plane/hooks",
"version": "0.27.0", "version": "0.27.1",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"description": "React hooks that are shared across multiple apps internally", "description": "React hooks that are shared across multiple apps internally",
"private": true, "private": true,

View file

@ -1,6 +1,6 @@
{ {
"name": "@plane/i18n", "name": "@plane/i18n",
"version": "0.27.0", "version": "0.27.1",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"description": "I18n shared across multiple apps internally", "description": "I18n shared across multiple apps internally",
"private": true, "private": true,

View file

@ -1,6 +1,6 @@
{ {
"name": "@plane/logger", "name": "@plane/logger",
"version": "0.27.0", "version": "0.27.1",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"description": "Logger shared across multiple apps internally", "description": "Logger shared across multiple apps internally",
"private": true, "private": true,

View file

@ -1,6 +1,6 @@
{ {
"name": "@plane/propel", "name": "@plane/propel",
"version": "0.27.0", "version": "0.27.1",
"private": true, "private": true,
"license": "AGPL-3.0", "license": "AGPL-3.0",
"scripts": { "scripts": {

View file

@ -1,6 +1,6 @@
{ {
"name": "@plane/services", "name": "@plane/services",
"version": "0.27.0", "version": "0.27.1",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"private": true, "private": true,
"main": "./src/index.ts", "main": "./src/index.ts",

View file

@ -1,6 +1,6 @@
{ {
"name": "@plane/shared-state", "name": "@plane/shared-state",
"version": "0.27.0", "version": "0.27.1",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"description": "Shared state shared across multiple apps internally", "description": "Shared state shared across multiple apps internally",
"private": true, "private": true,

View file

@ -1,6 +1,6 @@
{ {
"name": "@plane/tailwind-config", "name": "@plane/tailwind-config",
"version": "0.27.0", "version": "0.27.1",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"description": "common tailwind configuration across monorepo", "description": "common tailwind configuration across monorepo",
"main": "tailwind.config.js", "main": "tailwind.config.js",

View file

@ -1,6 +1,6 @@
{ {
"name": "@plane/types", "name": "@plane/types",
"version": "0.27.0", "version": "0.27.1",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"private": true, "private": true,
"type": "module", "type": "module",

View file

@ -1,6 +1,6 @@
{ {
"name": "@plane/typescript-config", "name": "@plane/typescript-config",
"version": "0.27.0", "version": "0.27.1",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"private": true, "private": true,
"files": [ "files": [

View file

@ -2,7 +2,7 @@
"name": "@plane/ui", "name": "@plane/ui",
"description": "UI components shared across multiple apps internally", "description": "UI components shared across multiple apps internally",
"private": true, "private": true,
"version": "0.27.0", "version": "0.27.1",
"main": "./dist/index.js", "main": "./dist/index.js",
"module": "./dist/index.mjs", "module": "./dist/index.mjs",
"types": "./dist/index.d.ts", "types": "./dist/index.d.ts",

View file

@ -1,6 +1,6 @@
{ {
"name": "@plane/utils", "name": "@plane/utils",
"version": "0.27.0", "version": "0.27.1",
"description": "Helper functions shared across multiple apps internally", "description": "Helper functions shared across multiple apps internally",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"private": true, "private": true,

View file

@ -318,3 +318,57 @@ export const copyTextToClipboard = async (text: string): Promise<void> => {
} }
await navigator.clipboard.writeText(text); 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("/")}` : "";
}
};