Skip to content
Open
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
9 changes: 7 additions & 2 deletions apps/admin/react-router.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import type { Config } from "@react-router/dev/config";
import { joinUrlPath } from "@plane/utils";

const basePath = joinUrlPath(process.env.VITE_ADMIN_BASE_PATH ?? "", "/") ?? "/";
const normalizeBasePath = (value: string): string => {
const trimmed = value.trim();
if (!trimmed || trimmed === "/") return "/";
return `/${trimmed.replace(/^\/+|\/+$/g, "")}/`;
Comment on lines +3 to +6
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

Keep basename canonical here as well.

normalizeBasePath now preserves accidental internal //, so the admin router basename changes for env values that the old path joiner would have normalized. That leaves routing behavior dependent on slash typos in VITE_ADMIN_BASE_PATH.

Possible fix
 const normalizeBasePath = (value: string): string => {
   const trimmed = value.trim();
   if (!trimmed || trimmed === "/") return "/";
-  return `/${trimmed.replace(/^\/+|\/+$/g, "")}/`;
+  return `/${trimmed.replace(/\/+/g, "/").replace(/^\/+|\/+$/g, "")}/`;
 };
📝 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
const normalizeBasePath = (value: string): string => {
const trimmed = value.trim();
if (!trimmed || trimmed === "/") return "/";
return `/${trimmed.replace(/^\/+|\/+$/g, "")}/`;
const normalizeBasePath = (value: string): string => {
const trimmed = value.trim();
if (!trimmed || trimmed === "/") return "/";
return `/${trimmed.replace(/\/+/g, "/").replace(/^\/+|\/+$/g, "")}/`;
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/admin/react-router.config.ts` around lines 3 - 6, normalizeBasePath
currently preserves accidental internal '//' from VITE_ADMIN_BASE_PATH which can
change router basename; update normalizeBasePath to canonicalize repeated
slashes by collapsing any consecutive slashes into a single slash (e.g., apply a
global replace like replace(/\/+/g, "/")) after trimming, then ensure the result
has a single leading and single trailing slash (still returning "/" for
empty/root). Modify the normalizeBasePath function so it both trims and
normalizes internal duplicate slashes before returning the final basename.

};
Comment on lines +3 to +7
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

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

The base path normalization logic is duplicated between this file and apps/admin/vite.config.ts (and similarly in apps/space). Consider moving normalizeBasePath to a shared helper module and importing it so config files remain consistent.

Copilot uses AI. Check for mistakes.

const basePath = normalizeBasePath(process.env.VITE_ADMIN_BASE_PATH ?? "");

export default {
appDirectory: "app",
Expand Down
9 changes: 7 additions & 2 deletions apps/admin/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ import * as dotenv from "@dotenvx/dotenvx";
import { reactRouter } from "@react-router/dev/vite";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
import { joinUrlPath } from "@plane/utils";

const normalizeBasePath = (value: string): string => {
const trimmed = value.trim();
if (!trimmed || trimmed === "/") return "/";
return `/${trimmed.replace(/^\/+|\/+$/g, "")}/`;
Comment on lines +7 to +10
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

Normalize duplicate separators before setting Vite base.

This version preserves internal //, so VITE_ADMIN_BASE_PATH="admin//preview" now produces /admin//preview/. That regresses the old joinUrlPath behavior and can send the admin bundle to the wrong asset prefix.

Possible fix
 const normalizeBasePath = (value: string): string => {
   const trimmed = value.trim();
   if (!trimmed || trimmed === "/") return "/";
-  return `/${trimmed.replace(/^\/+|\/+$/g, "")}/`;
+  return `/${trimmed.replace(/\/+/g, "/").replace(/^\/+|\/+$/g, "")}/`;
 };
📝 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
const normalizeBasePath = (value: string): string => {
const trimmed = value.trim();
if (!trimmed || trimmed === "/") return "/";
return `/${trimmed.replace(/^\/+|\/+$/g, "")}/`;
const normalizeBasePath = (value: string): string => {
const trimmed = value.trim();
if (!trimmed || trimmed === "/") return "/";
return `/${trimmed.replace(/\/+/g, "/").replace(/^\/+|\/+$/g, "")}/`;
};
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/admin/vite.config.ts` around lines 7 - 10, normalizeBasePath currently
preserves internal duplicate slashes (e.g., "admin//preview") causing wrong Vite
base; update normalizeBasePath to collapse consecutive "/" into a single "/"
before stripping/adding edges so internal "//" becomes "/". Specifically, in
normalizeBasePath (and where VITE_ADMIN_BASE_PATH is used) perform a global
collapse of repeated separators (e.g., via a replace of /\/+/g with "/"), then
remove leading/trailing slashes and wrap with a single leading and trailing "/"
as the final return value.

};
Comment on lines +7 to +11
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

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

normalizeBasePath is duplicated here and in apps/admin/react-router.config.ts (and also duplicated in the space app). Consider extracting this into a shared helper to avoid future drift in base path normalization.

Copilot uses AI. Check for mistakes.

dotenv.config({ path: path.resolve(__dirname, ".env") });

Expand All @@ -15,7 +20,7 @@ const viteEnv = Object.keys(process.env)
return a;
}, {});

const basePath = joinUrlPath(process.env.VITE_ADMIN_BASE_PATH ?? "", "/") ?? "/";
const basePath = normalizeBasePath(process.env.VITE_ADMIN_BASE_PATH ?? "");

export default defineConfig(() => ({
base: basePath,
Expand Down
9 changes: 7 additions & 2 deletions apps/space/react-router.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import type { Config } from "@react-router/dev/config";
import { joinUrlPath } from "@plane/utils";

const basePath = joinUrlPath(process.env.VITE_SPACE_BASE_PATH ?? "", "/") ?? "/";
const normalizeBasePath = (value: string): string => {
const trimmed = value.trim();
if (!trimmed || trimmed === "/") return "/";
return `/${trimmed.replace(/^\/+|\/+$/g, "")}/`;
Comment on lines +3 to +6
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

Collapse repeated slashes before assigning basename.

normalizeBasePath only trims the ends, so VITE_SPACE_BASE_PATH="space//beta" now becomes /space//beta/. The previous joinUrlPath flow normalized duplicate separators, and React Router will mount under a different basename for the same env value.

Possible fix
 const normalizeBasePath = (value: string): string => {
   const trimmed = value.trim();
   if (!trimmed || trimmed === "/") return "/";
-  return `/${trimmed.replace(/^\/+|\/+$/g, "")}/`;
+  return `/${trimmed.replace(/\/+/g, "/").replace(/^\/+|\/+$/g, "")}/`;
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/space/react-router.config.ts` around lines 3 - 6, normalizeBasePath
currently only trims ends and preserves internal duplicate slashes; update
normalizeBasePath to collapse repeated slashes inside the path before trimming
and assembling the final basename (e.g., run a global collapse like replacing
/{2,} with / on the value or trimmed value), then strip leading/trailing slashes
and return "/" for empty root; locate the function normalizeBasePath to apply
this change so inputs like "space//beta" become "/space/beta/" consistently.

};
Comment on lines +3 to +7
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

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

normalizeBasePath is duplicated in both the router config and Vite config for this app. Consider importing it from a shared helper to ensure base and basename stay consistent if the normalization logic changes.

Copilot uses AI. Check for mistakes.

const basePath = normalizeBasePath(process.env.VITE_SPACE_BASE_PATH ?? "");

export default {
appDirectory: "app",
Expand Down
9 changes: 7 additions & 2 deletions apps/space/vite.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ import * as dotenv from "@dotenvx/dotenvx";
import { reactRouter } from "@react-router/dev/vite";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
import { joinUrlPath } from "@plane/utils";

const normalizeBasePath = (value: string): string => {
const trimmed = value.trim();
if (!trimmed || trimmed === "/") return "/";
return `/${trimmed.replace(/^\/+|\/+$/g, "")}/`;
Comment on lines +7 to +10
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

Preserve the old path normalization for Vite base.

This helper now leaves internal // intact. A value like VITE_SPACE_BASE_PATH="space//beta" will emit assets under /space//beta/, which is a behavior change from the old joinUrlPath flow. Please normalize repeated separators here so base stays aligned with the router basename.

Possible fix
 const normalizeBasePath = (value: string): string => {
   const trimmed = value.trim();
   if (!trimmed || trimmed === "/") return "/";
-  return `/${trimmed.replace(/^\/+|\/+$/g, "")}/`;
+  return `/${trimmed.replace(/\/+/g, "/").replace(/^\/+|\/+$/g, "")}/`;
 };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/space/vite.config.ts` around lines 7 - 10, The normalizeBasePath
function currently preserves internal double slashes (e.g., "space//beta") which
causes Vite base to emit "/space//beta/"; update normalizeBasePath to collapse
consecutive slashes into a single slash after trimming so the output aligns with
the router basename: in the normalizeBasePath function (and any related callers)
replace sequences of multiple "/" within the path (e.g., via a regex that
squeezes /{2,} to "/") before adding the leading/trailing "/" so inputs like
"space//beta" become "/space/beta/".

};
Comment on lines +7 to +11
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

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

normalizeBasePath is now duplicated here and in the corresponding react-router.config.ts (and similarly in apps/admin). Consider extracting this helper into a small shared module (e.g., apps/space/config/base-path.ts or a utilities package) so the normalization rules don’t diverge over time.

Copilot uses AI. Check for mistakes.

dotenv.config({ path: path.resolve(__dirname, ".env") });

Expand All @@ -15,7 +20,7 @@ const viteEnv = Object.keys(process.env)
return a;
}, {});

const basePath = joinUrlPath(process.env.VITE_SPACE_BASE_PATH ?? "", "/") ?? "/";
const basePath = normalizeBasePath(process.env.VITE_SPACE_BASE_PATH ?? "");

export default defineConfig(() => ({
base: basePath,
Expand Down
19 changes: 16 additions & 3 deletions apps/web/core/components/home/user-greetings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
* See the LICENSE file for details.
*/

import type { FC } from "react";
// plane types
import { useTranslation } from "@plane/i18n";
import type { IUser } from "@plane/types";
Expand All @@ -23,23 +22,37 @@ export function UserGreetingsView(props: IUserGreetingsView) {
// store hooks
const { t } = useTranslation();

// Use profile timezone when set to a real zone; treat UTC/Etc/UTC as unset (common
// backend default) so we show local time. Otherwise use browser/OS timezone.
const profileZone = user?.user_timezone?.trim();
const isUtcDefault =
!profileZone || profileZone === "UTC" || profileZone === "Etc/UTC" || profileZone.toLowerCase() === "utc";
const timeZone = isUtcDefault
? typeof Intl !== "undefined" && Intl.DateTimeFormat
? Intl.DateTimeFormat().resolvedOptions().timeZone
: undefined
Comment on lines +25 to +33
Copy link

Copilot AI Mar 9, 2026

Choose a reason for hiding this comment

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

isUtcDefault currently has redundant checks (profileZone === "UTC" and profileZone.toLowerCase() === "utc" overlap). Consider normalizing once (e.g., const normalized = profileZone?.trim().toUpperCase()) and comparing against a small set to keep the intent clear and avoid repeated string ops.

Copilot uses AI. Check for mistakes.
Copy link
Author

Choose a reason for hiding this comment

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

@copilot open a new pull request to apply changes based on this feedback

: profileZone;
Comment on lines +27 to +34
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

Etc/UTC is still case-sensitive here.

The fallback only matches the exact "Etc/UTC" spelling. Values like "etc/utc" or "ETC/UTC" — which the PR description calls out as defaults too — will still be treated as real profile zones, so those users keep seeing UTC instead of their local time.

Possible fix
   const profileZone = user?.user_timezone?.trim();
-  const isUtcDefault =
-    !profileZone || profileZone === "UTC" || profileZone === "Etc/UTC" || profileZone.toLowerCase() === "utc";
+  const isUtcDefault = !profileZone || /^(?:etc\/)?utc$/i.test(profileZone);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/core/components/home/user-greetings.tsx` around lines 27 - 34,
Normalize the profileZone before comparing so "Etc/UTC" is matched
case-insensitively: compute a trimmed, lowercased variant of user?.user_timezone
(e.g., profileZoneNormalized = user?.user_timezone?.trim().toLowerCase()) and
update isUtcDefault to check against normalized values ("utc" and "etc/utc")
instead of the current case-sensitive checks; keep the rest of the timeZone
logic the same and reference the existing identifiers profileZone, isUtcDefault,
and timeZone when making the change.


const hour = new Intl.DateTimeFormat("en-US", {
hour12: false,
hour: "numeric",
...(timeZone && { timeZone }),
}).format(currentTime);

const date = new Intl.DateTimeFormat("en-US", {
month: "short",
day: "numeric",
...(timeZone && { timeZone }),
}).format(currentTime);

const weekDay = new Intl.DateTimeFormat("en-US", {
weekday: "long",
...(timeZone && { timeZone }),
}).format(currentTime);

const timeString = new Intl.DateTimeFormat("en-US", {
timeZone: user?.user_timezone,
hour12: false, // Use 24-hour format
...(timeZone && { timeZone }),
hour12: false,
hour: "2-digit",
minute: "2-digit",
}).format(currentTime);
Expand Down
Loading