Skip to content

Conversation

@prateekshourya29
Copy link
Member

@prateekshourya29 prateekshourya29 commented Dec 23, 2025

Description

This PR includes refactoring of auth-related components for both the admin and web applications, as well as some updates to the new design system.

Type of Change

  • Code refactoring

Note

Modernizes admin and web authentication UX and shared infrastructure.

  • Layout/UX: Replaces page scaffolding with PageWrapper and new AdminHeader (breadcrumbs), standardizes loaders, spacing, and copy (e.g., “Saving...” → “Saving”).
  • Navigation: Introduces dynamic sidebar via useSidebarMenu; tweaks help/user sections and shows instance version.
  • Auth config (admin): Centralizes providers with useAuthenticationModes; updates GitHub/GitLab/Google/Gitea pages to use AuthenticationMethodCard and consistent toggles.
  • OAuth (web/space): Adds useOAuthConfig hooks (core/extended) to generate provider options; refactors auth screens (AuthRoot, banners/headers) to consume them.
  • Types/utilities: Adds typed auth mode keys and OAuth option types; extracts route merge helper; fixes Yjs provider typing; safer searchParams access.
  • Cleanup: Removes obsolete CE/EE re-exports and upgrade button; consolidates providers (AppProviders).

Written by Cursor Bugbot for commit 96aa04b. This will update automatically on new commits. Configure here.

Summary by CodeRabbit

  • New Features

    • Consistent PageWrapper layout applied across admin pages; several pages now expose page metadata (browser titles)
    • Dynamic sidebar navigation and a hook-based OAuth provider system
  • Bug Fixes

    • Safer handling of URL search params to prevent initialization errors
  • Style

    • Button submit labels simplified ("Saving..." → "Saving")
    • Refined spacing, padding, backgrounds, icon sizing and color opacity for visual consistency
  • Refactor

    • Pages reorganized to use PageWrapper; authentication flows migrated to hook-driven configuration
  • Chores

    • Removed legacy upgrade button and cleaned up barrel re-exports

✏️ Tip: You can customize this high-level summary in your review settings.

@prateekshourya29 prateekshourya29 self-assigned this Dec 23, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 23, 2025

📝 Walkthrough

Walkthrough

Introduces a PageWrapper layout across many admin pages, replaces inline OAuth/OAuth-config construction with hook-driven config (core + extended), reorganizes providers and sidebar/menu hooks, adds header label mappings and PageWrapper component, and applies numerous presentational class and minor API surface adjustments (type additions, removed barrel exports).

Changes

Cohort / File(s) Summary
PageWrapper Layout & Page Restructuring
apps/admin/core/components/common/page-wrapper.tsx, apps/admin/app/(all)/(dashboard)/*/page.tsx, apps/admin/app/(all)/(dashboard)/workspace/create/page.tsx, apps/admin/app/(all)/(dashboard)/workspace/page.tsx
New PageWrapper component; many pages converted to use PageWrapper (header → header/customHeader). Several pages add meta/default exports; conditional loader/form rendering preserved.
Form UI & Button Text Updates
apps/admin/app/(all)/(dashboard)/**/form.tsx, apps/admin/app/(all)/(dashboard)/email/test-email-modal.tsx
Removed ellipses from submitting button labels (e.g., "Saving..." → "Saving"); small spacing/background/token class tweaks across multiple forms.
OAuth: Admin — Map + Hook
apps/admin/core/hooks/oauth/core.tsx, apps/admin/core/hooks/oauth/index.ts, apps/admin/core/hooks/oauth/types.ts
Replaced array-based getAuthenticationModes with a keyed map getCoreAuthenticationModesMap and introduced useAuthenticationModes hook; removed AuthenticationModes component and added typed props.
OAuth: Web — Hooks & config merge
apps/web/core/hooks/oauth/{core,extended,index}.ts, apps/web/ce/helpers/oauth-config.tsx (deleted)
Added useCoreOAuthConfig and useExtendedOAuthConfig; new useOAuthConfig merges them; removed old OAUTH_CONFIG helper (deleted).
Auth UI Refactors (banner/header/root)
apps/web/core/components/account/auth-forms/{auth-root,auth-banner,auth-header}.tsx, apps/web/core/components/auth-screens/header.tsx, apps/space/core/components/account/auth-forms/auth-root.tsx
Auth components now use useOAuthConfig; AuthBanner API changed to message prop; AuthHeaderBase extracted for shared header rendering.
Sidebar menu: hook-driven
apps/admin/core/hooks/use-sidebar-menu/{core,index,types}.ts, apps/admin/app/(all)/(dashboard)/sidebar-menu.tsx
New coreSidebarMenuLinks and useSidebarMenu hook; replaces static INSTANCE_ADMIN_LINKS with hook-derived sidebarMenu; updates active-state and typography.
Header labels & header component changes
apps/admin/core/components/common/header/{core,extended,index}.tsx
Added CORE_HEADER_SEGMENT_LABELS and EXTENDED_HEADER_SEGMENT_LABELS; HEADER_SEGMENT_LABELS mapping used for breadcrumbs; replaced getHeaderTitle switch.
Providers reorg & AppProviders
apps/admin/core/providers/{core,extended,index}.tsx, apps/admin/app/root.tsx, apps/admin/core/providers/core.tsx
Renamed AppProviders → CoreProviders, added ExtendedProviders, and new AppProviders composing both; updated import paths.
Route merge utility
apps/web/app/routes.ts, apps/web/app/routes/helper.ts
Extracted mergeRoutes implementation into new helper that deduplicates/merges routes (deep-merge children), and imported it from routes.ts.
Type additions
packages/types/src/instance/{auth,auth-ee}.ts
Added TCoreInstanceAuthenticationModeKeys, TInstanceAuthenticationModeKeys, TOAuthOption, TOAuthConfigs; added TExtendedInstanceAuthenticationModeKeys; tightened TInstanceAuthenticationModes.key type.
Barrel exports & removed components
apps/admin/ce/components/common/upgrade-button.tsx (deleted), apps/admin/ce/components/{authentication,index}.ts, apps/admin/ee/components/**, apps/web/ce/helpers/oauth-config.tsx (deleted)
Removed UpgradeButton component and several wildcard re-exports (narrowed barrel surfaces); deleted CE/EE re-exports and old OAuth helper.
Various presentational tweaks
multiple files: apps/admin/app/(all)/(dashboard)/**, apps/admin/core/components/**, apps/admin/app/(all)/(home)/**, apps/admin/core/components/workspace/list-item.tsx, etc.
Numerous className/token adjustments (sizes, spacing, color opacity removals), minor import path updates, small promise-handling changes (void prefix), and component root style tweaks (rounded-xs → rounded-lg).
Store hook import path updates
apps/admin/core/hooks/store/{use-instance,use-theme,use-user,use-workspace}.tsx
Updated StoreContext import path to "@/providers/store.provider" (module resolution only).
Instance setup + workspace adjustments
apps/admin/core/components/instance/setup-form.tsx, apps/admin/core/components/workspace/list-item.tsx
Safer searchParams access (optional chaining), minor placeholder text change, visual layout updates.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

Suggested reviewers

  • pushya22

Poem

"I hopped through components, tidy and bright,
Moved wrappers to headers, and made icons right.
Hooks stitched the OAuth thread, neat as a bun,
Barrels trimmed and routes merged — work nicely done! 🐰✨"

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly summarizes the main changes: refactoring of auth-related components and design updates for both web and admin applications.
Description check ✅ Passed The PR description includes the required 'Description' and 'Type of Change' sections. The description is detailed with a comprehensive summary note explaining layout/UX changes, navigation updates, auth config refactoring, OAuth improvements, type additions, and cleanup work, providing sufficient context for reviewers.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch refactor-components

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@makeplane
Copy link

makeplane bot commented Dec 23, 2025

Linked to Plane Work Item(s)

This comment was auto-generated by Plane

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/web/core/components/auth-screens/header.tsx (1)

11-24: Fix inverted page titles in authContentMap.

The pageTitle values are swapped: when type is EAuthModes.SIGN_IN (user is on the sign-in page), the page title should be "Sign in", not "Sign up". Similarly, when type is EAuthModes.SIGN_UP, the title should be "Sign up", not "Sign in". This causes incorrect browser tab titles.

🔎 Proposed fix
 const authContentMap = {
   [EAuthModes.SIGN_IN]: {
-    pageTitle: "Sign up",
+    pageTitle: "Sign in",
     text: "auth.common.new_to_plane",
     linkText: "Sign up",
     linkHref: "/sign-up",
   },
   [EAuthModes.SIGN_UP]: {
-    pageTitle: "Sign in",
+    pageTitle: "Sign up",
     text: "auth.common.already_have_an_account",
     linkText: "Sign in",
     linkHref: "/sign-in",
   },
 };
🧹 Nitpick comments (17)
apps/web/core/components/account/auth-forms/auth-banner.tsx (1)

10-10: Simplify the callback signature.

The parameter name bannerData with type undefined is semantically confusing—it suggests data is being passed, but the type only allows undefined. Since the callback is always invoked with undefined to clear state, consider one of these alternatives:

  1. Remove the parameter entirely: handleBannerData?: () => void and call it as handleBannerData?.()
  2. Rename to match intent: onClose?: () => void
  3. Make it more flexible: handleBannerData?: (value: TAuthErrorInfo | undefined) => void if you anticipate future use cases
🔎 Proposed refactor to remove the parameter
 type TAuthBanner = {
   message: React.ReactNode;
-  handleBannerData?: (bannerData: undefined) => void;
+  handleBannerData?: () => void;
 };

And update the call site on line 31:

-        onClick={() => handleBannerData?.(undefined)}
+        onClick={() => handleBannerData?.()}
apps/admin/core/providers/extended.tsx (1)

2-4: Placeholder pattern for future provider extensions.

ExtendedProviders currently acts as a passthrough component. This is a common scaffolding pattern for establishing extension points in the provider hierarchy where additional providers can be added later without modifying the core composition.

apps/admin/app/(all)/(dashboard)/sidebar-help-section.tsx (1)

110-110: Inconsistent icon sizing approach.

Line 110 still uses explicit dimension classes (h-3.5 w-3.5) while other icons in this file were updated to use size tokens (e.g., size-4 on lines 71 and 82). Consider updating for consistency.

🔎 Proposed fix for consistency
-                          <Icon className="h-3.5 w-3.5 text-secondary" />
+                          <Icon className="size-3.5 text-secondary" />
apps/admin/core/hooks/use-sidebar-menu/index.ts (1)

5-14: Consider using as const satisfies for stricter type inference.

While the current implementation is correct, you could leverage TypeScript 5.0+ features for more precise type inference:

export function useSidebarMenu(): TSidebarMenuItem[] {
  return [
    coreSidebarMenuLinks.general,
    coreSidebarMenuLinks.email,
    coreSidebarMenuLinks.authentication,
    coreSidebarMenuLinks.workspace,
    coreSidebarMenuLinks.ai,
    coreSidebarMenuLinks.image,
  ] as const satisfies readonly TSidebarMenuItem[];
}

This ensures the array is treated as a readonly tuple with exact types, catching any type mismatches at compile time. However, the current implementation is acceptable if the consuming code requires a mutable array.

As per coding guidelines for TypeScript 5.0+.

apps/admin/core/hooks/use-sidebar-menu/core.ts (1)

9-46: Consider using as const for more precise literal types.

For stricter type inference of the string literals and improved type safety, consider:

export const coreSidebarMenuLinks = {
  general: {
    Icon: Cog,
    name: "General",
    description: "Identify your instances and get key details.",
    href: `/general/`,
  },
  // ... other entries
} as const satisfies Record<TCoreSidebarMenuKey, TSidebarMenuItem>;

This would make name, description, and href literal types rather than just string, enabling better type checking and autocomplete. However, the current implementation is acceptable if you prefer the flexibility of regular string types.

As per coding guidelines for TypeScript 5.0+.

apps/admin/app/(all)/(dashboard)/sidebar-menu.tsx (1)

28-28: Optional chaining may be unnecessary.

The usePathname() hook from Next.js App Router always returns a string (the current pathname), so the optional chaining on line 28 is likely unnecessary:

const isActive = item.href === pathName || pathName.includes(item.href);

However, the defensive coding doesn't hurt and the current implementation is safe.

apps/web/app/routes/helper.ts (1)

30-33: Optional: Redundant Array.isArray checks.

Since RouteConfigEntry.children is typed as RouteConfigEntry[] | undefined, and the outer condition already confirms both are truthy, the Array.isArray checks are redundant. The children will always be arrays when defined.

🔎 Simplified version
         // Deep merge: recursively merge children
-        const mergedChildren = mergeRoutes(
-          Array.isArray(coreRoute.children) ? coreRoute.children : [],
-          Array.isArray(extendedRoute.children) ? extendedRoute.children : []
-        );
+        const mergedChildren = mergeRoutes(coreRoute.children, extendedRoute.children);
apps/admin/core/components/common/page-wrapper.tsx (1)

16-44: LGTM: Well-structured page wrapper component.

The component provides a consistent layout container with responsive sizing, optional headers, and scrollable content. The conditional layout logic on Line 31 appropriately handles the presence of actions.

Optional: Consider simplifying the header layout logic

The conditional className on Line 31 could potentially be simplified:

-            <div className={header.actions ? "flex flex-col gap-1" : "space-y-1"}>
+            <div className="space-y-1">

Both flex flex-col gap-1 and space-y-1 achieve similar vertical spacing. Using space-y-1 consistently would simplify the code, though the current approach may have specific alignment requirements when actions are present.

apps/space/core/hooks/oauth/extended.tsx (1)

4-7: Consider extracting shared OAuth config pattern.

The useExtendedOAuthConfig hook is duplicated in both apps/space and apps/web with identical implementations. While this pattern provides extensibility, consider extracting it to a shared location if the implementation is intended to remain identical across apps.

If app-specific customization is anticipated, the current duplication is acceptable for maintaining independence.

apps/admin/core/hooks/oauth/index.ts (1)

5-19: LGTM! Consider eliminating intermediate variable.

The implementation correctly derives authentication modes from the core map and returns them in a specific order. The explicit array construction ensures type safety and makes the selected modes clear.

Optional refactor to eliminate intermediate variable
 export const useAuthenticationModes = (props: TGetAuthenticationModeProps): TInstanceAuthenticationModes[] => {
   // derived values
   const authenticationModes = getCoreAuthenticationModesMap(props);
 
-  const availableAuthenticationModes: TInstanceAuthenticationModes[] = [
+  return [
     authenticationModes["unique-codes"],
     authenticationModes["passwords-login"],
     authenticationModes["google"],
     authenticationModes["github"],
     authenticationModes["gitlab"],
     authenticationModes["gitea"],
   ];
-
-  return availableAuthenticationModes;
 };
apps/space/core/hooks/oauth/core.tsx (3)

15-31: Consider memoizing derived values for performance.

The isOAuthEnabled boolean and next_path are recomputed on every render. For a frequently rendered auth component, consider wrapping them in useMemo:

🔎 Optional performance optimization
 export const useCoreOAuthConfig = (oauthActionText: string): TOAuthConfigs => {
   //router
   const searchParams = useSearchParams();
-  // query params
-  const next_path = searchParams.get("next_path");
   // theme
   const { resolvedTheme } = useTheme();
   // store hooks
   const { config } = useInstance();
+  
+  const next_path = useMemo(() => searchParams.get("next_path"), [searchParams]);
+  
   // derived values
-  const isOAuthEnabled =
+  const isOAuthEnabled = useMemo(() =>
     (config &&
       (config?.is_google_enabled ||
         config?.is_github_enabled ||
         config?.is_gitlab_enabled ||
         config?.is_gitea_enabled)) ||
-    false;
+    false, [config]);

32-76: Memoize oAuthOptions array to prevent unnecessary re-renders.

The oAuthOptions array is reconstructed on every render, creating new object references and function instances. This can cause unnecessary re-renders of child components consuming these options.

🔎 Recommended memoization
+  const oAuthOptions: TOAuthOption[] = useMemo(() => [
-  const oAuthOptions: TOAuthOption[] = [
     {
       id: "google",
       text: `${oauthActionText} with Google`,
       icon: <img src={googleLogo} height={18} width={18} alt="Google Logo" />,
       onClick: () => {
         window.location.assign(`${API_BASE_URL}/auth/google/${next_path ? `?next_path=${next_path}` : ``}`);
       },
       enabled: config?.is_google_enabled,
     },
     // ... other providers
-  ];
+  ], [oauthActionText, next_path, config, resolvedTheme]);

45-52: Handle undefined theme during hydration.

The resolvedTheme from next-themes can be undefined during SSR or initial hydration, which defaults to the githubDarkLogo. Consider adding explicit handling to prevent hydration mismatches:

🔎 Safer theme handling
       icon: (
         <img
-          src={resolvedTheme === "dark" ? githubLightLogo : githubDarkLogo}
+          src={resolvedTheme === "dark" ? githubLightLogo : githubDarkLogo}
           height={18}
           width={18}
           alt="GitHub Logo"
         />
       ),

Alternatively, provide a fallback:

       icon: (
         <img
-          src={resolvedTheme === "dark" ? githubLightLogo : githubDarkLogo}
+          src={!resolvedTheme || resolvedTheme === "light" ? githubDarkLogo : githubLightLogo}
           height={18}
           width={18}
           alt="GitHub Logo"
         />
       ),
apps/web/core/hooks/oauth/core.tsx (3)

15-31: Consider memoizing derived values for performance.

Similar to the space implementation, isOAuthEnabled and next_path are recomputed on every render. Consider memoization for better performance in frequently rendered auth flows.

🔎 Optional performance optimization
+  const next_path = useMemo(() => searchParams.get("next_path"), [searchParams]);
+  
   const isOAuthEnabled = useMemo(() =>
     (config &&
       (config?.is_google_enabled ||
         config?.is_github_enabled ||
         config?.is_gitlab_enabled ||
         config?.is_gitea_enabled)) ||
-    false;
+    false, [config]);

32-82: Memoize oAuthOptions to prevent unnecessary re-renders.

The oAuthOptions array is recreated on every render, which can cause performance issues in child components. Apply useMemo to maintain stable references:

🔎 Recommended memoization
+  const oAuthOptions: TOAuthOption[] = useMemo(() => [
-  const oAuthOptions: TOAuthOption[] = [
     {
       id: "google",
       text: `${oauthActionText} with Google`,
       // ... options
     },
     // ... other providers
-  ];
+  ], [oauthActionText, next_path, config, resolvedTheme]);

45-52: Handle undefined theme during hydration.

The resolvedTheme can be undefined during initial render, which may cause hydration mismatches. Consider explicit handling:

🔎 Safer theme handling
       icon: (
         <img
-          src={resolvedTheme === "dark" ? GithubDarkLogo : GithubLightLogo}
+          src={!resolvedTheme || resolvedTheme === "light" ? GithubLightLogo : GithubDarkLogo}
           height={18}
           width={18}
           alt="GitHub Logo"
         />
       ),
apps/admin/app/(all)/(dashboard)/authentication/page.tsx (1)

84-95: Simplify the toggle value parsing logic.

The Boolean(parseInt(enableSignUpConfig)) pattern is repeated and verbose. Consider extracting this to a derived variable for clarity and consistency.

🔎 Proposed simplification
   // derived values
   const enableSignUpConfig = formattedConfig?.ENABLE_SIGNUP ?? "";
+  const isSignUpEnabled = enableSignUpConfig === "1";
   const resolvedTheme = resolveGeneralTheme(resolvedThemeAdmin);

Then use in the ToggleSwitch:

               <ToggleSwitch
-                value={Boolean(parseInt(enableSignUpConfig))}
+                value={isSignUpEnabled}
                 onChange={() => {
-                  if (Boolean(parseInt(enableSignUpConfig)) === true) {
-                    updateConfig("ENABLE_SIGNUP", "0");
-                  } else {
-                    updateConfig("ENABLE_SIGNUP", "1");
-                  }
+                  updateConfig("ENABLE_SIGNUP", isSignUpEnabled ? "0" : "1");
                 }}
                 size="sm"
                 disabled={isSubmitting}
               />

Comment on lines 46 to 48
const enableIntercomConfig = () => {
submitInstanceConfigurations({ IS_INTERCOM_ENABLED: isIntercomEnabled ? "0" : "1" });
void submitInstanceConfigurations({ IS_INTERCOM_ENABLED: isIntercomEnabled ? "0" : "1" });
};
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

Good use of void prefix, but missing loading state management.

The void prefix correctly signals that the promise is intentionally ignored, which is good practice for linting rules.

However, there's a pre-existing issue: the isSubmitting state (line 19) is checked on line 73 to disable the toggle, but setIsSubmitting(true) is never called before invoking submitInstanceConfigurations. This means:

  • The loading state never displays
  • Users can rapidly toggle multiple times, triggering concurrent requests
  • The UX lacks feedback during submission
🔎 Proposed fix to add loading state management
 const enableIntercomConfig = () => {
+  setIsSubmitting(true);
   void submitInstanceConfigurations({ IS_INTERCOM_ENABLED: isIntercomEnabled ? "0" : "1" });
 };
📝 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 enableIntercomConfig = () => {
submitInstanceConfigurations({ IS_INTERCOM_ENABLED: isIntercomEnabled ? "0" : "1" });
void submitInstanceConfigurations({ IS_INTERCOM_ENABLED: isIntercomEnabled ? "0" : "1" });
};
const enableIntercomConfig = () => {
setIsSubmitting(true);
void submitInstanceConfigurations({ IS_INTERCOM_ENABLED: isIntercomEnabled ? "0" : "1" });
};
🤖 Prompt for AI Agents
In apps/admin/app/(all)/(dashboard)/general/intercom.tsx around lines 46 to 48,
the submit handler uses void submitInstanceConfigurations but never sets
isSubmitting, so the loading state is never shown and multiple rapid toggles can
fire concurrent requests; update enableIntercomConfig to setIsSubmitting(true)
before calling submitInstanceConfigurations, await the promise (or attach
then/catch) and in a finally block setIsSubmitting(false), and ensure errors are
caught and surfaced (e.g., via existing error handler) so the toggle is disabled
while the request is in-flight and re-enabled afterwards.

Comment on lines 72 to 75
useEffect(() => {
if (csrfToken === undefined)
authService.requestCSRFToken().then((data) => data?.csrf_token && setCsrfToken(data.csrf_token));
void authService.requestCSRFToken().then((data) => data?.csrf_token && setCsrfToken(data.csrf_token));
}, [csrfToken]);
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

Add error handling to prevent infinite retry loop.

The promise rejection is not handled, which means if requestCSRFToken() fails, csrfToken remains undefined and the effect will continuously retry on every render, creating an infinite loop of failed requests.

🔎 Proposed fix with error handling
  useEffect(() => {
    if (csrfToken === undefined)
-     void authService.requestCSRFToken().then((data) => data?.csrf_token && setCsrfToken(data.csrf_token));
+     void authService
+       .requestCSRFToken()
+       .then((data) => {
+         if (data?.csrf_token) {
+           setCsrfToken(data.csrf_token);
+         }
+       })
+       .catch((error) => {
+         console.error("Failed to fetch CSRF token:", error);
+         // Set empty string to prevent infinite retries
+         setCsrfToken("");
+       });
  }, [csrfToken]);

As per coding guidelines, proper error handling with logging is required for promise-based operations.

📝 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
useEffect(() => {
if (csrfToken === undefined)
authService.requestCSRFToken().then((data) => data?.csrf_token && setCsrfToken(data.csrf_token));
void authService.requestCSRFToken().then((data) => data?.csrf_token && setCsrfToken(data.csrf_token));
}, [csrfToken]);
useEffect(() => {
if (csrfToken === undefined)
void authService
.requestCSRFToken()
.then((data) => {
if (data?.csrf_token) {
setCsrfToken(data.csrf_token);
}
})
.catch((error) => {
console.error("Failed to fetch CSRF token:", error);
// Set empty string to prevent infinite retries
setCsrfToken("");
});
}, [csrfToken]);
🤖 Prompt for AI Agents
In apps/admin/app/(all)/(dashboard)/sidebar-dropdown.tsx around lines 72-75 the
call to authService.requestCSRFToken() has no error handling so a rejection
leaves csrfToken undefined and the effect repeatedly retries; fix by wrapping
the request in an async inner function or attaching .catch(), log the error
(console.error or app logger), and set a sentinel state to stop retries (e.g.,
setCsrfToken(null) or set a separate csrfFailed boolean) so the effect doesn't
loop indefinitely; ensure the effect still only triggers when needed (keep
dependency logic) and avoid swallowing the error.

})}
</div>
<div className="px-2 pb-1 pt-2 text-10">Version: v{packageJson.version}</div>
<div className="px-2 pb-1 pt-2 text-10">Version: v{instance?.current_version}</div>
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

Handle undefined version gracefully.

The version display uses optional chaining but doesn't handle the undefined case. If instance or current_version is undefined, this will render "vundefined" or "v".

🔎 Proposed fix
-            <div className="px-2 pb-1 pt-2 text-10">Version: v{instance?.current_version}</div>
+            <div className="px-2 pb-1 pt-2 text-10">
+              Version: {instance?.current_version ? `v${instance.current_version}` : "N/A"}
+            </div>
📝 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
<div className="px-2 pb-1 pt-2 text-10">Version: v{instance?.current_version}</div>
<div className="px-2 pb-1 pt-2 text-10">
Version: {instance?.current_version ? `v${instance.current_version}` : "N/A"}
</div>
🤖 Prompt for AI Agents
In apps/admin/app/(all)/(dashboard)/sidebar-help-section.tsx around line 131,
the version line uses optional chaining but will render "vundefined" or "v" when
instance or instance.current_version is missing; change it to handle undefined
by using a safe fallback or conditional render — e.g., compute a display string
like const version = instance?.current_version ? `v${instance.current_version}`
: 'Version unknown' (or render nothing) and use that variable in the div so the
UI shows a sensible fallback instead of "vundefined".

const lastNameParam = searchParams?.get("last_name") || undefined;
const companyParam = searchParams?.get("company") || undefined;
const emailParam = searchParams?.get("email") || undefined;
const isTelemetryEnabledParam = (searchParams?.get("is_telemetry_enabled") === "True" ? true : false) || true;
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 | 🔴 Critical

Critical: Telemetry parameter logic always returns true.

The || true fallback at the end of Line 61 makes isTelemetryEnabledParam always evaluate to true, regardless of the parameter value. This prevents users from opting out of telemetry via the URL parameter.

Current behavior:

  • ?is_telemetry_enabled=Falsetrue (incorrect)
  • ?is_telemetry_enabled=Truetrue (correct)
  • No parameter → true (correct as default)

Expected behavior:

  • ?is_telemetry_enabled=Falsefalse
  • ?is_telemetry_enabled=Truetrue
  • No parameter → true (default)
🔎 Proposed fix
-  const isTelemetryEnabledParam = (searchParams?.get("is_telemetry_enabled") === "True" ? true : false) || true;
+  const isTelemetryEnabledParam = searchParams?.get("is_telemetry_enabled") === "False" ? false : true;

This correctly defaults to true when the parameter is absent or undefined, but allows "False" to set it to false.

📝 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 isTelemetryEnabledParam = (searchParams?.get("is_telemetry_enabled") === "True" ? true : false) || true;
const isTelemetryEnabledParam = searchParams?.get("is_telemetry_enabled") === "False" ? false : true;
🤖 Prompt for AI Agents
In apps/admin/core/components/instance/setup-form.tsx around line 61, the
current expression ends with "|| true" which forces isTelemetryEnabledParam to
always be true; replace it with logic that returns true when the URL param is
absent but respects explicit "False" values — e.g. read the param value, if it's
null/undefined default to "True" (or return true), otherwise compare the param
string to "True" (optionally case-insensitive) to determine the boolean.

Comment on lines +106 to +118
type TAuthHeaderBase = {
header: React.ReactNode;
subHeader: string;
};

export function AuthHeaderBase(props: TAuthHeaderBase) {
return (
<div className="flex flex-col gap-1">
<span className="text-h4-semibold text-primary">{typeof header === "string" ? t(header) : header}</span>
<span className="text-h4-semibold text-placeholder">{subHeader}</span>
<span className="text-h4-semibold text-primary">{props.header}</span>
<span className="text-h4-semibold text-placeholder">{props.subHeader}</span>
</div>
);
});
}
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

Naming collision: AuthHeaderBase exported from multiple files with different purposes.

This file exports AuthHeaderBase (lines 111-118) that renders form header content (header + subHeader), while apps/web/core/components/auth-screens/header.tsx also exports AuthHeaderBase (lines 63-76) with a different signature and purpose (page navigation with logo and actions). This creates ambiguity and potential import conflicts.

Consider renaming one or both to clarify their distinct roles:

  • This component: AuthFormHeaderBase or AuthContentHeader
  • The other: AuthPageHeaderBase or AuthNavigationHeader
🔎 Proposed fix to rename this component
-type TAuthHeaderBase = {
+type TAuthFormHeaderBase = {
   header: React.ReactNode;
   subHeader: string;
 };
 
-export function AuthHeaderBase(props: TAuthHeaderBase) {
+export function AuthFormHeaderBase(props: TAuthFormHeaderBase) {
   return (
     <div className="flex flex-col gap-1">
       <span className="text-h4-semibold text-primary">{props.header}</span>
       <span className="text-h4-semibold text-placeholder">{props.subHeader}</span>
     </div>
   );
 }

And update the call site at line 103:

-  return <AuthHeaderBase subHeader={subHeader} header={header} />;
+  return <AuthFormHeaderBase subHeader={subHeader} header={header} />;
📝 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
type TAuthHeaderBase = {
header: React.ReactNode;
subHeader: string;
};
export function AuthHeaderBase(props: TAuthHeaderBase) {
return (
<div className="flex flex-col gap-1">
<span className="text-h4-semibold text-primary">{typeof header === "string" ? t(header) : header}</span>
<span className="text-h4-semibold text-placeholder">{subHeader}</span>
<span className="text-h4-semibold text-primary">{props.header}</span>
<span className="text-h4-semibold text-placeholder">{props.subHeader}</span>
</div>
);
});
}
type TAuthFormHeaderBase = {
header: React.ReactNode;
subHeader: string;
};
export function AuthFormHeaderBase(props: TAuthFormHeaderBase) {
return (
<div className="flex flex-col gap-1">
<span className="text-h4-semibold text-primary">{props.header}</span>
<span className="text-h4-semibold text-placeholder">{props.subHeader}</span>
</div>
);
}
🤖 Prompt for AI Agents
In apps/web/core/components/account/auth-forms/auth-header.tsx around lines 106
to 118, the exported component AuthHeaderBase collides with another
AuthHeaderBase elsewhere; rename this component to a clearer name (e.g.,
AuthFormHeaderBase or AuthContentHeader), update the export declaration
accordingly, and update all local call sites (notably the caller at line 103 in
this file) and any imports in other files to use the new name so there is no
ambiguity with apps/web/core/components/auth-screens/header.tsx.

Comment on lines +58 to +76
type TAuthHeaderBase = {
pageTitle: string;
additionalAction?: React.ReactNode;
};

export function AuthHeaderBase(props: TAuthHeaderBase) {
const { pageTitle, additionalAction } = props;
return (
<>
<PageHead title={pageTitle + " - Plane"} />
<div className="flex items-center justify-between gap-6 w-full flex-shrink-0 sticky top-0">
<Link href="/">
<PlaneLockup height={20} width={95} className="text-primary" />
</Link>
{additionalAction}
</div>
</>
);
});
}
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

Naming collision: See comment on apps/web/core/components/account/auth-forms/auth-header.tsx.

This AuthHeaderBase export has the same name as a different component exported from apps/web/core/components/account/auth-forms/auth-header.tsx (lines 111-118). Consider renaming this to AuthPageHeaderBase or AuthNavigationHeader to distinguish it from the form-content header.

🤖 Prompt for AI Agents
In apps/web/core/components/auth-screens/header.tsx around lines 58 to 76, the
exported component name AuthHeaderBase collides with another export of the same
name in apps/web/core/components/account/auth-forms/auth-header.tsx; rename this
component (and its type if needed) to a distinct name such as AuthPageHeaderBase
or AuthNavigationHeader, update the function export and any imports/uses across
the codebase to the new name, and run a quick search/replace to ensure there are
no remaining references to the old name.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (3)
apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx (3)

51-58: Consider using try/catch/finally instead of mixing await with .then()/.catch().

The current pattern is redundant—await already handles the promise. Using try/catch/finally is more idiomatic and consolidates the duplicated setIsSubmitting(false) call.

🔎 Proposed refactor
-    await updateConfigPromise
-      .then(() => {
-        setIsSubmitting(false);
-      })
-      .catch((err) => {
-        console.error(err);
-        setIsSubmitting(false);
-      });
+    try {
+      await updateConfigPromise;
+    } catch (err) {
+      console.error(err);
+    } finally {
+      setIsSubmitting(false);
+    }

65-66: Multiline string may introduce unwanted whitespace.

The description string split across lines will include the newline and leading spaces in the rendered text.

🔎 Proposed fix
-          description="Allow members to login or sign up to plane with their Google
-            accounts."
+          description="Allow members to login or sign up to plane with their Google accounts."

69-77: Simplify toggle logic and add radix to parseInt.

The parseInt call should include a radix parameter to avoid ambiguity. Additionally, the redundant === true comparison and repeated parsing can be simplified.

🔎 Proposed refactor
+          const isGoogleEnabled = Boolean(parseInt(enableGoogleConfig, 10));
           <ToggleSwitch
-              value={Boolean(parseInt(enableGoogleConfig))}
-              onChange={() => {
-                if (Boolean(parseInt(enableGoogleConfig)) === true) {
-                  updateConfig("IS_GOOGLE_ENABLED", "0");
-                } else {
-                  updateConfig("IS_GOOGLE_ENABLED", "1");
-                }
-              }}
+              value={isGoogleEnabled}
+              onChange={() => updateConfig("IS_GOOGLE_ENABLED", isGoogleEnabled ? "0" : "1")}
              size="sm"
              disabled={isSubmitting || !formattedConfig}
            />

Note: Extract isGoogleEnabled as a derived variable near line 26 to avoid repetition.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bb00007 and 96aa04b.

📒 Files selected for processing (4)
  • apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx
  • apps/admin/core/providers/extended.tsx
  • packages/editor/src/core/hooks/use-yjs-setup.ts
  • packages/types/src/instance/auth-ee.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/types/src/instance/auth-ee.ts
  • apps/admin/core/providers/extended.tsx
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx,mts,cts}

📄 CodeRabbit inference engine (.github/instructions/typescript.instructions.md)

**/*.{ts,tsx,mts,cts}: Use const type parameters for more precise literal inference in TypeScript 5.0+
Use the satisfies operator to validate types without widening them
Leverage inferred type predicates to reduce the need for explicit is return types in filter/check functions
Use NoInfer<T> utility to block inference for specific type arguments when they should be determined by other arguments
Utilize narrowing in switch(true) blocks for control flow analysis (TypeScript 5.3+)
Rely on narrowing from direct boolean comparisons for type guards
Trust preserved narrowing in closures when variables aren't modified after the check (TypeScript 5.4+)
Use constant indices to narrow object/array properties (TypeScript 5.5+)
Use standard ECMAScript decorators (Stage 3) instead of legacy experimentalDecorators
Use using declarations for explicit resource management with Disposable pattern instead of manual cleanup (TypeScript 5.2+)
Use with { type: "json" } for import attributes; avoid deprecated assert syntax (TypeScript 5.3/5.8+)
Use import type explicitly when importing types to ensure they are erased during compilation, respecting verbatimModuleSyntax flag
Use .ts, .mts, .cts extensions in import type statements (TypeScript 5.2+)
Use import type { Type } from "mod" with { "resolution-mode": "import" } for specific module resolution contexts (TypeScript 5.3+)
Use new iterator methods (map, filter, etc.) if targeting modern environments (TypeScript 5.6+)
Utilize new Set methods like union, intersection, etc., when available (TypeScript 5.5+)
Use Object.groupBy / Map.groupBy standard methods for grouping instead of external libraries (TypeScript 5.4+)
Use Promise.withResolvers() for creating promises with exposed resolve/reject functions (TypeScript 5.7+)
Use copying array methods (toSorted, toSpliced, with) for immutable array operations (TypeScript 5.2+)
Avoid accessing instance fields via super in classes (TypeScript 5....

Files:

  • packages/editor/src/core/hooks/use-yjs-setup.ts
  • apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Enable TypeScript strict mode and ensure all files are fully typed

Files:

  • packages/editor/src/core/hooks/use-yjs-setup.ts
  • apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx
**/*.{js,jsx,ts,tsx,json,css}

📄 CodeRabbit inference engine (AGENTS.md)

Use Prettier with Tailwind plugin for code formatting, run pnpm fix:format

Files:

  • packages/editor/src/core/hooks/use-yjs-setup.ts
  • apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,jsx,ts,tsx}: Use ESLint with shared config across packages, adhering to max warnings limits per package
Use camelCase for variable and function names, PascalCase for components and types
Use try-catch with proper error types and log errors appropriately

Files:

  • packages/editor/src/core/hooks/use-yjs-setup.ts
  • apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx
🧠 Learnings (5)
📚 Learning: 2025-11-25T10:18:05.172Z
Learnt from: CR
Repo: makeplane/plane PR: 0
File: .github/instructions/typescript.instructions.md:0-0
Timestamp: 2025-11-25T10:18:05.172Z
Learning: Applies to **/*.{ts,tsx,mts,cts} : Use `const` type parameters for more precise literal inference in TypeScript 5.0+

Applied to files:

  • packages/editor/src/core/hooks/use-yjs-setup.ts
📚 Learning: 2025-11-25T10:18:05.172Z
Learnt from: CR
Repo: makeplane/plane PR: 0
File: .github/instructions/typescript.instructions.md:0-0
Timestamp: 2025-11-25T10:18:05.172Z
Learning: Applies to **/*.{js,mjs,cjs} : Use `import` tags in JSDoc for cleaner type imports in JavaScript files when working in a mixed codebase (TypeScript 5.5+)

Applied to files:

  • packages/editor/src/core/hooks/use-yjs-setup.ts
📚 Learning: 2025-11-25T10:18:05.172Z
Learnt from: CR
Repo: makeplane/plane PR: 0
File: .github/instructions/typescript.instructions.md:0-0
Timestamp: 2025-11-25T10:18:05.172Z
Learning: Applies to **/*.{ts,tsx,mts,cts} : Use `with { type: "json" }` for import attributes; avoid deprecated `assert` syntax (TypeScript 5.3/5.8+)

Applied to files:

  • packages/editor/src/core/hooks/use-yjs-setup.ts
📚 Learning: 2025-11-25T10:18:05.172Z
Learnt from: CR
Repo: makeplane/plane PR: 0
File: .github/instructions/typescript.instructions.md:0-0
Timestamp: 2025-11-25T10:18:05.172Z
Learning: Applies to **/*.{ts,tsx,mts,cts} : Use `NoInfer<T>` utility to block inference for specific type arguments when they should be determined by other arguments

Applied to files:

  • packages/editor/src/core/hooks/use-yjs-setup.ts
📚 Learning: 2025-10-21T17:22:05.204Z
Learnt from: lifeiscontent
Repo: makeplane/plane PR: 7989
File: apps/web/app/(all)/[workspaceSlug]/(projects)/projects/(detail)/[projectId]/pages/(detail)/[pageId]/page.tsx:45-46
Timestamp: 2025-10-21T17:22:05.204Z
Learning: In the makeplane/plane repository, the refactor from useParams() to params prop is specifically scoped to page.tsx and layout.tsx files in apps/web/app (Next.js App Router pattern). Other components (hooks, regular client components, utilities) should continue using the useParams() hook as that is the correct pattern for non-route components.

Applied to files:

  • apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: Build packages
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (2)
apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx (1)

61-98: PageWrapper integration looks good.

The refactor to use PageWrapper with customHeader is clean and maintains the existing functionality while improving layout consistency. The conditional rendering of InstanceGoogleConfigForm vs Loader is preserved correctly.

packages/editor/src/core/hooks/use-yjs-setup.ts (1)

190-190: Type constraint ensures safety—removal of explicit assertion is correct.

The useState type parameter (line 39) explicitly constrains ydoc: Y.Doc, so TypeScript will enforce that provider.document is assignable to Y.Doc at compile time. This refactoring safely removes the unnecessary assertion.

Note: Library type verification for @hocuspocus/provider v2.15.2 could not be completed in this environment, but the code structure and usage pattern (e.g., passing provider.document to IndexeddbPersistence) are consistent with correct typing.

@sriramveeraghanta sriramveeraghanta merged commit 0c795e9 into preview Dec 24, 2025
10 checks passed
@sriramveeraghanta sriramveeraghanta deleted the refactor-components branch December 24, 2025 11:01
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants