Skip to content

Conversation

@lifeiscontent
Copy link
Collaborator

@lifeiscontent lifeiscontent commented Oct 8, 2025

Description

This pull request migrates the admin application from Next.js to a React Router + Vite architecture.
It replaces server-side rendering with a modern client-side routing setup and static asset build flow.

Key updates include:

  • Removal of Next.js dependencies, configuration files, and SSR-specific logic.
  • Introduction of React Router for route definitions, nested layouts, and navigation handling.
  • Integration of Vite for faster local development and optimized production builds.
  • Setup of a static asset server (e.g. nginx) for production deployment of admin builds.
  • Addition of a dedicated 404 page and consistent page metadata (titles/descriptions).
  • Compatibility layers for next/link, next/image, and related utilities to ensure smooth migration.
  • Adjusted Dockerfile, nginx configuration, and build scripts to reflect new asset structure.
  • Type, asset, and path import fixes across the admin app.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • Feature (non-breaking change which adds functionality)
  • Improvement (change that would cause existing functionality to not work as expected)
  • Code refactoring
  • Performance improvements
  • Documentation update

Screenshots and Media (if applicable)

N/A — migration affects build and routing structure, not UI appearance.


Test Scenarios

  • Verified all admin routes (login, dashboard, settings, workspace management) work as expected using React Router.
  • Confirmed page metadata updates properly (titles/descriptions).
  • Tested 404 handling for invalid routes.
  • Validated Vite build output and confirmed assets serve correctly via nginx.
  • Verified Docker build and run process produces correct static deployment.
  • Checked authentication flow and UI theming for regressions.
  • Ensured all type checks, linting, and import paths pass after migration.

References

https://app.plane.so/plane/browse/WEB-5169/


Summary by CodeRabbit

  • New Features

    • Migrated admin application from Next.js to React Router framework with improved routing and performance.
    • Reorganized authentication and settings pages with enhanced metadata handling.
  • Bug Fixes & Improvements

    • Optimized Docker deployment using nginx for static asset serving in production.
  • Chores

    • Updated build configuration and dependencies to support React Router architecture.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 8, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

This pull request migrates the admin application from Next.js to React Router with Vite, introduces server-side rendering via Express, updates Docker deployment to nginx-based static serving, refactors layout files, and adds Next.js API compatibility shims for seamless code migration across the monorepo.

Changes

Cohort / File(s) Summary
Admin App Framework Migration
apps/admin/app/(all)/(dashboard)/**/layout.tsx, apps/admin/app/(all)/(dashboard)/**/page.tsx, apps/admin/app/(all)/(home)/layout.tsx, apps/admin/app/(all)/(home)/page.tsx, apps/admin/app/layout.tsx
Removes Next.js layout files defining metadata; moves metadata exports to page.tsx using Route.MetaFunction and typed observer components; replaces children prop handling with Outlet in root layouts.
Admin Routes Configuration
apps/admin/app/routes.ts, apps/admin/react-router.config.ts
Adds React Router route definitions and react-router.config.ts with appDirectory, basename, and ssr settings.
Next.js Compatibility Shims
apps/admin/app/compat/next/link.tsx, apps/admin/app/compat/next/image.tsx, apps/admin/app/compat/next/navigation.ts, apps/admin/app/compat/next/helper.ts
Implements client-side shims mapping Next.js Link, Image, and navigation hooks (useRouter, usePathname, useSearchParams) to react-router equivalents.
TypeScript Type Declarations
apps/admin/app/types/next-*.d.ts, apps/admin/app/types/react-router-virtual.d.ts
Adds ambient module declarations for next/link, next/image, next/navigation, and virtual:react-router/server-build.
Server-Side Rendering & Express
apps/admin/server.mjs, apps/admin/server/app.ts, apps/admin/app/root.tsx, apps/admin/middleware.js
Introduces Express server with development/production modes, API proxying, React Router SSR handler, and Vercel Edge middleware with security headers.
Build & Dev Configuration
apps/admin/vite.config.ts, apps/admin/tsconfig.json, apps/admin/package.json, apps/admin/next-env.d.ts
Replaces Next.js config with Vite + react-router configuration; updates tsconfig extends and includes; switches scripts to react-router build/dev; removes Next.js type references.
Docker & Deployment
apps/admin/Dockerfile.admin, apps/admin/nginx/nginx.conf, apps/admin/.dockerignore, apps/admin/.eslintrc.cjs
Converts Docker build to multi-stage nginx-based production image with static asset serving; adds nginx configuration; configures eslint ignorePatterns for build artifacts.
Asset & Import Path Updates
apps/admin/app/(all)/(home)/auth-helpers.tsx, apps/admin/ce/components/authentication/authentication-modes.tsx, apps/admin/core/components/**/*.tsx, apps/admin/styles/globals.css
Updates asset imports from public/ to app/assets/ using ?url query; changes CSS import path from fonts to fonts.css.
Root Configuration Files
.dockerignore, .gitignore, pnpm-workspace.yaml
Adds build/ and .react-router/ to ignore patterns; updates catalog dependencies in workspace config with new React Router, Vite, and UI library versions.
Component Type Updates
apps/admin/core/components/instance/*.tsx, apps/admin/core/components/new-user-popup.tsx
Updates component types from FC to React.FC and wraps observer calls with explicit generics for route component props.
Error & Not Found Pages
apps/admin/app/error.tsx, apps/admin/app/components/404.tsx
Removes Next.js error.tsx; adds new 404 component with navigation link to /general/.
Package.json Updates Across Monorepo
packages/*/package.json, apps/live/package.json, apps/web/package.json
Adds @react-router/, @tanstack/, compression, express, and supporting libraries; removes isomorphic-dompurify; bumps @typescript-eslint, eslint-config, and related dependencies; marks packages/services as module type.
Utils & Services Updates
packages/utils/src/string.ts, packages/propel/package.json, packages/propel/tsdown.config.ts, packages/editor/package.json, packages/editor/tsdown.config.ts
Replaces isomorphic-dompurify with dompurify; adds .css export aliases for fonts and react-day-picker; adds new dependencies (@floating-ui/*, @babel/runtime, tabbable, fontfaceobserver).
Miscellaneous Config
apps/admin/public/.well-known/appspecific/com.chrome.devtools.json, apps/admin/public/favicon/site.webmanifest, apps/admin/app/providers.tsx
Creates Chrome devtools config; removes web manifest content; renames AppProviders export and updates import paths to (all)/ layout.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Server as Express Server
    participant Vite as Vite (Dev)
    participant RR as React Router
    participant API as Backend API

    rect rgb(220, 230, 255)
    Note over Client,API: Next.js Architecture (Previous)
    Client->>Server: Request /admin/page
    Server->>Server: Next.js render (SSR+CSR)
    Server-->>Client: HTML + JS
    end

    rect rgb(230, 255, 230)
    Note over Client,API: React Router + Express Architecture (New)
    Client->>Server: Request /admin/general
    
    alt Development Mode
        Server->>Vite: Delegate to Vite middleware
        Vite->>RR: Load virtual:react-router/server-build
        RR->>RR: Render route component
        Vite-->>Server: HTML
    else Production Mode
        Server->>RR: Handle via pre-built React Router
        RR->>RR: Render route component
        RR-->>Server: HTML
    end

    Server->>API: /api//* requests
    API-->>Server: API response
    
    Server-->>Client: HTML + SSR'd content
    Client->>Client: Hydrate with React
    
    Client->>RR: Client-side navigation
    RR-->>Client: Route component
    </mermaid>

## Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120+ minutes

This PR represents a comprehensive framework migration from Next.js to React Router + Vite for the admin application, affecting: (1) core application architecture and routing paradigm (~50 page/layout files modified), (2) build system and server infrastructure (Dockerfile, vite.config, react-router.config, Express server), (3) compatibility layer implementation (~4 new shim files), (4) widespread asset path migrations, (5) TypeScript declarations and type system updates, and (6) multi-package dependency bumps across the monorepo. The heterogeneity of changes—spanning framework-level rewrites, configuration shifts, server-side code introduction, and package.json revisions—demands careful reasoning for each file cohort, with particular attention to route metadata handling, SSR/CSR transitions, and API proxy routing.

## Possibly related PRs

- **#7593**: Primary migration PR converting the admin app from Next.js to React Router + Vite, with overlapping changes to Dockerfile, build scripts, package.json dependencies, and workspace configuration.

## Poem

> 🐰 *From Next's metadatas to Routes so fine,*  
> *React Router's paths now perfectly align!*  
> *Vite builds swift, Express serves the way,*  
> *Compatibility shims save the day!*  
> *The admin app hops to a new brigade,* ✨

<!-- walkthrough_end -->


<!-- pre_merge_checks_walkthrough_start -->

## Pre-merge checks and finishing touches
<details>
<summary>❌ Failed checks (1 warning)</summary>

|     Check name     | Status     | Explanation                                                                           | Resolution                                                                     |
| :----------------: | :--------- | :------------------------------------------------------------------------------------ | :----------------------------------------------------------------------------- |
| Docstring Coverage | ⚠️ Warning | Docstring coverage is 10.00% which is insufficient. The required threshold is 80.00%. | You can run `@coderabbitai generate docstrings` to improve docstring coverage. |

</details>
<details>
<summary>✅ Passed checks (2 passed)</summary>

|     Check name    | Status   | Explanation                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| :---------------: | :------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|    Title Check    | ✅ Passed | The pull request title "feat: admin react-router migration" clearly and concisely summarizes the primary change across the entire changeset. The raw_summary demonstrates that the core objective involves a comprehensive migration of the admin application from a Next.js architecture to React Router combined with Vite, including removal of Next.js configuration, addition of React Router routing infrastructure, Vite build configuration, Express server setup, and multiple compatibility shims. The title directly reflects this main undertaking and would allow teammates scanning commit history to quickly understand that this changeset involves the React Router migration of the admin application. |
| Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |

</details>

<!-- pre_merge_checks_walkthrough_end -->

<!-- finishing_touch_checkbox_start -->

<details>
<summary>✨ Finishing touches</summary>

- [ ] <!-- {"checkboxId": "7962f53c-55bc-4827-bfbf-6a18da830691"} --> 📝 Generate docstrings
<details>
<summary>🧪 Generate unit tests (beta)</summary>

- [ ] <!-- {"checkboxId": "f47ac10b-58cc-4372-a567-0e02b2c3d479", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} -->   Create PR with unit tests
- [ ] <!-- {"checkboxId": "07f1e7d6-8a8e-4e23-9900-8731c2c87f58", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} -->   Post copyable unit tests in a comment
- [ ] <!-- {"checkboxId": "6ba7b810-9dad-11d1-80b4-00c04fd430c8", "radioGroupId": "utg-output-choice-group-unknown_comment_id"} -->   Commit unit tests in branch `feat/admin-react-router-migration`

</details>

</details>

<!-- finishing_touch_checkbox_end -->

<!-- tips_start -->

---

Thanks for using [CodeRabbit](https://coderabbit.ai?utm_source=oss&utm_medium=github&utm_campaign=makeplane/plane&utm_content=7922)! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

<details>
<summary>❤️ Share</summary>

- [X](https://twitter.com/intent/tweet?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A&url=https%3A//coderabbit.ai)
- [Mastodon](https://mastodon.social/share?text=I%20just%20used%20%40coderabbitai%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20the%20proprietary%20code.%20Check%20it%20out%3A%20https%3A%2F%2Fcoderabbit.ai)
- [Reddit](https://www.reddit.com/submit?title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&text=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code.%20Check%20it%20out%3A%20https%3A//coderabbit.ai)
- [LinkedIn](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fcoderabbit.ai&mini=true&title=Great%20tool%20for%20code%20review%20-%20CodeRabbit&summary=I%20just%20used%20CodeRabbit%20for%20my%20code%20review%2C%20and%20it%27s%20fantastic%21%20It%27s%20free%20for%20OSS%20and%20offers%20a%20free%20trial%20for%20proprietary%20code)

</details>

<sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub>

<!-- tips_end -->

<!-- internal state start -->


<!-- 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= -->

<!-- internal state end -->
Loading

@lifeiscontent lifeiscontent force-pushed the feat/admin-react-router-migration branch 5 times, most recently from 5fd5879 to 95870bc Compare October 8, 2025 21:22
@@ -0,0 +1,25 @@
module.exports = {
root: true,
extends: ["@plane/eslint-config/next.js"],
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@sriramveeraghanta we should probably make an ESLint for react-router, but I figure we can do a fast follow.

@lifeiscontent lifeiscontent force-pushed the feat/admin-react-router-migration branch from 95870bc to 959434a Compare October 8, 2025 21:43
@lifeiscontent lifeiscontent marked this pull request as ready for review October 8, 2025 21:44
@Copilot Copilot AI review requested due to automatic review settings October 8, 2025 21:44
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR migrates the admin app from Next.js to React Router, updating the build system, routing structure, and deployment configuration while maintaining the same functionality.

  • Replaces Next.js with React Router + Vite for build tooling and routing
  • Updates package configurations to use specific TypeScript versions instead of catalog references
  • Migrates from SSR/static site generation to client-side routing with optional SSR support

Reviewed Changes

Copilot reviewed 114 out of 143 changed files in this pull request and generated no comments.

Show a summary per file
File Description
apps/admin/package.json Updates dependencies from Next.js to React Router, adds Vite build tooling
apps/admin/vite.config.ts New Vite configuration with React Router plugin and Next.js compatibility aliases
apps/admin/react-router.config.ts React Router configuration for client-side routing
apps/admin/app/root.tsx New root layout component replacing Next.js app structure
apps/admin/app/routes.ts Manual route configuration for React Router
apps/admin/app/compat/next/* Compatibility shims for Next.js APIs (navigation, Link, Image)
apps/admin/Dockerfile.admin Updates Docker deployment from Node.js server to nginx static hosting
packages/*/package.json Replaces catalog TypeScript references with explicit version 5.8.3

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

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: 19

Caution

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

⚠️ Outside diff range comments (1)
apps/admin/Dockerfile.admin (1)

40-57: Rename NEXT_PUBLIC_ env vars to VITE_ and update references**
Verified these vars are still used in apps/admin:

  • vite.config.ts (PUBLIC_ENV_KEYS)
  • react-router.config.ts (basename)
  • server/app.ts (proxy targets & base paths)
    Either rename to VITE_* and adjust PUBLIC_ENV_KEYS, process.env references, etc., or explicitly configure Vite to expose NEXT_PUBLIC_*—but align with Vite conventions.
♻️ Duplicate comments (2)
apps/admin/app/(all)/(dashboard)/email/test-email-modal.tsx (1)

22-22: React namespace not available for React.FC usage.

The component type annotation uses React.FC<Props>, but React is not imported. This is the same issue flagged in the import statement comment—apply the fix at Line 1 to resolve this.

apps/admin/.eslintrc.cjs (1)

3-3: Consider updating to a React Router-specific ESLint config.

The config extends "@plane/eslint-config/next.js" which may contain Next.js-specific rules that are no longer applicable after migrating to React Router. While this might work for now, it could lead to false positives or missed issues specific to React Router patterns.

As noted in the previous review comment, consider creating or using a React Router-specific ESLint configuration to ensure linting rules align with the new framework.

🧹 Nitpick comments (14)
apps/admin/core/utils/public-asset.ts (1)

1-1: Remove the empty module placeholder.

This file exports nothing and only serves to toggle module mode. Unless the build requires a marker file here, better to drop it to avoid dead artifacts. If a stub is needed, add a comment explaining why.

apps/admin/core/components/common/empty-state.tsx (1)

12-12: Consider tightening the icon type for consistency.

Line 10's image prop was changed from any to string. For consistency with this type-tightening pattern, consider also specifying a more precise type for icon (e.g., React.ReactNode or a more specific icon type if applicable).

-    icon?: any;
+    icon?: React.ReactNode;
apps/admin/app/(all)/(dashboard)/general/intercom.tsx (2)

72-72: Simplify redundant boolean conversion.

The ternary isIntercomEnabled ? true : false is redundant because isIntercomEnabled is already a boolean value (derived from lines 24-30).

Apply this diff to simplify:

-            value={isIntercomEnabled ? true : false}
+            value={isIntercomEnabled}

1-1: Consider removing Next.js-specific directive.

The "use client" directive is a Next.js 13+ convention for marking client components. Since this PR migrates from Next.js to React Router, this directive may no longer be necessary and could be removed.

Apply this diff if you want to remove it:

-"use client";
-
 import { useState } from "react";
apps/admin/app/compat/next/navigation.ts (1)

17-19: Consider router-level prefetching instead of a no-op.

The prefetch method is currently a no-op, which means eager link prefetching patterns from Next.js won't improve perceived performance. React Router v7 supports route-level prefetching via <Link prefetch> and programmatic prefetching.

Consider implementing basic prefetch logic if performance optimization is important, or document that this feature is intentionally omitted.

.dockerignore (1)

64-69: Consider removing duplicate patterns.

Lines 67-68 duplicate existing patterns:

  • build/ already covered by L13-14 (build/ and **/build/)
  • node_modules/ already covered by L6-7 (node_modules/ and **/node_modules/)

While Docker handles duplicates gracefully, removing them would reduce redundancy and improve maintainability.

Apply this diff to remove duplicates:

 # React Router - https://github.com/remix-run/react-router-templates/blob/dc79b1a065f59f3bfd840d4ef75cc27689b611e6/default/.dockerignore
 .react-router/
-build/
-node_modules/
 README.md
apps/admin/react-router.config.ts (1)

1-8: Consider renaming environment variable prefix.

The configuration correctly sets ssr: false which aligns with the nginx-based static serving in the Dockerfile. However, NEXT_PUBLIC_ADMIN_BASE_PATH retains the Next.js naming convention. Consider renaming environment variables to follow Vite conventions (e.g., VITE_ADMIN_BASE_PATH) for consistency with the new stack, unless maintaining Next.js prefixes is intentional for backward compatibility.

If renaming, update this line and corresponding references:

-  basename: process.env.NEXT_PUBLIC_ADMIN_BASE_PATH,
+  basename: process.env.VITE_ADMIN_BASE_PATH,
apps/admin/server/app.ts (2)

27-36: Add error handling for the proxy middleware.

The proxy middleware lacks error event handlers. If the upstream API is unreachable or times out, this could result in unhandled errors or hanging requests.

Consider adding error handlers:

 app.use(
   "/api",
   createProxyMiddleware({
     target: NEXT_PUBLIC_API_BASE_URL,
     changeOrigin: true,
     secure: false,
+    onError: (err, req, res) => {
+      console.error('Proxy error:', err);
+      res.status(502).json({ error: 'Bad Gateway' });
+    },
     pathRewrite: (path: string) =>
       NORMALIZED_API_BASE_PATH === "/api" ? path : path.replace(/^\/api/, NORMALIZED_API_BASE_PATH),
   })
 );

7-18: Consider validating required environment variables.

The environment variable normalization provides defaults, but it might be beneficial to validate that critical variables like NEXT_PUBLIC_API_BASE_URL are set in production to avoid fallback to localhost.

Add validation for production:

if (process.env.NODE_ENV === 'production' && !process.env.NEXT_PUBLIC_API_BASE_URL) {
  console.warn('Warning: NEXT_PUBLIC_API_BASE_URL not set in production, using default');
}
apps/admin/deploy/nginx.default.conf.template (2)

25-27: Avoid if in location context when possible.

Using if directives in Nginx location blocks is generally discouraged and can lead to unexpected behavior. Consider using a map directive or separate location blocks instead.

Replace the conditional rewrite with a map:

# Add this at the http level in nginx.conf or at the top of the server block
map $api_base $rewrite_path {
    default $uri;
    "~^(?!/api).*" $api_base$uri;
}

# Then in the location block:
location /api/ {
    set $api_origin $NEXT_PUBLIC_API_BASE_URL;
    set $api_base $NEXT_PUBLIC_API_BASE_PATH;
    
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;

    rewrite ^/api/(.*)$ $api_base/$1 break;
    proxy_pass $api_origin;
}

However, if the current approach works reliably in your setup, you may choose to defer this refactor.


11-12: Consider adding caching and compression for static assets.

Static assets should be cached to improve performance, and gzip/brotli compression should be enabled for text-based assets.

Add caching and compression directives:

# Add gzip compression at the top of the server block
gzip on;
gzip_vary on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;
gzip_min_length 1000;

# Update the static asset location with caching
location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
    root /usr/share/nginx/html;
    expires 1y;
    add_header Cache-Control "public, immutable";
    access_log off;
}

Also applies to: 43-45

apps/admin/app/root.tsx (1)

55-57: Consider adding an error boundary.

The root component lacks error handling. If a child route throws an uncaught error, users will see a broken page without a fallback UI.

React Router supports error boundaries through route configuration. Consider adding an ErrorBoundary component to handle route-level errors:

import { useRouteError, isRouteErrorResponse } from "react-router";

export function ErrorBoundary() {
  const error = useRouteError();
  
  if (isRouteErrorResponse(error)) {
    return (
      <div className="flex h-screen items-center justify-center">
        <div className="text-center">
          <h1 className="text-4xl font-bold">{error.status}</h1>
          <p className="text-lg">{error.statusText}</p>
        </div>
      </div>
    );
  }
  
  return (
    <div className="flex h-screen items-center justify-center">
      <div className="text-center">
        <h1 className="text-4xl font-bold">Oops!</h1>
        <p className="text-lg">Something went wrong</p>
      </div>
    </div>
  );
}

Then export it alongside the Root component to be used in route configuration.

apps/admin/package.json (2)

35-35: Remove unused client-only dependency
The client-only package declared at apps/admin/package.json:35 isn’t imported or required anywhere—no SSR conflict. Consider removing it if it’s not needed.


77-79: Confirm TS 5.8.3, Vite 7.1.7 & React Router 7.9.x integration

  • Ensure your Node.js version meets Vite 7’s minimum requirement.
  • Convert React Router route-generated type imports to import type { … } per framework-mode docs.
  • Run your dev/build and type-check pipeline (including any Vite plugins like vite-plugin-checker) after upgrading to TS 5.8.3/Vite 7.1.7.
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f2539c5 and 959434a.

⛔ Files ignored due to path filters (21)
  • apps/admin/app/assets/favicon/apple-touch-icon.png is excluded by !**/*.png
  • apps/admin/app/assets/favicon/favicon-16x16.png is excluded by !**/*.png
  • apps/admin/app/assets/favicon/favicon-32x32.png is excluded by !**/*.png
  • apps/admin/app/assets/favicon/favicon.ico is excluded by !**/*.ico
  • apps/admin/app/assets/images/logo-spinner-dark.gif is excluded by !**/*.gif
  • apps/admin/app/assets/images/logo-spinner-light.gif is excluded by !**/*.gif
  • apps/admin/app/assets/images/plane-takeoff.png is excluded by !**/*.png
  • apps/admin/app/assets/instance/instance-failure-dark.svg is excluded by !**/*.svg
  • apps/admin/app/assets/instance/instance-failure.svg is excluded by !**/*.svg
  • apps/admin/app/assets/logos/github-black.png is excluded by !**/*.png
  • apps/admin/app/assets/logos/github-white.png is excluded by !**/*.png
  • apps/admin/app/assets/logos/gitlab-logo.svg is excluded by !**/*.svg
  • apps/admin/app/assets/logos/google-logo.svg is excluded by !**/*.svg
  • apps/admin/app/assets/logos/oidc-logo.svg is excluded by !**/*.svg
  • apps/admin/app/assets/logos/saml-logo.svg is excluded by !**/*.svg
  • apps/admin/app/assets/logos/takeoff-icon-dark.svg is excluded by !**/*.svg
  • apps/admin/app/assets/logos/takeoff-icon-light.svg is excluded by !**/*.svg
  • apps/admin/public/auth/background-pattern-dark.svg is excluded by !**/*.svg
  • apps/admin/public/auth/background-pattern.svg is excluded by !**/*.svg
  • apps/admin/public/instance/plane-takeoff.png is excluded by !**/*.png
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (107)
  • .dockerignore (1 hunks)
  • .gitignore (1 hunks)
  • apps/admin/.dockerignore (1 hunks)
  • apps/admin/.eslintrc.cjs (1 hunks)
  • apps/admin/.eslintrc.js (0 hunks)
  • apps/admin/Dockerfile.admin (1 hunks)
  • apps/admin/app/(all)/(dashboard)/ai/form.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/ai/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/ai/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/github/form.tsx (3 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/github/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/gitlab/form.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/gitlab/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/google/form.tsx (3 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/google/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/email/email-config-form.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/email/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/email/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/email/test-email-modal.tsx (4 hunks)
  • apps/admin/app/(all)/(dashboard)/general/form.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/general/intercom.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/general/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/general/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/header.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/image/form.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/image/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/image/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/layout.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/sidebar-help-section.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/sidebar.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/workspace/create/form.tsx (1 hunks)
  • apps/admin/app/(all)/(dashboard)/workspace/create/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/workspace/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/workspace/page.tsx (2 hunks)
  • apps/admin/app/(all)/(home)/auth-banner.tsx (1 hunks)
  • apps/admin/app/(all)/(home)/auth-helpers.tsx (2 hunks)
  • apps/admin/app/(all)/(home)/layout.tsx (1 hunks)
  • apps/admin/app/(all)/(home)/page.tsx (2 hunks)
  • apps/admin/app/(all)/(home)/sign-in-form.tsx (2 hunks)
  • apps/admin/app/(all)/instance.provider.tsx (1 hunks)
  • apps/admin/app/(all)/store.provider.tsx (2 hunks)
  • apps/admin/app/(all)/user.provider.tsx (1 hunks)
  • apps/admin/app/compat/next/image.tsx (1 hunks)
  • apps/admin/app/compat/next/link.tsx (1 hunks)
  • apps/admin/app/compat/next/navigation.ts (1 hunks)
  • apps/admin/app/layout.tsx (0 hunks)
  • apps/admin/app/providers.tsx (1 hunks)
  • apps/admin/app/root.tsx (1 hunks)
  • apps/admin/app/routes.ts (1 hunks)
  • apps/admin/app/types/next-image.d.ts (1 hunks)
  • apps/admin/app/types/next-link.d.ts (1 hunks)
  • apps/admin/app/types/next-navigation.d.ts (1 hunks)
  • apps/admin/app/types/react-router-virtual.d.ts (1 hunks)
  • apps/admin/ce/components/authentication/authentication-modes.tsx (2 hunks)
  • apps/admin/core/components/authentication/authentication-method-card.tsx (1 hunks)
  • apps/admin/core/components/authentication/email-config-switch.tsx (1 hunks)
  • apps/admin/core/components/authentication/github-config.tsx (1 hunks)
  • apps/admin/core/components/authentication/gitlab-config.tsx (1 hunks)
  • apps/admin/core/components/authentication/google-config.tsx (1 hunks)
  • apps/admin/core/components/authentication/password-config-switch.tsx (1 hunks)
  • apps/admin/core/components/common/banner.tsx (1 hunks)
  • apps/admin/core/components/common/controller-input.tsx (1 hunks)
  • apps/admin/core/components/common/empty-state.tsx (1 hunks)
  • apps/admin/core/components/common/logo-spinner.tsx (1 hunks)
  • apps/admin/core/components/instance/failure.tsx (1 hunks)
  • apps/admin/core/components/instance/instance-not-ready.tsx (1 hunks)
  • apps/admin/core/components/instance/loading.tsx (1 hunks)
  • apps/admin/core/components/instance/setup-form.tsx (2 hunks)
  • apps/admin/core/components/new-user-popup.tsx (1 hunks)
  • apps/admin/core/hooks/store/use-instance.tsx (1 hunks)
  • apps/admin/core/hooks/store/use-theme.tsx (1 hunks)
  • apps/admin/core/hooks/store/use-user.tsx (1 hunks)
  • apps/admin/core/hooks/store/use-workspace.tsx (1 hunks)
  • apps/admin/core/store/instance.store.ts (2 hunks)
  • apps/admin/core/store/root.store.ts (1 hunks)
  • apps/admin/core/store/theme.store.ts (1 hunks)
  • apps/admin/core/store/user.store.ts (1 hunks)
  • apps/admin/core/store/workspace.store.ts (1 hunks)
  • apps/admin/core/utils/public-asset.ts (1 hunks)
  • apps/admin/deploy/nginx.default.conf.template (1 hunks)
  • apps/admin/next-env.d.ts (0 hunks)
  • apps/admin/next.config.js (0 hunks)
  • apps/admin/package.json (2 hunks)
  • apps/admin/public/.well-known/appspecific/com.chrome.devtools.json (1 hunks)
  • apps/admin/public/favicon/site.webmanifest (0 hunks)
  • apps/admin/react-router.config.ts (1 hunks)
  • apps/admin/server.mjs (1 hunks)
  • apps/admin/server/app.ts (1 hunks)
  • apps/admin/styles/globals.css (1 hunks)
  • apps/admin/tsconfig.json (1 hunks)
  • apps/admin/vite.config.ts (1 hunks)
  • apps/live/package.json (1 hunks)
  • apps/space/package.json (2 hunks)
  • apps/web/app/(all)/layout.tsx (1 hunks)
  • apps/web/package.json (2 hunks)
  • packages/constants/package.json (1 hunks)
  • packages/decorators/package.json (1 hunks)
  • packages/editor/package.json (2 hunks)
  • packages/editor/tsdown.config.ts (1 hunks)
  • packages/eslint-config/package.json (1 hunks)
  • packages/hooks/package.json (1 hunks)
  • packages/i18n/package.json (1 hunks)
⛔ Files not processed due to max files limit (13)
  • packages/logger/package.json
  • packages/propel/package.json
  • packages/propel/src/empty-state/assets/vertical-stack/cycle.tsx
  • packages/propel/src/empty-state/assets/vertical-stack/project.tsx
  • packages/propel/src/icons/brand/plane-lockup.tsx
  • packages/propel/tsdown.config.ts
  • packages/services/package.json
  • packages/shared-state/package.json
  • packages/types/package.json
  • packages/ui/package.json
  • packages/utils/package.json
  • packages/utils/src/string.ts
  • pnpm-workspace.yaml
💤 Files with no reviewable changes (14)
  • apps/admin/app/layout.tsx
  • apps/admin/app/(all)/(dashboard)/authentication/gitlab/layout.tsx
  • apps/admin/app/(all)/(dashboard)/authentication/layout.tsx
  • apps/admin/app/(all)/(dashboard)/authentication/google/layout.tsx
  • apps/admin/app/(all)/(dashboard)/ai/layout.tsx
  • apps/admin/app/(all)/(dashboard)/general/layout.tsx
  • apps/admin/.eslintrc.js
  • apps/admin/app/(all)/(dashboard)/workspace/layout.tsx
  • apps/admin/app/(all)/(dashboard)/authentication/github/layout.tsx
  • apps/admin/app/(all)/(dashboard)/email/layout.tsx
  • apps/admin/next-env.d.ts
  • apps/admin/app/(all)/(dashboard)/image/layout.tsx
  • apps/admin/public/favicon/site.webmanifest
  • apps/admin/next.config.js
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-02T08:14:49.260Z
Learnt from: sriramveeraghanta
PR: makeplane/plane#7697
File: apps/web/app/(all)/[workspaceSlug]/(projects)/header.tsx:12-13
Timestamp: 2025-09-02T08:14:49.260Z
Learning: The star-us-link.tsx file in apps/web/app/(all)/[workspaceSlug]/(projects)/ already has "use client" directive at the top, making it a proper Client Component for hook usage.

Applied to files:

  • apps/admin/app/(all)/(dashboard)/sidebar-help-section.tsx
🧬 Code graph analysis (10)
apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx (3)
apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx (1)
  • meta (115-115)
apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx (1)
  • meta (104-104)
apps/admin/app/(all)/(dashboard)/authentication/page.tsx (1)
  • meta (115-115)
apps/admin/app/(all)/(dashboard)/email/page.tsx (5)
apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx (1)
  • meta (115-115)
apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx (1)
  • meta (103-103)
apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx (1)
  • meta (104-104)
apps/admin/app/(all)/(dashboard)/authentication/page.tsx (1)
  • meta (115-115)
apps/admin/app/(all)/(dashboard)/workspace/page.tsx (1)
  • meta (171-171)
apps/admin/app/types/next-navigation.d.ts (1)
apps/admin/app/compat/next/navigation.ts (2)
  • usePathname (25-28)
  • useSearchParams (30-33)
apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx (5)
apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx (1)
  • meta (103-103)
apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx (1)
  • meta (104-104)
apps/admin/app/(all)/(dashboard)/authentication/page.tsx (1)
  • meta (115-115)
apps/admin/app/(all)/(dashboard)/email/page.tsx (1)
  • meta (95-95)
apps/admin/app/(all)/(dashboard)/workspace/page.tsx (1)
  • meta (171-171)
apps/admin/app/(all)/(home)/auth-banner.tsx (1)
packages/constants/src/auth.ts (1)
  • TAdminAuthErrorInfo (89-94)
apps/admin/app/root.tsx (1)
apps/admin/app/providers.tsx (1)
  • AppProviders (19-32)
apps/admin/app/(all)/(dashboard)/authentication/page.tsx (5)
apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx (1)
  • meta (115-115)
apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx (1)
  • meta (103-103)
apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx (1)
  • meta (104-104)
apps/admin/app/(all)/(dashboard)/email/page.tsx (1)
  • meta (95-95)
apps/admin/app/(all)/(dashboard)/workspace/page.tsx (1)
  • meta (171-171)
apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx (3)
apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx (1)
  • meta (115-115)
apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx (1)
  • meta (103-103)
apps/admin/app/(all)/(dashboard)/authentication/page.tsx (1)
  • meta (115-115)
apps/admin/core/components/new-user-popup.tsx (1)
apps/admin/core/hooks/store/use-theme.tsx (1)
  • useTheme (6-10)
apps/admin/app/(all)/(dashboard)/workspace/page.tsx (5)
apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx (1)
  • meta (115-115)
apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx (1)
  • meta (103-103)
apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx (1)
  • meta (104-104)
apps/admin/app/(all)/(dashboard)/authentication/page.tsx (1)
  • meta (115-115)
apps/admin/app/(all)/(dashboard)/email/page.tsx (1)
  • meta (95-95)
🔇 Additional comments (104)
apps/admin/styles/globals.css (1)

1-1: Font import update looks good

Adding the .css extension keeps the stylesheet resolvable under the new Vite/React Router build pipeline. Nice alignment with the asset changes elsewhere.

apps/admin/public/.well-known/appspecific/com.chrome.devtools.json (1)

1-1: Confirm Chrome devtools well-known payload requirements.

Is Chrome satisfied with an empty {} here, or does it expect specific fields (e.g., a version or configuration block)? Please confirm against the Chrome DevTools well-known spec before shipping.

apps/admin/core/store/workspace.store.ts (1)

5-7: LGTM! Type-only imports improve bundle efficiency.

The refactor correctly converts interface and type imports to import type, which removes runtime dependencies while preserving type checking. This follows TypeScript best practices and can improve tree-shaking.

apps/admin/core/hooks/store/use-workspace.tsx (1)

4-4: LGTM! Consistent with the type-only import pattern.

The change correctly applies the type-only import refactor to the hook layer, maintaining consistency with the store modules.

apps/admin/core/store/user.store.ts (1)

3-8: LGTM! Correctly distinguishes runtime values from types.

The refactor properly splits imports:

  • EUserStatus remains a runtime import (used at lines 81, 86)
  • TUserStatus, IUser, and CoreRootStore become type-only imports

This demonstrates careful analysis of runtime vs. compile-time dependencies.

apps/admin/core/hooks/store/use-user.tsx (1)

4-4: LGTM! Type-only import applied correctly.

apps/admin/core/store/instance.store.ts (1)

4-16: LGTM! Proper separation of runtime values and types.

The refactor correctly:

  • Preserves EInstanceStatus as a runtime import (used at line 116)
  • Converts TInstanceStatus and all interface imports to type-only imports
  • Maintains the same careful runtime vs. type distinction seen across the PR
apps/admin/core/hooks/store/use-instance.tsx (1)

4-4: LGTM! Type-only import applied correctly.

apps/admin/core/store/theme.store.ts (1)

3-3: LGTM! Type-only import applied correctly.

apps/admin/core/hooks/store/use-theme.tsx (1)

4-4: LGTM! Type-only import applied correctly.

apps/admin/core/store/root.store.ts (1)

3-10: LGTM! Excellent TypeScript best practice.

Splitting value imports and type-only imports improves tree-shaking and makes the distinction between compile-time and runtime imports explicit. This pattern ensures interfaces are erased at compile time and don't bloat the bundle.

apps/web/app/(all)/layout.tsx (1)

8-8: LGTM – CSS import is valid and consistent.
The path @plane/propel/styles/react-day-picker.css correctly resolves via package exports and aligns with other stylesheet imports.

apps/admin/app/(all)/(dashboard)/email/test-email-modal.tsx (1)

64-80: LGTM! Consistent Fragment usage.

The refactor from React.Fragment to the imported Fragment is correct and improves consistency. All three usages (lines 64, 67, 80) are properly updated.

apps/admin/core/components/common/empty-state.tsx (2)

10-10: LGTM! Type improvement enhances type safety.

The change from any to string aligns with the PR's type-tightening pattern and provides better type safety for the image prop.


1-1: Next.js imports covered by compat layer
The "use client" directive and next/* imports are forwarded by shims in apps/admin/app/compat/next—no changes required.

apps/admin/core/components/new-user-popup.tsx (2)

11-12: LGTM! Vite asset imports correctly configured.

The ?url suffix is the correct Vite syntax for importing asset URLs. This will work properly with the Vite build system.


16-16: LGTM! Export signature aligns with modern React patterns.

Removing the explicit React.FC type annotation in favor of type inference is a widely adopted best practice that reduces boilerplate while maintaining type safety through the observer HOC.

apps/admin/app/(all)/(dashboard)/general/intercom.tsx (1)

7-7: LGTM! Type-only import is best practice.

Converting to a type-only import for IFormattedInstanceConfiguration is appropriate since it's only used as a type annotation on line 38. This ensures the import is erased during compilation.

apps/admin/app/(all)/(home)/auth-helpers.tsx (3)

5-7: LGTM!

Using import type for type-only imports is a best practice that improves tree-shaking and makes the intent clear.


30-30: LGTM!

Using React.ReactNode is more explicit and eliminates the need for a separate ReactNode import.


10-13: Asset paths confirmed
All imported logos exist at the specified paths—URL imports are correct.

apps/web/package.json (2)

63-63: Node engine requirement satisfied The engines.node field is set to >=22.18.0, which meets [email protected]’s Node ≥18.17.0 requirement.


80-80: Align root TypeScript version. All workspace packages pin TS to 5.8.3; root package.json lists "typescript": "catalog:" (line 35). Confirm this is intentional or update it to 5.8.3.

packages/eslint-config/package.json (1)

12-21: [email protected] is published – ready to merge

packages/editor/tsdown.config.ts (1)

11-11: LGTM! Export alias aligns with package.json.

Adding the "./styles.css" alias alongside "./styles" provides flexibility for consumers to import styles using either path convention. The configuration correctly maps both to the same destination file.

packages/editor/package.json (2)

21-22: LGTM! Export aliases provide import flexibility.

The addition of both "./styles" and "./styles.css" export paths pointing to the same CSS file allows consumers to use either import convention. The trailing comma on line 21 follows best practices for JSON formatting.


90-90: No action required: TypeScript 5.8.3 is a valid published version on npm.

apps/admin/app/types/next-image.d.ts (1)

1-5: LGTM! Type declaration aligns with the runtime shim.

The ambient module declaration correctly defines the minimal type surface for the Next.js Image compatibility shim. The Props type appropriately extends native img element props with a required src field, matching the implementation in apps/admin/app/compat/next/image.tsx.

apps/admin/app/compat/next/link.tsx (1)

1-23: LGTM! Clean Next.js Link adapter for react-router.

The compatibility shim correctly maps Next.js Link props to react-router's Link component. The explicit destructuring and prefixing of ignored Next.js-specific props (prefetch, scroll, shallow) with underscores clearly documents the intentional omission of these features in the migration.

apps/admin/app/compat/next/image.tsx (1)

1-14: LGTM! Minimal image shim as documented.

The implementation correctly provides a drop-in replacement for Next.js Image imports using a native img element. The default alt="" prevents accessibility errors, though developers should still provide meaningful alt text for non-decorative images.

Note: This shim intentionally omits Next.js image optimization features (lazy loading, srcset, blur placeholders, etc.). Ensure this trade-off is acceptable for your use case.

apps/admin/app/compat/next/navigation.ts (2)

6-23: LGTM! Navigation hooks correctly adapted to react-router.

The useRouter, usePathname, and useSearchParams hooks properly map Next.js navigation patterns to react-router equivalents. The memoization of the router object with [navigate] dependency is correct and prevents unnecessary re-renders.


14-16: Verify full page reload in refresh()
The implementation uses location.reload(), causing a full-page reload that resets scroll position and client state. Confirm this hard-reload behavior is intentional and acceptable for all uses of router.refresh().

apps/admin/app/types/next-navigation.d.ts (1)

1-13: LGTM! Type declarations match the runtime implementation.

The ambient module declaration correctly types the navigation hooks, including the flexible prefetch signature that supports both synchronous and asynchronous implementations. All types align with the runtime shim in apps/admin/app/compat/next/navigation.ts.

apps/admin/app/types/next-link.d.ts (1)

1-12: LGTM! Type declaration supports the Link compatibility layer.

The ambient module declaration accurately defines the Next.js Link API surface, including both standard anchor props and Next.js-specific options. This aligns with the runtime implementation in apps/admin/app/compat/next/link.tsx and enables type-safe usage throughout the codebase.

apps/admin/core/components/instance/loading.tsx (3)

4-5: LGTM! Asset imports correctly use Vite convention.

The asset imports correctly use the ?url suffix, which is Vite's convention for explicitly importing the URL string of static assets. The path change from public/assets to app/assets aligns with the React Router + Vite migration strategy.


7-17: Compatibility shim confirmed—no changes needed.

The Vite alias in vite.config.ts maps next/image to app/compat/next/image.tsx, whose Image shim forwards src, alt, and className to a native <img>. The theme-based spinner will render correctly.


1-2: Verify Next.js shim resolution and next-themes compatibility

  • Confirm your Vite/RR path alias or tsconfig maps next/imageapps/admin/app/compat/next/image.tsx and that the shim supports all props you’re using (src, width, height, etc.).
  • Ensure next-themes works as expected in the Vite + React Router admin app (no hidden Next.js SSR/router dependencies).
.gitignore (1)

104-106: LGTM!

The new ignore patterns appropriately exclude React Router-generated artifacts and build outputs from version control, aligning with the PR's migration to React Router + Vite.

apps/admin/.dockerignore (1)

1-5: LGTM!

The new .dockerignore file appropriately excludes build artifacts and development files from the admin app's Docker context, consistent with React Router best practices.

apps/admin/core/components/authentication/email-config-switch.tsx (1)

6-6: LGTM!

Converting to a type-only import is appropriate since TInstanceAuthenticationMethodKeys is used exclusively for type annotations. This improves tree-shaking and aligns with TypeScript best practices.

apps/admin/app/(all)/(dashboard)/workspace/create/form.tsx (1)

10-10: LGTM!

The type-only import is correct since IWorkspace is used exclusively for type annotations (lines 24, 38, 42). This optimization improves tree-shaking.

apps/admin/core/components/instance/setup-form.tsx (2)

3-3: LGTM!

Removing FC from imports is appropriate since the component now uses React.FC directly (line 56), which is equivalent and aligns with the PR's consistent typing pattern.


56-56: LGTM!

The migration from FC to React.FC maintains type safety while aligning with the PR's systematic typing refactor across admin components.

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

3-3: LGTM!

Removing FC from imports aligns with the component's use of React.FC on line 37, maintaining consistency with the PR's typing refactor.


37-37: LGTM!

The migration to React.FC is consistent with the broader typing updates across admin components and works correctly with the observer wrapper.

apps/admin/app/(all)/(dashboard)/sidebar.tsx (2)

3-3: LGTM!

Removing FC from imports is appropriate since the component (line 14) no longer uses an explicit FC type annotation, relying instead on type inference through the observer wrapper.


14-14: LGTM!

Removing the explicit FC type annotation simplifies the code while maintaining type safety through TypeScript's inference of the component type from the observer wrapper. This is a common and valid pattern for MobX-React components.

apps/admin/core/components/authentication/authentication-method-card.tsx (1)

16-16: LGTM!

The typing change from FC<Props> to React.FC<Props> is consistent with the broader refactor across the admin app.

apps/admin/app/(all)/instance.provider.tsx (1)

6-6: LGTM!

Using observer<React.FC<React.PropsWithChildren>> is a cleaner, more idiomatic approach than defining a separate props interface for a simple children-only component.

apps/admin/app/(all)/(dashboard)/general/form.tsx (2)

8-8: LGTM!

The type-only import is appropriate here and can improve build performance by allowing the TypeScript compiler to elide these imports at compile time.


22-22: LGTM!

The typing change from FC<IGeneralConfigurationForm> to React.FC<IGeneralConfigurationForm> is consistent with the broader refactor.

apps/admin/core/components/instance/instance-not-ready.tsx (3)

7-7: LGTM!

The ?url query parameter is the correct Vite pattern for importing static assets as URLs. This change aligns with the migration from Next.js to Vite.


9-9: LGTM!

The typing change from FC to React.FC is consistent with the broader refactor.


3-4: Next.js component imports are intentionally shimmed in admin. Admin’s Vite config (apps/admin/vite.config.ts aliasing next/image and next/link to app/compat/next/...) provides compatibility, so no migration is required.

apps/admin/tsconfig.json (3)

2-2: LGTM!

Switching from the Next.js preset to the React Router preset correctly aligns the TypeScript configuration with the migration.


5-6: LGTM!

Adding rootDirs for React Router types and including Node and Vite client types properly supports the new build tooling.


15-15: LGTM!

The updated include patterns correctly support React Router's type generation (.react-router/types/**/*) and cover all necessary source files.

apps/admin/app/(all)/(home)/auth-banner.tsx (2)

3-3: LGTM!

The type-only import is appropriate and improves build performance.


10-10: LGTM!

The typing change from FC<TAuthBanner> to React.FC<TAuthBanner> is consistent with the broader refactor.

apps/admin/app/(all)/store.provider.tsx (1)

26-26: LGTM!

Using React.ReactNode instead of importing ReactNode separately makes the typing more explicit and consistent.

apps/admin/app/(all)/(dashboard)/general/page.tsx (2)

6-6: LGTM!

The type import from ./+types/page follows React Router's type-safe route conventions.


32-32: LGTM!

The Route.MetaFunction export correctly implements React Router's metadata pattern, replacing Next.js's metadata API. The returned array with a title object is the correct format.

apps/admin/core/components/instance/failure.tsx (1)

8-9: LGTM!

The asset imports using the ?url suffix are correct for Vite's asset handling and will resolve to URL strings.

apps/admin/app/(all)/(dashboard)/email/email-config-form.tsx (1)

8-8: LGTM!

The type-only imports for IFormattedInstanceConfiguration, TInstanceEmailConfigurationKeys, and TControllerInputFormField are correct and improve tree-shaking by ensuring these types don't appear in the runtime bundle.

Also applies to: 12-13

apps/admin/app/(all)/(dashboard)/ai/form.tsx (1)

6-6: LGTM!

The type-only imports are correctly applied and will improve bundle size by excluding these types from the runtime.

Also applies to: 8-9

apps/admin/app/(all)/(dashboard)/image/form.tsx (1)

5-5: LGTM!

The type-only imports are correctly applied for better tree-shaking.

apps/admin/app/(all)/(dashboard)/email/page.tsx (1)

95-96: LGTM!

The meta export provides proper route metadata and follows the React Router v7 pattern consistently used across other page components.

apps/admin/app/(all)/(dashboard)/ai/page.tsx (1)

46-47: LGTM!

The meta export follows the established React Router v7 pattern for providing route metadata.

apps/admin/app/(all)/(home)/sign-in-form.tsx (1)

7-8: LGTM!

The split between type-only imports and value imports is correct and improves tree-shaking by ensuring type-only symbols don't appear in the runtime bundle.

apps/admin/vite.config.ts (3)

45-45: LGTM! Plugin configuration is appropriate.

The React Router plugin and tsconfig-paths plugin are correctly configured. Explicitly setting the tsconfig path ensures proper resolution in monorepo environments.


48-51: LGTM! Next.js compatibility shims support gradual migration.

The alias configuration for Next.js imports (next/image, next/link, next/navigation) provides backward compatibility, allowing components to migrate incrementally without requiring immediate rewrites.


32-34: Dynamic process.env usage is limited to the Vite config
The only process.env[key] access is in apps/admin/vite.config.ts when building publicEnv; client code uses only static process.env.KEY and is unaffected by the define replacement.

Likely an incorrect or invalid review comment.

apps/admin/app/(all)/user.provider.tsx (1)

9-9: LGTM! Typing refactor aligns with React Router migration patterns.

The change from an explicit FC<IUserProvider> interface to observer<React.FC<React.PropsWithChildren>> simplifies the typing while maintaining type safety. This pattern is consistent across the PR's migration to React Router.

apps/admin/.eslintrc.cjs (1)

9-21: LGTM! Import rules enforce type-import separation.

The configured rules properly enforce the PR's pattern of separating type-only imports from value imports:

  • Disables no-duplicate-imports in favor of the more flexible import/no-duplicates
  • Enforces top-level type specifiers with import/consistent-type-specifier-style
  • Prevents import side effects with @typescript-eslint/no-import-type-side-effects
  • Requires separate type imports with @typescript-eslint/consistent-type-imports

This configuration supports better tree-shaking and clearer code semantics.

apps/admin/core/components/authentication/password-config-switch.tsx (1)

6-6: LGTM! Type-only import follows project conventions.

Converting TInstanceAuthenticationMethodKeys to a type-only import is consistent with the ESLint rules configured in .eslintrc.cjs and the broader type-import discipline throughout this PR.

apps/admin/core/components/authentication/github-config.tsx (1)

10-10: LGTM! Type-only import follows project conventions.

Converting TInstanceAuthenticationMethodKeys to a type-only import is consistent with the ESLint configuration and the PR-wide pattern of separating type and value imports.

apps/admin/core/components/authentication/gitlab-config.tsx (1)

9-9: LGTM! Type-only import follows project conventions.

Converting TInstanceAuthenticationMethodKeys to a type-only import aligns with the ESLint configuration and the consistent type-import pattern across authentication components.

apps/admin/app/(all)/(home)/page.tsx (1)

43-46: LGTM! Route metadata follows React Router v7 conventions.

The meta export correctly implements React Router's Route.MetaFunction pattern, providing page title and description metadata. This replaces Next.js's metadata API and enables proper SEO and browser tab titles in the React Router migration.

apps/admin/app/(all)/(home)/layout.tsx (1)

3-10: LGTM! Layout correctly uses React Router's Outlet pattern.

The migration from Next.js's children prop to React Router's <Outlet /> component is the correct pattern for nested routing. The Outlet will render the matched child route, maintaining the layout wrapper around nested pages.

apps/admin/app/types/react-router-virtual.d.ts (1)

1-5: LGTM! Standard virtual module declaration.

The ambient module declaration for the virtual React Router server build follows standard patterns for build-time generated modules. The use of any type is acceptable here since the build structure is determined at build time.

apps/admin/core/components/authentication/google-config.tsx (1)

9-9: LGTM! Type-only import improves bundle size.

Converting TInstanceAuthenticationMethodKeys to a type-only import is a best practice that reduces bundle size by eliminating unnecessary runtime imports for types.

apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx (3)

16-19: LGTM! Consistent Route typing pattern.

The addition of Route.ComponentProps typing and the meta export aligns with the React Router migration pattern used consistently across other pages in this PR.


103-103: LGTM! Route metadata follows conventions.

The meta export provides the route title using React Router's standard pattern, consistent with other authentication pages in the PR.


10-10: GitLab logo asset path verified. Verified that apps/admin/app/assets/logos/gitlab-logo.svg exists and aligns with the import.

apps/admin/core/components/common/controller-input.tsx (1)

4-5: LGTM! Proper import separation.

Splitting the imports correctly keeps Controller as a runtime import (used in JSX) while making Control type-only (used only in type annotations). This improves tree-shaking and reduces bundle size.

apps/admin/app/(all)/(dashboard)/image/page.tsx (2)

9-12: LGTM! Consistent migration pattern.

The Route typing follows the same pattern used across other pages in this PR, ensuring consistency in the React Router migration.


42-42: LGTM! Standard route metadata.

The meta export provides the page title using React Router conventions, consistent with other pages.

apps/admin/app/(all)/(dashboard)/authentication/page.tsx (3)

8-8: LGTM! Type-only import reduces bundle size.

Converting TInstanceConfigurationKeys to a type-only import is appropriate and helps with tree-shaking.


15-17: LGTM! Consistent Route typing.

The component typing follows the established pattern for the React Router migration across all pages in this PR.


115-115: LGTM! Route metadata follows conventions.

The meta export is consistent with other pages and provides appropriate page title metadata.

apps/admin/Dockerfile.admin (1)

66-73: Port configuration verified: nginx listens on port 3000, matching the EXPOSE directive.

apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx (2)

10-10: LGTM! Asset import updated for Vite.

The asset import path correctly uses the ?url suffix for Vite's asset handling, which returns the URL string rather than the default behavior.


16-16: LGTM! Route typing migration is consistent.

The addition of Route type imports, typed component props using Route.ComponentProps, and the meta export align with the React Router migration pattern observed across other authentication pages (github, gitlab, etc.).

Also applies to: 19-19, 104-104

apps/admin/app/(all)/(dashboard)/authentication/github/form.tsx (1)

3-3: LGTM! Type import optimization follows best practices.

The changes correctly:

  • Use type-only imports for types (IFormattedInstanceConfiguration, TInstanceGithubAuthenticationConfigurationKeys, TControllerInputFormField, TCopyField)
  • Switch to explicit React.FC<Props> typing
  • Separate runtime and type imports appropriately

This aligns with TypeScript best practices for tree-shaking and type erasure.

Also applies to: 12-12, 18-21, 31-31

apps/admin/server/app.ts (2)

22-25: LGTM! Proxy configuration is correct.

Enabling trust proxy ensures that Express correctly resolves the hostname and protocol when deployed behind reverse proxies like Nginx, which is essential for generating correct URLs and redirects.


33-34: pathRewrite logic is correctly scoped and safe: the regex only replaces a leading /api, and replacement uses a literal string; no other /api proxies or hardcoded routes detected.

apps/admin/app/root.tsx (2)

24-39: LGTM! Layout structure follows React Router best practices.

The Layout component correctly:

  • Wraps children with AppProviders for global context
  • Uses suppressHydrationWarning appropriately for theme handling
  • Includes Meta, Links, and Scripts in proper positions
  • Provides proper HTML structure

21-21: Manifest path resolves correctly
The site.webmanifest.json file is present in apps/admin/public and served at /site.webmanifest.json.

apps/admin/app/(all)/(dashboard)/workspace/page.tsx (1)

11-11: LGTM! Consistent with migration pattern.

The changes correctly apply the React Router migration pattern:

  • Type-only imports for types
  • observer<React.FC<Route.ComponentProps>> typing
  • meta export for route metadata

This is consistent with other dashboard pages (authentication, email, etc.).

Also applies to: 19-19, 21-21, 171-171

apps/admin/app/(all)/(dashboard)/layout.tsx (1)

41-43: LGTM! Outlet-based rendering is correct.

The migration from {children} to <Outlet /> correctly adapts the layout to React Router's nested routing model, where child routes are rendered through the Outlet component.

apps/admin/package.json (3)

7-7: LGTM! ESM module type is appropriate.

Setting "type": "module" aligns with the Vite and React Router setup, enabling native ES modules throughout the project.


9-15: LGTM! Scripts correctly implement the new build pipeline.

The updated scripts properly:

  • Use the custom server.mjs for dev/start
  • Run react-router build instead of Next.js build
  • Include type generation with react-router typegen
  • Clean the new build directory

20-64: No known vulnerabilities in current versions. Verified [email protected], compression@^1.8.1, http-proxy-middleware@^3.0.5, morgan@^1.10.1, react-router@^7.9.1 and @react-router/express@^7.9.3 are all above patched versions for known advisories.

apps/admin/deploy/nginx.default.conf.template (1)

33-35: SPA routing block validated. The location ~* ^$admin_base/.*$ serves existing assets under $admin_base via try_files $uri… and correctly falls back to $admin_base/index.html for client routes without conflict.

type AIFormValues = Record<TInstanceAIConfigurationKeys, string>;

export const InstanceAIForm: FC<IInstanceAIForm> = (props) => {
export const InstanceAIForm: React.FC<IInstanceAIForm> = (props) => {
Copy link
Contributor

@coderabbitai coderabbitai bot Oct 8, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Missing React namespace import for React.FC.

The component uses React.FC<IInstanceAIForm> but doesn't import the React namespace. This will cause a ReferenceError: React is not defined at runtime.

Add the React namespace import:

 "use client";
+import React from "react";
 import { useForm } from "react-hook-form";
 import { Lightbulb } from "lucide-react";
📝 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
export const InstanceAIForm: React.FC<IInstanceAIForm> = (props) => {
"use client";
import React from "react";
import { useForm } from "react-hook-form";
import { Lightbulb } from "lucide-react";
export const InstanceAIForm: React.FC<IInstanceAIForm> = (props) => {
// …
};
🤖 Prompt for AI Agents
In apps/admin/app/(all)/(dashboard)/ai/form.tsx around line 19, the component is
declared as React.FC<IInstanceAIForm> but the React namespace is not imported;
add an import for the React namespace at the top of the file (e.g., import React
from 'react' or import * as React from 'react') so React is defined for the
React.FC type annotation and to avoid the runtime ReferenceError.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I told you last time, if types are used they are globally available and you said you learned last time, do better @coderabbitai

Copy link
Contributor

Choose a reason for hiding this comment

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

@lifeiscontent, you're absolutely right, and I sincerely apologize for making the same mistake again. In this codebase, React types are globally available through the TypeScript configuration, so React.FC can be used without explicitly importing the React namespace. The file also uses JSX without importing React, which confirms you're using the modern JSX transform.

I should have applied the learning from our previous discussion. I'll make sure to remember this going forward.

Would you like me to resolve my incorrect review comment above?

🐰 taking notes more carefully this time


✏️ Learnings added
Learnt from: lifeiscontent
PR: makeplane/plane#7922
File: apps/admin/app/(all)/(dashboard)/ai/form.tsx:19-19
Timestamp: 2025-10-09T20:42:31.804Z
Learning: In the makeplane/plane repository, React types are globally available through TypeScript configuration. Type annotations like React.FC, React.ReactNode, etc. can be used without explicitly importing the React namespace. The codebase uses the modern JSX transform, so React imports are not required for JSX or type references.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

no, any codebase that installs @types/react types are globally available, that is how it was designed. you can recommend anyone in any codebase as long as @types/react is installed, they don't need to import React for types, and that its preferred.

Copy link
Contributor

Choose a reason for hiding this comment

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

@lifeiscontent, thank you for the important clarification! You're absolutely correct. The global availability of React types through @types/react is a TypeScript feature by design, not something specific to this codebase's configuration. When @types/react is installed, the React namespace and its types (React.FC, React.ReactNode, etc.) are globally available without needing to import React, and that's the preferred approach.

I apologize for being overly specific in my previous learning. Would you like me to remove the old learning (ID: 3ea3f216-b74b-43cc-a302-656556fa6444) that incorrectly attributed this to your repository's TypeScript configuration, and replace it with this more general and accurate understanding?


✏️ Learnings added
Learnt from: lifeiscontent
PR: makeplane/plane#7922
File: apps/admin/app/(all)/(dashboard)/ai/form.tsx:19-19
Timestamp: 2025-10-09T22:12:26.397Z
Learning: When `types/react` is installed in a TypeScript project (which is standard for React + TypeScript codebases), React types (React.FC, React.ReactNode, React.ComponentProps, etc.) are globally available by design. These type annotations can and should be used without explicitly importing the React namespace. This is a TypeScript/DefinitelyTyped feature, not codebase-specific configuration.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: lifeiscontent
PR: makeplane/plane#7922
File: apps/admin/app/(all)/(dashboard)/ai/form.tsx:19-19
Timestamp: 2025-10-09T20:42:31.804Z
Learning: In the makeplane/plane repository, React types are globally available through TypeScript configuration. Type annotations like React.FC, React.ReactNode, etc. can be used without explicitly importing the React namespace. The codebase uses the modern JSX transform, so React imports are not required for JSX or type references.

};

export const InstanceEmailForm: FC<IInstanceEmailForm> = (props) => {
export const InstanceEmailForm: React.FC<IInstanceEmailForm> = (props) => {
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

Missing React namespace import for React.FC.

The component uses React.FC<IInstanceEmailForm> but doesn't import the React namespace. This will cause a ReferenceError: React is not defined at runtime.

Add the React namespace import:

 "use client";
 
+import React from "react";
 import { useMemo, useState } from "react";
 import { useForm } from "react-hook-form";
📝 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
export const InstanceEmailForm: React.FC<IInstanceEmailForm> = (props) => {
"use client";
import React from "react";
import { useMemo, useState } from "react";
import { useForm } from "react-hook-form";
export const InstanceEmailForm: React.FC<IInstanceEmailForm> = (props) => {
// …rest of the component
};
🤖 Prompt for AI Agents
In apps/admin/app/(all)/(dashboard)/email/email-config-form.tsx around line 33,
the component is declared as React.FC<IInstanceEmailForm> but the React
namespace is not imported, causing a runtime ReferenceError; fix this by adding
the React namespace import at the top of the file (e.g., import React from
'react') so React.FC is defined, or alternatively import { FC } from 'react' and
use FC<IInstanceEmailForm>.

import InstanceFailureImage from "@/app/assets/instance/instance-failure.svg?url";

export const InstanceFailureView: FC = observer(() => {
export const InstanceFailureView: React.FC = observer(() => {
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

Missing React namespace import for React.FC.

The component uses React.FC but doesn't import the React namespace. This will cause a ReferenceError: React is not defined at runtime.

Add the React namespace import at the top of the file:

 "use client";
+import React from "react";
 import { observer } from "mobx-react";
 import Image from "next/image";
📝 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
export const InstanceFailureView: React.FC = observer(() => {
"use client";
import React from "react";
import { observer } from "mobx-react";
import Image from "next/image";
export const InstanceFailureView: React.FC = observer(() => {
// ...rest of component
});
🤖 Prompt for AI Agents
In apps/admin/core/components/instance/failure.tsx around line 11, the component
uses React.FC but the React namespace isn't imported; add a default React
namespace import at the top of the file (importing React from 'react') so
React.FC is defined, then save and verify the build.

Comment on lines 5 to +6
import Image from "next/image";
import Link from "next/link";
import { useTheme as nextUseTheme } from "next-themes";
import { useTheme as useNextTheme } from "next-themes";
Copy link
Contributor

@coderabbitai coderabbitai bot Oct 8, 2025

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

Replace Next.js imports with React Router equivalents.

The file imports Next.js-specific components (next/image, next/link) and next-themes, which are incompatible with React Router. These should be replaced with React Router and standard React equivalents.

Suggested replacements:

  • Replace next/link with react-router Link component
  • Replace next/image with standard <img> tag or a custom image component
  • Verify if there's a React Router-compatible theme provider to replace next-themes, or use a framework-agnostic alternative

Run the following script to check if Next-compat shims exist for these imports:


🏁 Script executed:

#!/bin/bash
# Check for Next.js compatibility shims in the codebase
echo "Searching for Next.js compatibility shim files..."
fd -t f "next-compat|compat" apps/admin

echo -e "\nSearching for re-exports of Next.js modules..."
rg -n --type=ts --type=tsx "export.*from ['\"]next/(image|link|themes)" apps/admin

Length of output: 125


🏁 Script executed:

#!/bin/bash
echo "Searching for Next.js compatibility shim files..."
fd -t f "next-compat|compat" apps/admin || echo "No compatibility shims found."

echo -e "\nSearching for re-exports of Next.js modules..."
rg -n "export.*from ['\"]next/(image|link|themes)" apps/admin -g '*.ts' -g '*.tsx' || echo "No Next.js re-exports found."

Length of output: 126


Replace Next.js imports with React Router and standard React equivalents
In apps/admin/core/components/new-user-popup.tsx, update imports:

  • import Link from "next/link"import { Link } from "react-router-dom"
  • import Image from "next/image" → use a standard <img> tag or your custom Image component
  • import { useTheme as useNextTheme } from "next-themes" → plug in a React Router–compatible or framework-agnostic theme provider (e.g., Theme UI, styled-components); verify it’s available in the codebase.
🤖 Prompt for AI Agents
In apps/admin/core/components/new-user-popup.tsx around lines 4 to 6, replace
Next.js-specific imports with React-router and framework-agnostic equivalents:
remove `import Image from "next/image"` and use a standard <img> (or your
project’s Image component) in the JSX, replace `import Link from "next/link"`
with `import { Link } from "react-router-dom"`, and remove `import { useTheme as
useNextTheme } from "next-themes"` in favor of your app’s theme hook/provider
(e.g., Theme UI or styled-components) — update any usages in the component to
call the new theme hook/API and ensure the new Link and img usage match existing
props and styles.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

next-themes works in react, we'll migrate away from this incrementally.

Copy link
Contributor

Choose a reason for hiding this comment

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

@lifeiscontent, thank you for the clarification! You're absolutely right that next-themes is a React library and works outside of Next.js. I appreciate the context that you'll be migrating away from it incrementally—that approach makes sense for a smooth transition.


✏️ Learnings added
Learnt from: lifeiscontent
PR: makeplane/plane#7922
File: apps/admin/core/components/new-user-popup.tsx:4-6
Timestamp: 2025-10-09T20:43:07.746Z
Learning: The `next-themes` library is React-compatible and can be used outside of Next.js applications. It's not Next.js-specific despite its name.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

Comment on lines +18 to +27
const ADMIN_BASE_URL = process.env.NEXT_PUBLIC_ADMIN_BASE_URL || DEFAULT_BASE_URL;
let parsedBaseUrl;
try {
parsedBaseUrl = new URL(ADMIN_BASE_URL);
} catch {
parsedBaseUrl = new URL(DEFAULT_BASE_URL);
}

const PORT = Number.parseInt(parsedBaseUrl.port, 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 | 🔴 Critical

Fix port derivation when NEXT_PUBLIC_ADMIN_BASE_URL omits the port

If NEXT_PUBLIC_ADMIN_BASE_URL doesn’t include an explicit port (e.g., https://admin.example.com), parsedBaseUrl.port is an empty string, so Number.parseInt returns NaN. app.listen(NaN) fails immediately, breaking production startup despite the fallback comment above this block. Please guard for missing/invalid ports and honor process.env.PORT before defaulting.

-const PORT = Number.parseInt(parsedBaseUrl.port, 10);
+const envPort = process.env.PORT ? Number.parseInt(process.env.PORT, 10) : undefined;
+const urlPort = parsedBaseUrl.port ? Number.parseInt(parsedBaseUrl.port, 10) : undefined;
+const PORT =
+  (!Number.isNaN(envPort ?? NaN) ? envPort : undefined) ??
+  (!Number.isNaN(urlPort ?? NaN) ? urlPort : undefined) ??
+  3001;
📝 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 ADMIN_BASE_URL = process.env.NEXT_PUBLIC_ADMIN_BASE_URL || DEFAULT_BASE_URL;
let parsedBaseUrl;
try {
parsedBaseUrl = new URL(ADMIN_BASE_URL);
} catch {
parsedBaseUrl = new URL(DEFAULT_BASE_URL);
}
const PORT = Number.parseInt(parsedBaseUrl.port, 10);
const ADMIN_BASE_URL = process.env.NEXT_PUBLIC_ADMIN_BASE_URL || DEFAULT_BASE_URL;
let parsedBaseUrl;
try {
parsedBaseUrl = new URL(ADMIN_BASE_URL);
} catch {
parsedBaseUrl = new URL(DEFAULT_BASE_URL);
}
const envPort = process.env.PORT ? Number.parseInt(process.env.PORT, 10) : undefined;
const urlPort = parsedBaseUrl.port ? Number.parseInt(parsedBaseUrl.port, 10) : undefined;
const PORT =
(!Number.isNaN(envPort ?? NaN) ? envPort : undefined) ??
(!Number.isNaN(urlPort ?? NaN) ? urlPort : undefined) ??
3001;
🤖 Prompt for AI Agents
In apps/admin/server.mjs around lines 18 to 27, the code parses
parsedBaseUrl.port directly which yields an empty string when
NEXT_PUBLIC_ADMIN_BASE_URL omits the port and results in NaN from
Number.parseInt; update the logic to: prefer a valid numeric process.env.PORT if
set, otherwise use parsedBaseUrl.port only if non-empty and parseInt yields a
valid number, and finally fall back to a defined DEFAULT_PORT; ensure you use
parseInt(..., 10) and guard with Number.isFinite/!Number.isNaN checks (or isNaN)
before calling app.listen.

Comment on lines 7 to 12
"paths": {
"@/app/*": ["app/*"],
"@/*": ["core/*"],
"@/public/*": ["public/*"],
"@/plane-admin/*": ["ce/*"],
"@/styles/*": ["styles/*"]
},
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

🧩 Analysis chain

Verify the removal of the public/* path mapping.

The public/* path mapping has been removed from compilerOptions.paths. Ensure no imports in the codebase rely on this mapping (e.g., @/public/* or public/*).

Run the following script to check for uses of the removed path mapping:


🏁 Script executed:

#!/bin/bash
# Search for imports that might have used the public/* path mapping
rg -n 'from ["\']@/public/|from ["\']public/' --type ts --type tsx

Length of output: 68


🏁 Script executed:

#!/bin/bash
# Search for imports that might have used the public/* path mapping
rg -n --type ts --type tsx -e '@/public/' -e 'from "public/' -e "from 'public/" || true

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Search for imports or from statements referencing the removed public/* mapping
rg -nP '(?:import|from)\s+["\'](@/public/|public/)' -g '*.ts' -g '*.tsx' || true

Length of output: 167


🏁 Script executed:

#!/bin/bash
# Search for any references to the removed `public/*` mapping
rg -n --glob '*.ts' --glob '*.tsx' -e "@/public/" -e "public/" || true

Length of output: 16252


Restore public/* path mapping or update imports
The removal of "public/*" in apps/admin/tsconfig.json breaks dozens of asset imports (e.g. import AllFiltersImage from "@/public/empty-state/module/all-filters.svg" in apps/web/core/components/modules/modules-list-view.tsx). Revert the mapping or update all @/public/* and /public/* imports to valid paths.

🤖 Prompt for AI Agents
In apps/admin/tsconfig.json around lines 7–12 the "public/*" path mapping was
removed causing many asset imports like "@/public/..." to break; restore the
mapping by adding an entry "public/*": ["public/*"] into the "paths" block (or
alternatively update all imports to point to the correct relative/alias path
such as "@/app/public/..." or "./public/..." depending on file location). After
changing tsconfig, run a full type-check/build and grep the repo for "@/public/"
and "/public/" to verify and update any remaining imports to the chosen scheme.

"@types/react-dom": "catalog:",
"tsdown": "catalog:",
"typescript": "catalog:"
"typescript": "5.8.3"
Copy link
Member

Choose a reason for hiding this comment

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

Why did we move away from the pnpm catalog for typescript.

"@atlaskit/pragmatic-drag-and-drop-hitbox": 1.1.0
'@atlaskit/pragmatic-drag-and-drop': 1.7.4
'@atlaskit/pragmatic-drag-and-drop-auto-scroll': 1.4.0
'@atlaskit/pragmatic-drag-and-drop-hitbox': 1.1.0
Copy link
Member

Choose a reason for hiding this comment

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

Our formatter uses double quotes. Can you revert this file changes?

exports: {
customExports: (out) => ({
...out,
"./styles/fonts.css": "./dist/styles/fonts/index.css",
Copy link
Member

Choose a reason for hiding this comment

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

Any reason why this .css notion is required for the exports.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

vite has issues importing it because its ambiguous

@lifeiscontent lifeiscontent force-pushed the feat/admin-react-router-migration branch 2 times, most recently from 7b5adc0 to 466aa32 Compare October 9, 2025 21:08
cursor[bot]

This comment was marked as outdated.

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: 14

Caution

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

⚠️ Outside diff range comments (1)
apps/admin/core/components/authentication/google-config.tsx (1)

20-20: Missing React import causes ReferenceError.

Line 20 references React.FC, but the React import was removed. This will cause a runtime error.

Add the React import at the top of the file:

 "use client";
 
+import React from "react";
 import { observer } from "mobx-react";
♻️ Duplicate comments (2)
apps/admin/app/(all)/(dashboard)/email/page.tsx (1)

14-14: The React namespace issue persists.

As noted in the previous review, React.FC requires importing the React namespace. The component still uses observer<React.FC<Route.ComponentProps>> without the necessary import, causing a runtime ReferenceError: React is not defined.

Apply the fix from the previous review:

 "use client";
 
+import React from "react";
 import { useEffect, useState } from "react";
 import { observer } from "mobx-react";

Then correct the observer typing:

-const InstanceEmailPage = observer<React.FC<Route.ComponentProps>>(() => {
+const InstanceEmailPage: React.FC<Route.ComponentProps> = observer(() => {
apps/admin/app/(all)/(dashboard)/layout.tsx (1)

5-5: Wrong router import persists.

As flagged in the previous review, the file imports useRouter from "next/navigation" (Line 5) but uses it in a React Router context (Lines 19, 24-25). This causes runtime errors since the Next.js router is unavailable.

Apply the fix from the previous review:

-import { useRouter } from "next/navigation";
+import { useNavigate } from "react-router";
 import { Outlet } from "react-router";

Then update the usage:

-  const { replace } = useRouter();
+  const navigate = useNavigate();
-    if (isUserLoggedIn === false) replace("/");
+    if (isUserLoggedIn === false) navigate("/", { replace: true });
-  }, [replace, isUserLoggedIn]);
+  }, [navigate, isUserLoggedIn]);

Also applies to: 19-19, 24-25

🧹 Nitpick comments (9)
apps/admin/app/(all)/(dashboard)/workspace/create/page.tsx (2)

1-1: Remove obsolete "use client" directive.

The "use client" directive is Next.js-specific and has no effect in React Router. Remove it as part of the migration.

Apply this diff:

-"use client";
-
 import { observer } from "mobx-react";

8-8: Fix type annotation to match function signature.

The component is typed as React.FC<Route.ComponentProps> but the function signature () => (...) doesn't accept any props. This creates a type mismatch.

Choose one of these solutions:

Option 1: Accept props parameter (even if unused)

-const WorkspaceCreatePage = observer<React.FC<Route.ComponentProps>>(() => (
+const WorkspaceCreatePage = observer<React.FC<Route.ComponentProps>>((props) => (

Option 2: Remove the generic type if props aren't needed

-const WorkspaceCreatePage = observer<React.FC<Route.ComponentProps>>(() => (
+const WorkspaceCreatePage = observer(() => (

The first option is preferred if the migration pattern requires consistent typing across all route components.

apps/admin/app/(all)/instance.provider.tsx (1)

6-6: Consider conventional observer typing patterns.

The pattern observer<React.FC<React.PropsWithChildren>>((props) => ...) is non-standard. The observer HOC from mobx-react infers the component type rather than accepting an explicit generic parameter, and the props parameter is now implicitly any instead of being explicitly typed.

Consider one of these standard patterns:

Option 1: Type the component variable

-export const InstanceProvider = observer<React.FC<React.PropsWithChildren>>((props) => {
+export const InstanceProvider: React.FC<React.PropsWithChildren> = observer((props) => {

Option 2: Type the props parameter directly

-export const InstanceProvider = observer<React.FC<React.PropsWithChildren>>((props) => {
+export const InstanceProvider = observer((props: React.PropsWithChildren) => {
apps/admin/app/(all)/user.provider.tsx (1)

9-9: Consider conventional observer typing patterns.

The pattern observer<React.FC<React.PropsWithChildren>>(({ children }) => ...) is non-standard. The observer HOC from mobx-react infers the component type rather than accepting an explicit generic parameter, and without explicit parameter typing, the destructured children loses type safety.

Consider one of these standard patterns:

Option 1: Type the component variable

-export const UserProvider = observer<React.FC<React.PropsWithChildren>>(({ children }) => {
+export const UserProvider: React.FC<React.PropsWithChildren> = observer(({ children }) => {

Option 2: Type the props parameter directly

-export const UserProvider = observer<React.FC<React.PropsWithChildren>>(({ children }) => {
+export const UserProvider = observer(({ children }: React.PropsWithChildren) => {
apps/admin/core/components/new-user-popup.tsx (1)

16-16: LGTM! Removed React.FC typing.

Removing the explicit React.FC type annotation is a recommended refactor. Modern React best practices favor explicit prop typing over React.FC, which implicitly includes children and can lead to confusion.

apps/admin/app/providers.tsx (2)

1-1: Consider removing the "use client" directive.

The "use client" directive is a Next.js React Server Components directive that has no effect in React Router. Since this migration moves away from Next.js, this directive can be removed for clarity.

Apply this diff:

-"use client";
-
 import { ThemeProvider } from "next-themes";

5-8: Rename Next.js route-group (all) directory to a conventional providers folder
Move files from apps/admin/app/(all) into apps/admin/app/providers and update imports (e.g. import { InstanceProvider } from "./providers/instance.provider") to remove Next.js-specific naming.

apps/admin/app/(all)/store.provider.tsx (1)

1-1: Remove unnecessary “use client” directive
The "use client" pragma is only meaningful for Next.js Server Components and has no effect under React Router—remove it.

apps/admin/ce/components/authentication/authentication-modes.tsx (1)

13-18: Optional: simplify observer typing (line 101)
The explicit observer<React.FC<TAuthenticationModeProps>> generic isn’t needed—type inference already handles TAuthenticationModeProps. Consider using observer((props: TAuthenticationModeProps) => …) or annotating the component:

const AuthenticationModes: FC<TAuthenticationModeProps> = observer(props => );
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 959434a and 466aa32.

⛔ Files ignored due to path filters (21)
  • apps/admin/app/assets/favicon/apple-touch-icon.png is excluded by !**/*.png
  • apps/admin/app/assets/favicon/favicon-16x16.png is excluded by !**/*.png
  • apps/admin/app/assets/favicon/favicon-32x32.png is excluded by !**/*.png
  • apps/admin/app/assets/favicon/favicon.ico is excluded by !**/*.ico
  • apps/admin/app/assets/images/logo-spinner-dark.gif is excluded by !**/*.gif
  • apps/admin/app/assets/images/logo-spinner-light.gif is excluded by !**/*.gif
  • apps/admin/app/assets/images/plane-takeoff.png is excluded by !**/*.png
  • apps/admin/app/assets/instance/instance-failure-dark.svg is excluded by !**/*.svg
  • apps/admin/app/assets/instance/instance-failure.svg is excluded by !**/*.svg
  • apps/admin/app/assets/logos/github-black.png is excluded by !**/*.png
  • apps/admin/app/assets/logos/github-white.png is excluded by !**/*.png
  • apps/admin/app/assets/logos/gitlab-logo.svg is excluded by !**/*.svg
  • apps/admin/app/assets/logos/google-logo.svg is excluded by !**/*.svg
  • apps/admin/app/assets/logos/oidc-logo.svg is excluded by !**/*.svg
  • apps/admin/app/assets/logos/saml-logo.svg is excluded by !**/*.svg
  • apps/admin/app/assets/logos/takeoff-icon-dark.svg is excluded by !**/*.svg
  • apps/admin/app/assets/logos/takeoff-icon-light.svg is excluded by !**/*.svg
  • apps/admin/public/auth/background-pattern-dark.svg is excluded by !**/*.svg
  • apps/admin/public/auth/background-pattern.svg is excluded by !**/*.svg
  • apps/admin/public/instance/plane-takeoff.png is excluded by !**/*.png
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (107)
  • .dockerignore (1 hunks)
  • .gitignore (1 hunks)
  • apps/admin/.dockerignore (1 hunks)
  • apps/admin/.eslintrc.cjs (1 hunks)
  • apps/admin/.eslintrc.js (0 hunks)
  • apps/admin/Dockerfile.admin (1 hunks)
  • apps/admin/app/(all)/(dashboard)/ai/form.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/ai/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/ai/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/github/form.tsx (3 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/github/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/gitlab/form.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/gitlab/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/google/form.tsx (3 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/google/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/email/email-config-form.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/email/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/email/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/email/test-email-modal.tsx (4 hunks)
  • apps/admin/app/(all)/(dashboard)/general/form.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/general/intercom.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/general/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/general/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/header.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/image/form.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/image/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/image/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/layout.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/sidebar-help-section.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/sidebar.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/workspace/create/form.tsx (1 hunks)
  • apps/admin/app/(all)/(dashboard)/workspace/create/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/workspace/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/workspace/page.tsx (2 hunks)
  • apps/admin/app/(all)/(home)/auth-banner.tsx (1 hunks)
  • apps/admin/app/(all)/(home)/auth-helpers.tsx (2 hunks)
  • apps/admin/app/(all)/(home)/layout.tsx (1 hunks)
  • apps/admin/app/(all)/(home)/page.tsx (2 hunks)
  • apps/admin/app/(all)/(home)/sign-in-form.tsx (2 hunks)
  • apps/admin/app/(all)/instance.provider.tsx (1 hunks)
  • apps/admin/app/(all)/store.provider.tsx (2 hunks)
  • apps/admin/app/(all)/user.provider.tsx (1 hunks)
  • apps/admin/app/compat/next/image.tsx (1 hunks)
  • apps/admin/app/compat/next/link.tsx (1 hunks)
  • apps/admin/app/compat/next/navigation.ts (1 hunks)
  • apps/admin/app/layout.tsx (0 hunks)
  • apps/admin/app/providers.tsx (1 hunks)
  • apps/admin/app/root.tsx (1 hunks)
  • apps/admin/app/routes.ts (1 hunks)
  • apps/admin/app/types/next-image.d.ts (1 hunks)
  • apps/admin/app/types/next-link.d.ts (1 hunks)
  • apps/admin/app/types/next-navigation.d.ts (1 hunks)
  • apps/admin/app/types/react-router-virtual.d.ts (1 hunks)
  • apps/admin/ce/components/authentication/authentication-modes.tsx (2 hunks)
  • apps/admin/core/components/authentication/authentication-method-card.tsx (1 hunks)
  • apps/admin/core/components/authentication/email-config-switch.tsx (1 hunks)
  • apps/admin/core/components/authentication/github-config.tsx (1 hunks)
  • apps/admin/core/components/authentication/gitlab-config.tsx (1 hunks)
  • apps/admin/core/components/authentication/google-config.tsx (1 hunks)
  • apps/admin/core/components/authentication/password-config-switch.tsx (1 hunks)
  • apps/admin/core/components/common/banner.tsx (1 hunks)
  • apps/admin/core/components/common/controller-input.tsx (1 hunks)
  • apps/admin/core/components/common/empty-state.tsx (1 hunks)
  • apps/admin/core/components/common/logo-spinner.tsx (1 hunks)
  • apps/admin/core/components/instance/failure.tsx (1 hunks)
  • apps/admin/core/components/instance/instance-not-ready.tsx (1 hunks)
  • apps/admin/core/components/instance/loading.tsx (1 hunks)
  • apps/admin/core/components/instance/setup-form.tsx (2 hunks)
  • apps/admin/core/components/new-user-popup.tsx (1 hunks)
  • apps/admin/core/hooks/store/use-instance.tsx (1 hunks)
  • apps/admin/core/hooks/store/use-theme.tsx (1 hunks)
  • apps/admin/core/hooks/store/use-user.tsx (1 hunks)
  • apps/admin/core/hooks/store/use-workspace.tsx (1 hunks)
  • apps/admin/core/store/instance.store.ts (2 hunks)
  • apps/admin/core/store/root.store.ts (1 hunks)
  • apps/admin/core/store/theme.store.ts (1 hunks)
  • apps/admin/core/store/user.store.ts (1 hunks)
  • apps/admin/core/store/workspace.store.ts (1 hunks)
  • apps/admin/core/utils/public-asset.ts (1 hunks)
  • apps/admin/deploy/nginx.default.conf.template (1 hunks)
  • apps/admin/next-env.d.ts (0 hunks)
  • apps/admin/next.config.js (0 hunks)
  • apps/admin/package.json (2 hunks)
  • apps/admin/public/.well-known/appspecific/com.chrome.devtools.json (1 hunks)
  • apps/admin/public/favicon/site.webmanifest (0 hunks)
  • apps/admin/react-router.config.ts (1 hunks)
  • apps/admin/server.mjs (1 hunks)
  • apps/admin/server/app.ts (1 hunks)
  • apps/admin/styles/globals.css (1 hunks)
  • apps/admin/tsconfig.json (1 hunks)
  • apps/admin/vite.config.ts (1 hunks)
  • apps/live/package.json (1 hunks)
  • apps/web/app/(all)/layout.tsx (1 hunks)
  • apps/web/package.json (0 hunks)
  • packages/decorators/package.json (1 hunks)
  • packages/editor/package.json (1 hunks)
  • packages/editor/tsdown.config.ts (1 hunks)
  • packages/eslint-config/package.json (1 hunks)
  • packages/i18n/package.json (1 hunks)
  • packages/logger/package.json (1 hunks)
  • packages/propel/package.json (2 hunks)
  • packages/propel/tsdown.config.ts (1 hunks)
⛔ Files not processed due to max files limit (6)
  • packages/services/package.json
  • packages/shared-state/package.json
  • packages/ui/package.json
  • packages/utils/package.json
  • packages/utils/src/string.ts
  • pnpm-workspace.yaml
💤 Files with no reviewable changes (15)
  • apps/admin/.eslintrc.js
  • apps/admin/app/(all)/(dashboard)/email/layout.tsx
  • apps/admin/next-env.d.ts
  • apps/admin/app/(all)/(dashboard)/authentication/google/layout.tsx
  • apps/admin/app/(all)/(dashboard)/authentication/layout.tsx
  • apps/admin/app/layout.tsx
  • apps/admin/app/(all)/(dashboard)/authentication/gitlab/layout.tsx
  • apps/admin/app/(all)/(dashboard)/workspace/layout.tsx
  • apps/admin/app/(all)/(dashboard)/general/layout.tsx
  • apps/admin/app/(all)/(dashboard)/authentication/github/layout.tsx
  • apps/admin/next.config.js
  • apps/admin/public/favicon/site.webmanifest
  • apps/admin/app/(all)/(dashboard)/ai/layout.tsx
  • apps/web/package.json
  • apps/admin/app/(all)/(dashboard)/image/layout.tsx
✅ Files skipped from review due to trivial changes (2)
  • apps/admin/core/components/authentication/password-config-switch.tsx
  • packages/logger/package.json
🚧 Files skipped from review as they are similar to previous changes (47)
  • .gitignore
  • apps/admin/tsconfig.json
  • apps/admin/core/store/theme.store.ts
  • apps/admin/app/(all)/(dashboard)/workspace/page.tsx
  • apps/admin/app/root.tsx
  • apps/admin/core/components/authentication/email-config-switch.tsx
  • apps/admin/app/(all)/(dashboard)/header.tsx
  • apps/admin/styles/globals.css
  • apps/admin/app/compat/next/link.tsx
  • apps/admin/app/types/next-image.d.ts
  • apps/web/app/(all)/layout.tsx
  • apps/admin/app/(all)/(dashboard)/sidebar-help-section.tsx
  • apps/admin/core/components/authentication/github-config.tsx
  • apps/admin/.dockerignore
  • apps/admin/core/hooks/store/use-instance.tsx
  • apps/admin/app/compat/next/navigation.ts
  • apps/admin/app/types/next-navigation.d.ts
  • apps/admin/app/(all)/(home)/layout.tsx
  • apps/admin/app/(all)/(home)/auth-helpers.tsx
  • apps/admin/app/(all)/(dashboard)/ai/form.tsx
  • apps/admin/app/(all)/(dashboard)/general/form.tsx
  • apps/admin/app/(all)/(dashboard)/ai/page.tsx
  • apps/admin/core/store/instance.store.ts
  • apps/admin/server.mjs
  • apps/admin/core/components/authentication/gitlab-config.tsx
  • apps/admin/public/.well-known/appspecific/com.chrome.devtools.json
  • apps/admin/app/(all)/(dashboard)/email/email-config-form.tsx
  • .dockerignore
  • apps/admin/core/components/common/banner.tsx
  • apps/admin/core/components/authentication/authentication-method-card.tsx
  • apps/admin/core/components/instance/instance-not-ready.tsx
  • apps/admin/app/types/react-router-virtual.d.ts
  • apps/admin/core/store/workspace.store.ts
  • apps/admin/app/(all)/(dashboard)/general/intercom.tsx
  • packages/editor/tsdown.config.ts
  • apps/admin/core/utils/public-asset.ts
  • apps/admin/app/(all)/(home)/sign-in-form.tsx
  • packages/decorators/package.json
  • apps/admin/app/(all)/(dashboard)/workspace/create/form.tsx
  • apps/admin/app/(all)/(dashboard)/image/page.tsx
  • apps/admin/core/components/instance/loading.tsx
  • apps/admin/core/hooks/store/use-user.tsx
  • apps/admin/.eslintrc.cjs
  • apps/admin/core/components/common/logo-spinner.tsx
  • apps/admin/core/components/common/controller-input.tsx
  • packages/eslint-config/package.json
  • apps/admin/core/hooks/store/use-theme.tsx
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-10-09T20:42:31.804Z
Learnt from: lifeiscontent
PR: makeplane/plane#7922
File: apps/admin/app/(all)/(dashboard)/ai/form.tsx:19-19
Timestamp: 2025-10-09T20:42:31.804Z
Learning: In the makeplane/plane repository, React types are globally available through TypeScript configuration. Type annotations like React.FC, React.ReactNode, etc. can be used without explicitly importing the React namespace. The codebase uses the modern JSX transform, so React imports are not required for JSX or type references.

Applied to files:

  • apps/admin/app/(all)/(dashboard)/authentication/gitlab/form.tsx
  • apps/admin/app/(all)/(dashboard)/image/form.tsx
  • apps/admin/app/(all)/(dashboard)/email/test-email-modal.tsx
  • apps/admin/core/components/instance/failure.tsx
  • apps/admin/app/(all)/(dashboard)/sidebar.tsx
📚 Learning: 2025-10-09T20:43:07.746Z
Learnt from: lifeiscontent
PR: makeplane/plane#7922
File: apps/admin/core/components/new-user-popup.tsx:4-6
Timestamp: 2025-10-09T20:43:07.746Z
Learning: The `next-themes` library is React-compatible and can be used outside of Next.js applications. It's not Next.js-specific despite its name.

Applied to files:

  • apps/admin/core/components/new-user-popup.tsx
🧬 Code graph analysis (8)
apps/admin/app/(all)/(home)/auth-banner.tsx (1)
packages/constants/src/auth.ts (1)
  • TAdminAuthErrorInfo (89-94)
apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx (5)
apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx (1)
  • meta (115-115)
apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx (1)
  • meta (104-104)
apps/admin/app/(all)/(dashboard)/authentication/page.tsx (1)
  • meta (115-115)
apps/admin/app/(all)/(dashboard)/email/page.tsx (1)
  • meta (95-95)
apps/admin/app/(all)/(dashboard)/workspace/page.tsx (1)
  • meta (171-171)
apps/admin/app/(all)/(dashboard)/authentication/page.tsx (5)
apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx (1)
  • meta (115-115)
apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx (1)
  • meta (103-103)
apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx (1)
  • meta (104-104)
apps/admin/app/(all)/(dashboard)/email/page.tsx (1)
  • meta (95-95)
apps/admin/app/(all)/(dashboard)/workspace/page.tsx (1)
  • meta (171-171)
apps/admin/vite.config.ts (1)
apps/admin/server.mjs (1)
  • __dirname (8-8)
apps/admin/app/(all)/(dashboard)/email/page.tsx (5)
apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx (1)
  • meta (115-115)
apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx (1)
  • meta (103-103)
apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx (1)
  • meta (104-104)
apps/admin/app/(all)/(dashboard)/authentication/page.tsx (1)
  • meta (115-115)
apps/admin/app/(all)/(dashboard)/workspace/page.tsx (1)
  • meta (171-171)
apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx (3)
apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx (1)
  • meta (115-115)
apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx (1)
  • meta (103-103)
apps/admin/app/(all)/(dashboard)/authentication/page.tsx (1)
  • meta (115-115)
apps/admin/core/components/new-user-popup.tsx (1)
apps/admin/core/hooks/store/use-theme.tsx (1)
  • useTheme (6-10)
apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx (3)
apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx (1)
  • meta (103-103)
apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx (1)
  • meta (104-104)
apps/admin/app/(all)/(dashboard)/authentication/page.tsx (1)
  • meta (115-115)
⏰ 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). (2)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (30)
apps/admin/core/hooks/store/use-workspace.tsx (1)

4-4: LGTM! Type-only import optimization.

Converting to import type is correct since IWorkspaceStore is only used as a return type annotation. This follows TypeScript best practices, helps with tree-shaking, and aligns with the broader pattern of adopting type-only imports across admin modules.

packages/i18n/package.json (1)

27-27: Confirm CJS compatibility of lodash-es.

lodash-es only ships ESM entry points. If the tsdown build leaves dependencies external, the published dist/index.js (CJS export) will end up doing require("lodash-es"), which will throw ERR_REQUIRE_ESM at runtime. Please confirm the build fully bundles or transforms these imports for the CJS output; otherwise switch to lodash (CJS) for the commonjs build or add an alternate entry point.

apps/admin/core/store/user.store.ts (1)

3-8: LGTM! Correct separation of value and type imports.

The refactor properly distinguishes runtime values from compile-time types:

  • EUserStatus remains a value import (used at runtime in lines 81, 86)
  • TUserStatus, IUser, and CoreRootStore are now type-only imports (used solely for type annotations)

This optimization reduces the runtime bundle size by ensuring types are erased during compilation.

apps/admin/core/store/root.store.ts (1)

3-10: LGTM! Consistent type/value import pattern.

The refactor correctly separates:

  • Concrete store classes (InstanceStore, ThemeStore, UserStore, WorkspaceStore) as value imports for instantiation (lines 21-24, 36-39)
  • Interface types (IInstanceStore, IThemeStore, IUserStore, IWorkspaceStore) as type-only imports for property declarations (lines 15-18)

This pattern aligns with the broader refactoring across the admin store modules and improves TypeScript's ability to tree-shake unused code.

apps/admin/app/(all)/(dashboard)/workspace/create/page.tsx (1)

22-23: LGTM: Meta export follows React Router conventions.

The meta function export correctly provides page metadata for React Router 7.x, replacing Next.js metadata.

apps/admin/app/(all)/user.provider.tsx (1)

3-3: LGTM!

Removing unused imports (FC and ReactNode) improves code clarity.

packages/propel/tsdown.config.ts (1)

42-45: LGTM! CSS exports properly expose assets for Vite.

The explicit .css extension mappings resolve Vite's import ambiguity (as noted in the previous review discussion), while the directory-style mappings maintain backward compatibility. This pattern aligns with similar changes in other packages.

packages/propel/package.json (2)

164-167: LGTM! Package exports align with build configuration.

The CSS exports correctly mirror the tsdown.config.ts mappings and properly expose static assets without requiring separate import/require conditions.


171-175: Resolve dependency update concerns.

All @floating-ui updates are patch/minor (1.7.x → fixes and non-breaking feature) and [email protected] only adds a new API—no breaking changes detected.

apps/admin/core/components/new-user-popup.tsx (1)

11-12: LGTM! Proper Vite asset imports.

The use of Vite's ?url suffix for importing SVG assets is the correct approach for React Router with Vite. This properly migrates away from Next.js public folder conventions to Vite's asset handling system.

apps/admin/core/components/authentication/google-config.tsx (1)

9-9: LGTM: Type-only import correctly applied.

The type-only import is appropriate since TInstanceAuthenticationMethodKeys is used exclusively as a type annotation (line 17), improving tree-shaking and clarifying usage intent.

apps/admin/app/providers.tsx (1)

19-19: All import references updated for AppProviders
Script confirms no remaining default imports or InstanceLayout usages; apps/admin/app/root.tsx correctly imports and uses the named AppProviders. No further changes needed.

apps/admin/core/components/instance/failure.tsx (2)

8-9: LGTM! Asset imports updated for Vite.

The asset imports now use Vite's ?url suffix for static asset handling, which is the correct approach for this React Router + Vite migration.


3-4: Verify Next.js imports across core components
Multiple files still import next/image, next/link, and next-themes (e.g. new-user-popup.tsx, loading.tsx, logo-spinner.tsx, empty-state.tsx, failure.tsx, instance-not-ready.tsx). Confirm whether these should be replaced with native/React Router equivalents or retained under the Next compatibility shims.

apps/admin/ce/components/authentication/authentication-modes.tsx (1)

101-101: Verify non-standard observer typing pattern.

The new pattern observer<React.FC<TAuthenticationModeProps>>((props) => ...) is unconventional. Standard mobx-react patterns are:

  1. const C = observer((props: Props) => ...) — infer type from props
  2. const C: React.FC<Props> = observer((props) => ...) — type annotation on const (original pattern)

The generic on observer typically expects the props type, not the wrapped React.FC<Props> type. The arrow function (props) => ... is not structurally a React.FC, so this pattern may not type-check correctly depending on mobx-react's type definitions.

Confirm this compiles without TypeScript errors and that the component behavior is correct. If issues arise, revert to the original pattern:

-export const AuthenticationModes = observer<React.FC<TAuthenticationModeProps>>((props) => {
+export const AuthenticationModes: React.FC<TAuthenticationModeProps> = observer((props) => {
apps/admin/core/components/common/empty-state.tsx (1)

10-10: Approve typing change and compatibility shim

The image prop is correctly typed as string, and the compatibility shim at apps/admin/app/compat/next/image.tsx exports a default Image component that renders a native <img>.

apps/admin/app/types/next-link.d.ts (1)

1-12: LGTM!

The ambient module declaration properly types the Next.js Link compatibility shim. The usage of React.ComponentProps and React.FC without an explicit import is correct for this codebase, as React types are globally available through TypeScript configuration.

Based on learnings.

apps/admin/server/app.ts (1)

7-18: LGTM!

Environment normalization properly handles trailing slashes and provides sensible defaults. The trust proxy setting correctly enables X-Forwarded-* header handling when behind nginx.

apps/admin/vite.config.ts (1)

26-44: LGTM!

The conditional SSR build logic properly gates server-side bundle generation behind an explicit environment flag, allowing the same configuration to support both static deployments (default) and SSR deployments when needed.

apps/admin/package.json (2)

7-19: LGTM!

The package.json correctly configures ES module support and migrates all scripts from Next.js to the React Router + Express runtime. The new dev/build/start scripts align with the server.mjs entrypoint introduced in this PR.


30-31: Confirm adapter ≥7.4.1 and test proxy behavior

Express 5.1.0 is fully compatible with @react-router/express v7.9.3, and v7.4.1+ includes the HTTP request-smuggling fix. Since you’re on 7.9.3, run dependency-vulnerability scans and test your handlers behind proxies/load-balancers to ensure no regressions.

apps/admin/app/(all)/(home)/page.tsx (2)

11-11: LGTM!

The type import correctly brings in the Route type from the generated route types, enabling type-safe metadata exports.


43-46: LGTM!

The meta export provides appropriate SEO metadata for the admin home/setup page, following the standard React Router route metadata pattern used throughout the admin app.

apps/admin/app/(all)/(dashboard)/email/test-email-modal.tsx (2)

1-1: The import changes are correct for this codebase.

The removal of the React default import while using React.FC<Props> at line 22 is correct. In this codebase, React types are globally available through TypeScript configuration, so type annotations like React.FC can be used without explicitly importing the React namespace. The past review comment suggesting to add a React import is outdated.

Based on learnings.


64-134: LGTM!

The Fragment usage updates correctly import Fragment from React and use it directly instead of React.Fragment, improving code conciseness while maintaining the same runtime behavior.

apps/admin/app/routes.ts (1)

1-22: LGTM! Clean route configuration.

The route structure correctly maps React Router paths to page components. The explicit index and nested route calls properly mirror the file system layout, and the type safety via RouteConfig ensures compile-time route validation.

apps/admin/app/(all)/(dashboard)/authentication/github/form.tsx (1)

12-21: Good practice: type-only imports.

The separation of type-only imports from value imports improves tree-shaking and makes dependencies clearer.

apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx (1)

13-14: Correct asset import pattern for Vite.

The ?url query suffix correctly instructs Vite to resolve asset imports as URLs rather than inlining them.

apps/admin/app/(all)/(dashboard)/authentication/page.tsx (1)

115-116: Standardize authentication meta titles
Central settings page uses "Authentication Settings – Plane Web" while other auth pages end with – God Mode. Confirm this is intentional or update to – God Mode.

apps/admin/app/(all)/(dashboard)/layout.tsx (1)

17-17: Missing React import for React.FC usage.

The component signature uses React.FC<Route.ComponentProps> without importing the React namespace.

Add the React import:

 "use client";
 
+import React from "react";
 import { useEffect } from "react";
 import { observer } from "mobx-react";
⛔ Skipped due to learnings
Learnt from: lifeiscontent
PR: makeplane/plane#7922
File: apps/admin/app/(all)/(dashboard)/ai/form.tsx:19-19
Timestamp: 2025-10-09T20:42:31.804Z
Learning: In the makeplane/plane repository, React types are globally available through TypeScript configuration. Type annotations like React.FC, React.ReactNode, etc. can be used without explicitly importing the React namespace. The codebase uses the modern JSX transform, so React imports are not required for JSX or type references.

import type { Route } from "./+types/page";

const InstanceAuthenticationPage = observer(() => {
const InstanceAuthenticationPage = observer<React.FC<Route.ComponentProps>>(() => {
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

Missing React import causes runtime error.

The component uses observer<React.FC<Route.ComponentProps>> but doesn't import the React namespace, causing a ReferenceError: React is not defined at runtime.

Apply this fix:

 "use client";
 
+import React from "react";
 import { useState } from "react";
 import { observer } from "mobx-react";

Then correct the typing:

-const InstanceAuthenticationPage = observer<React.FC<Route.ComponentProps>>(() => {
+const InstanceAuthenticationPage: React.FC<Route.ComponentProps> = observer(() => {
📝 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 InstanceAuthenticationPage = observer<React.FC<Route.ComponentProps>>(() => {
"use client";
import React from "react";
import { useState } from "react";
import { observer } from "mobx-react";
const InstanceAuthenticationPage: React.FC<Route.ComponentProps> = observer(() => {
// …component implementation…
});
🤖 Prompt for AI Agents
In apps/admin/app/(all)/(dashboard)/authentication/page.tsx around line 17, the
component uses the React namespace in its typing
(observer<React.FC<Route.ComponentProps>>) but React is not imported, causing a
runtime ReferenceError; add an import for React at the top of the file (import
React from "react") and then simplify/correct the typing by using
observer<Route.ComponentProps>(...) or observer(() => ...) with an explicit
component type (e.g., const InstanceAuthenticationPage:
React.FC<Route.ComponentProps> = observer((props) => { ... })) so the React
namespace is available and the component typing is valid.

import React from "react";
import { observer } from "mobx-react";
import Image from "next/image";
import Link from "next/link";
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

Replace Next.js Link with React Router Link.

The Next.js Link component is incompatible with React Router. React Router's Link uses the to prop instead of href, and the Next.js version won't function correctly in a React Router application.

Apply this diff to replace the Next.js Link:

-import Link from "next/link";
+import { Link } from "react-router";
 import { useTheme as useNextTheme } from "next-themes";

And update the JSX usage:

           <div className="flex items-center gap-4 pt-2">
-            <Link href="/workspace/create" className={getButtonStyling("primary", "sm")}>
+            <Link to="/workspace/create" className={getButtonStyling("primary", "sm")}>
               Create workspace
             </Link>

Also applies to: 33-33

🤖 Prompt for AI Agents
In apps/admin/core/components/new-user-popup.tsx around lines 5 and 33, the file
imports and uses Next.js Link which is incompatible with React Router; replace
the import "next/link" with React Router's Link from "react-router-dom", update
all Link usages to use the "to" prop instead of "href" (and remove any
Next.js-only props like passHref), and ensure any surrounding code expects a
React Router Link (adjust types/props if necessary).

Comment on lines 33 to 35
location ~* ^$admin_base/.*$ {
try_files $uri $uri/ $admin_base/index.html =404;
}
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

Handle empty or root admin base path edge case.

The regex location ~* ^$admin_base/.*$ will fail when $admin_base is empty or set to /. In these cases, the pattern becomes ~* ^/.*$ or an invalid empty regex, which won't match as intended and could break SPA routing.

Consider adding validation or a conditional block:

# If admin_base is root, use a different location block
location / {
    try_files $uri $uri/ /index.html =404;
}

# Otherwise, use the admin_base prefix
location ~* ^$admin_base {
    try_files $uri $uri/ $admin_base/index.html =404;
}

Or validate and default $admin_base to a non-empty value like /god-mode when empty.

🤖 Prompt for AI Agents
In apps/admin/deploy/nginx.default.conf.template around lines 33-35, the regex
location using ~* ^$admin_base/.*$ breaks when $admin_base is empty or '/', so
update the template to handle the root/empty case: add a conditional branch (or
template logic) that emits a plain location / block using try_files $uri $uri/
/index.html =404 when $admin_base is empty or '/', and otherwise emit the
existing location that prefixes with $admin_base and uses try_files $uri $uri/
$admin_base/index.html =404; alternatively ensure $admin_base is
validated/defaulted to a non-empty path before rendering.

Comment on lines +1 to +4
import path from "node:path";
import { reactRouter } from "@react-router/dev/vite";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
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

Define __dirname for ES module compatibility.

The file uses __dirname at line 41, but with "type": "module" in package.json, __dirname is not available by default in ES modules. This will cause a ReferenceError at runtime.

Add the ES module equivalent at the top of the file:

 import path from "node:path";
+import { fileURLToPath } from "node:url";
 import { reactRouter } from "@react-router/dev/vite";
 import { defineConfig } from "vite";
 import tsconfigPaths from "vite-tsconfig-paths";
+
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
📝 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
import path from "node:path";
import { reactRouter } from "@react-router/dev/vite";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { reactRouter } from "@react-router/dev/vite";
import { defineConfig } from "vite";
import tsconfigPaths from "vite-tsconfig-paths";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
🤖 Prompt for AI Agents
In apps/admin/vite.config.ts around lines 1 to 4, the file later uses __dirname
but under "type":"module" __dirname is undefined; add an ES module equivalent by
importing fileURLToPath from 'node:url' and computing __filename =
fileURLToPath(import.meta.url) then __dirname = path.dirname(__filename) at the
top of the file so subsequent code can use __dirname without runtime
ReferenceError.

@lifeiscontent lifeiscontent force-pushed the feat/admin-react-router-migration branch from 466aa32 to 4ffa5ac Compare October 9, 2025 22:08
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: 13

Caution

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

⚠️ Outside diff range comments (4)
apps/admin/core/components/common/empty-state.tsx (2)

1-1: Remove the Next.js-specific directive.

The "use client" directive is specific to Next.js React Server Components and has no effect in a React Router + Vite setup. All components are client-side by default in React Router.

Apply this diff to remove the directive:

-"use client";
-
 import React from "react";

4-4: Replace Next.js Image import with a React Router-compatible solution.

The next/image import is incompatible with React Router + Vite and will cause a runtime error. Replace it with a standard <img> element or a custom image component.

Apply this diff to replace the Next.js Image import:

 import React from "react";
-import Image from "next/image";
 import { Button } from "@plane/propel/button";

Then update the usage at line 30:

-      {image && <Image src={image} className="w-52 sm:w-60" alt={primaryButton?.text || "button image"} />}
+      {image && <img src={image} className="w-52 sm:w-60" alt={primaryButton?.text || "button image"} />}
apps/admin/core/components/instance/instance-not-ready.tsx (1)

14-14: Update Next.js component usage to React Router equivalents.

The component still uses Next.js-specific components that must be replaced:

  1. Line 14: Replace <Image> with standard <img> tag
  2. Line 21: Replace Next.js Link with React Router Link and change href to to

Apply this diff to update the component usage:

-        <Image src={PlaneTakeOffImage} alt="Plane Logo" />
+        <img src={PlaneTakeOffImage} alt="Plane Logo" />
-        <Link href={"/setup/?auth_enabled=0"}>
+        <Link to="/setup/?auth_enabled=0">
           <Button size="lg" className="w-full">
             Get started
           </Button>

Also applies to: 21-21

apps/admin/app/(all)/(dashboard)/image/page.tsx (1)

3-42: Import React types and annotate component instead of generic

React.FC is used without importing React types, and observer<React.FC<...>> supplies the wrong generic argument. Mirror the fix from other pages by importing FC and typing the constant.

-import { observer } from "mobx-react";
+import { observer } from "mobx-react";
+import type { FC } from "react";-const InstanceImagePage = observer<React.FC<Route.ComponentProps>>(() => {
+const InstanceImagePage: FC<Route.ComponentProps> = observer(() => {
♻️ Duplicate comments (8)
apps/admin/app/(all)/(dashboard)/email/test-email-modal.tsx (1)

22-22: The React.FC usage is correct—React types are globally available.

The past review comment suggesting to add a React import is incorrect. As per the codebase's TypeScript configuration, React types (React.FC, React.ReactNode, etc.) are globally available and can be used without explicitly importing the React namespace. The modern JSX transform also eliminates the need for React to be in scope.

Based on learnings.

apps/admin/app/(all)/(dashboard)/image/form.tsx (1)

5-5: LGTM! Type-only import and React.FC usage are correct.

The conversion to type-only imports (line 5) and the React.FC annotation (line 17) are both appropriate. Per project learnings, React types are globally available, so the React.FC usage requires no explicit namespace import.

Also applies to: 17-17

apps/admin/app/(all)/(dashboard)/authentication/github/form.tsx (1)

3-3: Consistent refactor across authentication forms.

The typing updates here mirror those in the Google and GitLab authentication forms, maintaining consistency. The separation of type-only imports (lines 12, 19, 21) from value imports (lines 18, 20) is clean and intentional.

Also applies to: 12-12, 18-21, 31-31

apps/admin/app/(all)/(dashboard)/ai/form.tsx (1)

6-6: Correct application of type-only imports and React.FC pattern.

Based on learnings, the React.FC usage at line 19 is valid without an explicit React namespace import. The type-only imports (lines 6, 8) properly separate types from runtime values, following the established project pattern.

Also applies to: 8-9, 19-19

apps/admin/vite.config.ts (1)

1-56: Define __dirname in this ESM module.

With "type": "module", __dirname is undefined and this config will crash as soon as Vite evaluates the file. Re-introduce it via fileURLToPath.

-import path from "node:path";
+import path from "node:path";
+import { fileURLToPath } from "node:url";
 import { reactRouter } from "@react-router/dev/vite";
 import { defineConfig } from "vite";
 import tsconfigPaths from "vite-tsconfig-paths";
 
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+
apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx (1)

3-24: Fix missing React types and incorrect observer generic

React.FC is referenced without importing the React namespace, so TypeScript can’t resolve React. Also, observer<React.FC<...>> is the wrong generic—it should receive the props shape, not the component type. Drop the generic and annotate the variable instead.

-import { useState } from "react";
+import { useState, type FC } from "react";-const InstanceGithubAuthenticationPage = observer<React.FC<Route.ComponentProps>>(() => {
+const InstanceGithubAuthenticationPage: FC<Route.ComponentProps> = observer(() => {
apps/admin/package.json (1)

30-32: Unavailable @react-router/* versions will break installs

@react-router/express and @react-router/node are pinned to ^7.9.3, but that version is not published on npm—pnpm install/yarn install will fail. Align them with the latest available release (e.g. ^7.8.2 or the actual published tag matching [email protected]).
Based on learnings

-    "@react-router/express": "^7.9.3",
-    "@react-router/node": "^7.9.3",
+    "@react-router/express": "^7.8.2",
+    "@react-router/node": "^7.8.2",
apps/admin/app/(all)/(dashboard)/layout.tsx (1)

5-25: Replace Next.js router with React Router’s navigation hook.

This file still imports useRouter from next/navigation, which no longer exists in the Vite/React Router runtime. The hook will throw at load time, breaking dashboard rendering. Switch to useNavigate (or the appropriate React Router API) and update the effect accordingly.

-import { useRouter } from "next/navigation";
-import { Outlet } from "react-router";
+import { Outlet, useNavigate } from "react-router";-  const { replace } = useRouter();
+  const navigate = useNavigate();-    if (isUserLoggedIn === false) replace("/");
-  }, [replace, isUserLoggedIn]);
+    if (isUserLoggedIn === false) navigate("/", { replace: true });
+  }, [navigate, isUserLoggedIn]);
🧹 Nitpick comments (4)
.gitignore (1)

106-107: Avoid duplicate ignore entries.

build/ and .react-router/ are already ignored at Lines 21-22. Duplicating them at the end just adds noise—please drop the extras.

apps/admin/core/components/common/empty-state.tsx (1)

12-12: Consider improving the icon type for consistency.

The icon property is typed as any, which is inconsistent with the type improvement made to the image prop. Consider using a more specific type such as React.ComponentType or React.ReactNode based on what the Button component's prependIcon prop accepts.

Example refactor:

   primaryButton?: {
-    icon?: any;
+    icon?: React.ComponentType<{ className?: string }>;
     text: string;
     onClick: () => void;
   };

Note: Adjust the type based on the actual prependIcon prop type definition in the Button component.

apps/admin/app/types/next-link.d.ts (1)

1-12: Approve compatibility shim; document ignored Next.js props
Shim destructures and ignores prefetch, scroll, and shallow as no-ops per the type definition, and no usages of as, passHref, locale, or legacyBehavior were found. Consider adding a brief comment or docs clarifying these Next.js-specific props are intentionally unsupported.

apps/admin/core/components/instance/setup-form.tsx (1)

56-57: Remove unnecessary props parameter and empty destructure.

The component doesn't use any props, so the props parameter and empty destructure on line 57 serve no purpose.

Apply this diff:

-export const InstanceSetupForm: React.FC = (props) => {
-  const {} = props;
+export const InstanceSetupForm: React.FC = () => {
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 466aa32 and 4ffa5ac.

⛔ Files ignored due to path filters (21)
  • apps/admin/app/assets/favicon/apple-touch-icon.png is excluded by !**/*.png
  • apps/admin/app/assets/favicon/favicon-16x16.png is excluded by !**/*.png
  • apps/admin/app/assets/favicon/favicon-32x32.png is excluded by !**/*.png
  • apps/admin/app/assets/favicon/favicon.ico is excluded by !**/*.ico
  • apps/admin/app/assets/images/logo-spinner-dark.gif is excluded by !**/*.gif
  • apps/admin/app/assets/images/logo-spinner-light.gif is excluded by !**/*.gif
  • apps/admin/app/assets/images/plane-takeoff.png is excluded by !**/*.png
  • apps/admin/app/assets/instance/instance-failure-dark.svg is excluded by !**/*.svg
  • apps/admin/app/assets/instance/instance-failure.svg is excluded by !**/*.svg
  • apps/admin/app/assets/logos/github-black.png is excluded by !**/*.png
  • apps/admin/app/assets/logos/github-white.png is excluded by !**/*.png
  • apps/admin/app/assets/logos/gitlab-logo.svg is excluded by !**/*.svg
  • apps/admin/app/assets/logos/google-logo.svg is excluded by !**/*.svg
  • apps/admin/app/assets/logos/oidc-logo.svg is excluded by !**/*.svg
  • apps/admin/app/assets/logos/saml-logo.svg is excluded by !**/*.svg
  • apps/admin/app/assets/logos/takeoff-icon-dark.svg is excluded by !**/*.svg
  • apps/admin/app/assets/logos/takeoff-icon-light.svg is excluded by !**/*.svg
  • apps/admin/public/auth/background-pattern-dark.svg is excluded by !**/*.svg
  • apps/admin/public/auth/background-pattern.svg is excluded by !**/*.svg
  • apps/admin/public/instance/plane-takeoff.png is excluded by !**/*.png
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (107)
  • .dockerignore (1 hunks)
  • .gitignore (1 hunks)
  • apps/admin/.dockerignore (1 hunks)
  • apps/admin/.eslintrc.cjs (1 hunks)
  • apps/admin/.eslintrc.js (0 hunks)
  • apps/admin/Dockerfile.admin (1 hunks)
  • apps/admin/app/(all)/(dashboard)/ai/form.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/ai/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/ai/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/github/form.tsx (3 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/github/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/gitlab/form.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/gitlab/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/google/form.tsx (3 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/google/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/email/email-config-form.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/email/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/email/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/email/test-email-modal.tsx (4 hunks)
  • apps/admin/app/(all)/(dashboard)/general/form.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/general/intercom.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/general/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/general/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/header.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/image/form.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/image/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/image/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/layout.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/sidebar-help-section.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/sidebar.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/workspace/create/form.tsx (1 hunks)
  • apps/admin/app/(all)/(dashboard)/workspace/create/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/workspace/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/workspace/page.tsx (2 hunks)
  • apps/admin/app/(all)/(home)/auth-banner.tsx (1 hunks)
  • apps/admin/app/(all)/(home)/auth-helpers.tsx (2 hunks)
  • apps/admin/app/(all)/(home)/layout.tsx (1 hunks)
  • apps/admin/app/(all)/(home)/page.tsx (2 hunks)
  • apps/admin/app/(all)/(home)/sign-in-form.tsx (2 hunks)
  • apps/admin/app/(all)/instance.provider.tsx (1 hunks)
  • apps/admin/app/(all)/store.provider.tsx (2 hunks)
  • apps/admin/app/(all)/user.provider.tsx (1 hunks)
  • apps/admin/app/compat/next/image.tsx (1 hunks)
  • apps/admin/app/compat/next/link.tsx (1 hunks)
  • apps/admin/app/compat/next/navigation.ts (1 hunks)
  • apps/admin/app/layout.tsx (0 hunks)
  • apps/admin/app/providers.tsx (1 hunks)
  • apps/admin/app/root.tsx (1 hunks)
  • apps/admin/app/routes.ts (1 hunks)
  • apps/admin/app/types/next-image.d.ts (1 hunks)
  • apps/admin/app/types/next-link.d.ts (1 hunks)
  • apps/admin/app/types/next-navigation.d.ts (1 hunks)
  • apps/admin/app/types/react-router-virtual.d.ts (1 hunks)
  • apps/admin/ce/components/authentication/authentication-modes.tsx (2 hunks)
  • apps/admin/core/components/authentication/authentication-method-card.tsx (1 hunks)
  • apps/admin/core/components/authentication/email-config-switch.tsx (1 hunks)
  • apps/admin/core/components/authentication/github-config.tsx (1 hunks)
  • apps/admin/core/components/authentication/gitlab-config.tsx (1 hunks)
  • apps/admin/core/components/authentication/google-config.tsx (1 hunks)
  • apps/admin/core/components/authentication/password-config-switch.tsx (1 hunks)
  • apps/admin/core/components/common/banner.tsx (1 hunks)
  • apps/admin/core/components/common/controller-input.tsx (1 hunks)
  • apps/admin/core/components/common/empty-state.tsx (1 hunks)
  • apps/admin/core/components/common/logo-spinner.tsx (1 hunks)
  • apps/admin/core/components/instance/failure.tsx (1 hunks)
  • apps/admin/core/components/instance/instance-not-ready.tsx (1 hunks)
  • apps/admin/core/components/instance/loading.tsx (1 hunks)
  • apps/admin/core/components/instance/setup-form.tsx (2 hunks)
  • apps/admin/core/components/new-user-popup.tsx (1 hunks)
  • apps/admin/core/hooks/store/use-instance.tsx (1 hunks)
  • apps/admin/core/hooks/store/use-theme.tsx (1 hunks)
  • apps/admin/core/hooks/store/use-user.tsx (1 hunks)
  • apps/admin/core/hooks/store/use-workspace.tsx (1 hunks)
  • apps/admin/core/store/instance.store.ts (2 hunks)
  • apps/admin/core/store/root.store.ts (1 hunks)
  • apps/admin/core/store/theme.store.ts (1 hunks)
  • apps/admin/core/store/user.store.ts (1 hunks)
  • apps/admin/core/store/workspace.store.ts (1 hunks)
  • apps/admin/core/utils/public-asset.ts (1 hunks)
  • apps/admin/middleware.js (1 hunks)
  • apps/admin/next-env.d.ts (0 hunks)
  • apps/admin/next.config.js (0 hunks)
  • apps/admin/nginx/nginx.conf (1 hunks)
  • apps/admin/package.json (2 hunks)
  • apps/admin/public/.well-known/appspecific/com.chrome.devtools.json (1 hunks)
  • apps/admin/public/favicon/site.webmanifest (0 hunks)
  • apps/admin/react-router.config.ts (1 hunks)
  • apps/admin/server.mjs (1 hunks)
  • apps/admin/server/app.ts (1 hunks)
  • apps/admin/styles/globals.css (1 hunks)
  • apps/admin/tsconfig.json (1 hunks)
  • apps/admin/vite.config.ts (1 hunks)
  • apps/live/package.json (1 hunks)
  • apps/web/app/(all)/layout.tsx (1 hunks)
  • apps/web/package.json (0 hunks)
  • packages/decorators/package.json (1 hunks)
  • packages/editor/package.json (1 hunks)
  • packages/editor/tsdown.config.ts (1 hunks)
  • packages/eslint-config/package.json (1 hunks)
  • packages/i18n/package.json (1 hunks)
  • packages/logger/package.json (1 hunks)
  • packages/propel/package.json (2 hunks)
⛔ Files not processed due to max files limit (7)
  • packages/propel/tsdown.config.ts
  • packages/services/package.json
  • packages/shared-state/package.json
  • packages/ui/package.json
  • packages/utils/package.json
  • packages/utils/src/string.ts
  • pnpm-workspace.yaml
💤 Files with no reviewable changes (15)
  • apps/web/package.json
  • apps/admin/app/(all)/(dashboard)/authentication/gitlab/layout.tsx
  • apps/admin/.eslintrc.js
  • apps/admin/next.config.js
  • apps/admin/app/(all)/(dashboard)/image/layout.tsx
  • apps/admin/app/(all)/(dashboard)/workspace/layout.tsx
  • apps/admin/app/(all)/(dashboard)/email/layout.tsx
  • apps/admin/app/layout.tsx
  • apps/admin/app/(all)/(dashboard)/authentication/github/layout.tsx
  • apps/admin/app/(all)/(dashboard)/ai/layout.tsx
  • apps/admin/app/(all)/(dashboard)/authentication/layout.tsx
  • apps/admin/app/(all)/(dashboard)/authentication/google/layout.tsx
  • apps/admin/app/(all)/(dashboard)/general/layout.tsx
  • apps/admin/public/favicon/site.webmanifest
  • apps/admin/next-env.d.ts
✅ Files skipped from review due to trivial changes (2)
  • apps/admin/public/.well-known/appspecific/com.chrome.devtools.json
  • apps/admin/core/hooks/store/use-instance.tsx
🚧 Files skipped from review as they are similar to previous changes (41)
  • apps/admin/core/components/authentication/authentication-method-card.tsx
  • apps/admin/app/(all)/(dashboard)/authentication/page.tsx
  • apps/admin/.eslintrc.cjs
  • apps/admin/app/(all)/(dashboard)/workspace/create/form.tsx
  • apps/admin/app/(all)/(dashboard)/general/page.tsx
  • apps/live/package.json
  • apps/admin/app/types/next-navigation.d.ts
  • apps/admin/app/(all)/store.provider.tsx
  • apps/admin/app/(all)/(home)/auth-banner.tsx
  • apps/admin/app/(all)/(dashboard)/header.tsx
  • apps/admin/app/(all)/(dashboard)/general/form.tsx
  • apps/admin/app/(all)/(dashboard)/email/page.tsx
  • apps/admin/app/(all)/(home)/page.tsx
  • apps/admin/app/(all)/(dashboard)/email/email-config-form.tsx
  • apps/admin/core/components/authentication/github-config.tsx
  • apps/admin/core/store/theme.store.ts
  • apps/admin/app/(all)/(dashboard)/ai/page.tsx
  • apps/admin/core/store/workspace.store.ts
  • apps/admin/app/(all)/(dashboard)/workspace/create/page.tsx
  • apps/admin/app/(all)/(home)/auth-helpers.tsx
  • apps/admin/.dockerignore
  • apps/admin/app/compat/next/navigation.ts
  • apps/admin/core/components/authentication/gitlab-config.tsx
  • apps/admin/core/hooks/store/use-theme.tsx
  • apps/admin/core/store/instance.store.ts
  • apps/admin/styles/globals.css
  • apps/admin/app/(all)/(dashboard)/general/intercom.tsx
  • apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx
  • apps/admin/react-router.config.ts
  • apps/admin/server.mjs
  • apps/admin/app/providers.tsx
  • apps/admin/app/types/next-image.d.ts
  • apps/admin/core/utils/public-asset.ts
  • apps/admin/core/components/authentication/password-config-switch.tsx
  • apps/admin/app/compat/next/image.tsx
  • apps/admin/core/components/common/logo-spinner.tsx
  • apps/admin/core/components/authentication/google-config.tsx
  • apps/admin/core/components/common/controller-input.tsx
  • apps/admin/app/(all)/(dashboard)/workspace/page.tsx
  • apps/admin/app/compat/next/link.tsx
  • apps/web/app/(all)/layout.tsx
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-09-02T08:14:49.260Z
Learnt from: sriramveeraghanta
PR: makeplane/plane#7697
File: apps/web/app/(all)/[workspaceSlug]/(projects)/header.tsx:12-13
Timestamp: 2025-09-02T08:14:49.260Z
Learning: The star-us-link.tsx file in apps/web/app/(all)/[workspaceSlug]/(projects)/ already has "use client" directive at the top, making it a proper Client Component for hook usage.

Applied to files:

  • apps/admin/app/(all)/(dashboard)/sidebar-help-section.tsx
📚 Learning: 2025-10-09T20:43:07.746Z
Learnt from: lifeiscontent
PR: makeplane/plane#7922
File: apps/admin/core/components/new-user-popup.tsx:4-6
Timestamp: 2025-10-09T20:43:07.746Z
Learning: The `next-themes` library is React-compatible and can be used outside of Next.js applications. It's not Next.js-specific despite its name.

Applied to files:

  • apps/admin/core/components/new-user-popup.tsx
📚 Learning: 2025-10-09T20:42:31.804Z
Learnt from: lifeiscontent
PR: makeplane/plane#7922
File: apps/admin/app/(all)/(dashboard)/ai/form.tsx:19-19
Timestamp: 2025-10-09T20:42:31.804Z
Learning: In the makeplane/plane repository, React types are globally available through TypeScript configuration. Type annotations like React.FC, React.ReactNode, etc. can be used without explicitly importing the React namespace. The codebase uses the modern JSX transform, so React imports are not required for JSX or type references.

Applied to files:

  • apps/admin/app/(all)/(dashboard)/ai/form.tsx
  • apps/admin/app/(all)/(home)/sign-in-form.tsx
  • apps/admin/app/(all)/(dashboard)/image/form.tsx
  • apps/admin/app/(all)/(dashboard)/authentication/gitlab/form.tsx
  • apps/admin/app/(all)/(dashboard)/authentication/github/form.tsx
  • apps/admin/core/components/instance/failure.tsx
  • apps/admin/app/(all)/(dashboard)/sidebar.tsx
  • apps/admin/core/components/common/banner.tsx
  • apps/admin/app/(all)/(dashboard)/email/test-email-modal.tsx
🧬 Code graph analysis (5)
apps/admin/core/components/new-user-popup.tsx (1)
apps/admin/core/hooks/store/use-theme.tsx (1)
  • useTheme (6-10)
apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx (3)
apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx (1)
  • meta (103-103)
apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx (1)
  • meta (104-104)
apps/admin/app/(all)/(dashboard)/authentication/page.tsx (1)
  • meta (115-115)
apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx (3)
apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx (1)
  • meta (115-115)
apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx (1)
  • meta (104-104)
apps/admin/app/(all)/(dashboard)/authentication/page.tsx (1)
  • meta (115-115)
apps/admin/vite.config.ts (1)
apps/admin/server.mjs (1)
  • __dirname (8-8)
apps/admin/app/root.tsx (1)
apps/admin/app/providers.tsx (1)
  • AppProviders (19-32)
⏰ 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). (1)
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (33)
apps/admin/core/hooks/store/use-workspace.tsx (1)

4-4: LGTM! Proper use of type-only import.

The change to import type is correct since IWorkspaceStore is only used for type annotation. This improves build optimization by ensuring the type is erased at compile time.

apps/admin/core/components/common/banner.tsx (1)

1-1: LGTM! Type-only import for FC.

Converting the FC import to a type-only import is a beneficial refactor. Since FC is exclusively used for type annotation (line 9) and React types are globally available in this codebase via TypeScript configuration, this change improves tree-shaking and makes the type-only usage explicit.

Based on learnings.

packages/editor/package.json (1)

21-22: LGTM! Dual export paths for CSS styles.

The addition of "./styles.css" as an alias to the existing "./styles" export provides flexibility for consumers to import using either path. Both resolve to the same CSS file, and the change is consistent with the corresponding update in tsdown.config.ts.

packages/editor/tsdown.config.ts (1)

11-11: LGTM! Consistent export mapping added.

The new "./styles.css" export mapping aligns with the corresponding change in package.json, ensuring that both the package exports and the build configuration recognize the dual import paths for the CSS styles.

packages/propel/package.json (1)

164-167: CSS export additions LGTM

Thanks for adding the .css aliases—this lines up with the new tsdown mappings so downstream apps can import the compiled styles without guessing extensions.

apps/admin/app/(all)/(dashboard)/email/test-email-modal.tsx (2)

1-1: LGTM! Import changes align with modern React patterns.

The addition of Fragment to the import statement is correct and necessary for the direct Fragment usage throughout the component. The modern JSX transform eliminates the need for a default React import.


64-80: LGTM! Fragment usage is consistent and correct.

All instances of as={Fragment} are correct and consistent with the Fragment import added at line 1. This improves readability by using the direct imported reference instead of the namespace qualifier.

apps/admin/core/store/user.store.ts (1)

3-8: LGTM! Correct type-only import separation.

The refactor correctly separates type-only imports (TUserStatus, IUser, CoreRootStore) from the value import (EUserStatus). This follows TypeScript best practices and improves bundle size by ensuring types are erased at compile time.

apps/admin/core/store/root.store.ts (1)

3-10: LGTM! Proper type-only import separation.

The refactor correctly separates concrete store classes (value imports) from their interfaces (type-only imports). This pattern is consistently applied across all four store types and follows TypeScript best practices.

apps/admin/core/hooks/store/use-user.tsx (1)

4-4: LGTM! Correct type-only import.

The change to import type is appropriate since IUserStore is only used as a return type annotation.

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

3-3: LGTM! The removal of FC type annotation is consistent with the migration.

The removal of the FC type annotation and simplification to a plain function wrapped by observer aligns with the broader refactor across the admin UI components. The runtime behavior remains unchanged, and the component logic is correct.

Based on learnings: React types are globally available in this codebase through TypeScript configuration, so the FC type could still be used without importing if needed, but removing it is a valid stylistic choice.

Also applies to: 14-14

apps/admin/core/components/new-user-popup.tsx (1)

1-1: The "use client" directive is still present despite being marked as addressed.

This line was flagged in a previous review and marked as "✅ Addressed in commits f2539c5 to 466aa32," but the directive still appears in the current code. Please verify whether the fix was applied or if this file needs to be updated to remove the Next.js-specific directive as part of the React Router migration.

apps/admin/core/components/common/empty-state.tsx (1)

10-10: LGTM! Type improvement enhances type safety.

Changing the image prop type from any to string improves type safety and accurately reflects how the prop is used (as an image URL).

packages/eslint-config/package.json (1)

12-20: Verify Node/TypeScript baselines for @typescript-eslint v8

  • Node engine ≥22.18.0 satisfies the ≥18.18 requirement.
  • TypeScript is referenced via “catalog:” in package manifests—ensure the resolved TS version is ≥5.5.
packages/i18n/package.json (2)

35-38: Thanks for moving @types/node; double-check catalog entry.

Relocating @types/node into devDependencies matches our pattern. Just ensure the catalog actually defines this reference so installs don’t fail.


27-30: Confirm CJS build doesn’t require the ESM-only lodash-es.
Run npm run build and inspect dist/index.js for any bare require("lodash-es"). If it’s still present (causing ERR_REQUIRE_ESM), either bundle lodash-es into the CJS output or switch the CJS export to use lodash instead.

apps/admin/core/components/authentication/email-config-switch.tsx (1)

6-6: LGTM! Type-only import is correct.

Converting to import type is appropriate here since TInstanceAuthenticationMethodKeys is only used as a type annotation on line 14, not at runtime. This follows TypeScript best practices and aligns with the broader pattern of type-only imports across the admin app migration.

apps/admin/core/components/instance/loading.tsx (2)

4-5: LGTM: Vite asset imports are correctly configured.

The ?url suffix is the correct Vite syntax for importing assets as URL strings. This ensures the imported values are public URL strings that can be used directly as image sources.

However, ensure the Image component issue (Line 1) is resolved for the migration to work correctly.


1-1: Next/Image compatibility shim verified
A shim exists in apps/admin/app/compat/next/image.tsx (with types in apps/admin/app/types/next-image.d.ts), so the next/image import is safe to keep.

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

3-3: LGTM! Typing update aligns with project-wide refactor.

The removal of the FC import and switch to React.FC annotation is consistent with the broader typing standardization across the admin app. Since React types are globally available in this codebase, the React.FC usage is correct without an explicit namespace import.

Also applies to: 37-37

apps/admin/app/(all)/(dashboard)/authentication/google/form.tsx (1)

2-2: Excellent typing and import hygiene.

The refactor to type-only imports for configuration types and form field types (lines 11, 17, 19) follows TypeScript best practices by enabling better tree-shaking and making the distinction between types and runtime values explicit. The component signature update to React.FC<Props> is consistent with the project-wide pattern.

Also applies to: 11-11, 16-19, 29-29

apps/admin/app/(all)/(dashboard)/authentication/gitlab/form.tsx (1)

1-1: Authentication form typing refactor complete.

The GitLab form now matches the typing structure of the GitHub and Google authentication forms. The type-only imports (lines 9, 15, 17) and React.FC signature (line 27) complete the consistent pattern across all authentication configuration forms.

Also applies to: 9-9, 14-17, 27-27

packages/logger/package.json (1)

31-31: @types/[email protected] confirmed published Version 4.17.23 exists on npm and can be merged as-is.

apps/admin/core/components/instance/instance-not-ready.tsx (2)

7-7: LGTM! Correct Vite asset import syntax.

The ?url suffix is the correct Vite syntax for importing static asset URLs. This change properly aligns with the migration to Vite.


9-9: React.FC usage is valid without explicit import
Modern JSX transform and global React types (via @types/react) make React.FC and JSX available without importing React.

Likely an incorrect or invalid review comment.

apps/admin/core/components/instance/failure.tsx (3)

8-9: LGTM! Asset imports correctly updated for Vite.

The migration from public SVG paths to app/assets with the ?url query parameter is the correct approach for Vite's static asset handling. This ensures the SVG files are imported as URL strings compatible with Vite's build system.

Based on learnings


11-11: The past review comment is incorrect.

The previous comment suggesting to import React is not applicable to this codebase. React types (including React.FC) are globally available through TypeScript configuration, and the modern JSX transform means React imports are not required for type references.

Based on learnings


1-4: Confirm or replace Next.js-specific imports
This component still imports next/image and next-themes, but I didn’t find a clear shim or alias for them. Confirm that @bprogress/next (or another layer) provides replacements and that import paths have been updated accordingly. If no shim exists, replace with framework-agnostic alternatives (e.g., <img> tags, a generic theme provider).

packages/decorators/package.json (1)

27-27: Confirmed @types/[email protected] is published. Installation will succeed; no changes needed.

apps/admin/app/(all)/(home)/sign-in-form.tsx (1)

7-8: LGTM! Type-only import separation is a best practice.

Splitting type imports from value imports improves tree-shaking and helps prevent circular dependency issues in TypeScript.

apps/admin/ce/components/authentication/authentication-modes.tsx (3)

6-10: LGTM!

Using import type for type-only imports is a TypeScript best practice. This ensures types are stripped at build time and don't affect the runtime bundle.


2-2: next/image import is correctly shimmed—no action required
The Vite config aliases "next/image" to app/compat/next/image.tsx with corresponding types in apps/admin/app/types/next-image.d.ts, so no replacement is needed.

Likely an incorrect or invalid review comment.


13-18: Approve Vite asset imports
Verified all six logos exist in apps/admin/app/assets/logos and the ?url imports are correct.

@@ -1,6 +1,6 @@
"use client";
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

Remove the Next.js "use client" directive.

This directive is specific to Next.js and has no effect in React Router. Since this PR migrates from Next.js to React Router, the directive should be removed.

Apply this diff to remove the directive:

-"use client";
-
 import { useEffect, useRef } from "react";
📝 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
"use client";
import { useEffect, useRef } from "react";
🤖 Prompt for AI Agents
In apps/admin/app/(all)/(dashboard)/sidebar.tsx around line 1, remove the
Next.js-specific "use client" directive at the top of the file; this directive
has no effect under React Router—delete that line so the file no longer contains
the "use client" pragma and ensure there are no leftover blank lines at the top.

Comment on lines 1 to 5
"use client";

import { FC, useEffect, useMemo, useState } from "react";
import { useEffect, useMemo, useState } from "react";
import { useSearchParams } from "next/navigation";
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

🧩 Analysis chain

Verify Next.js dependencies in React Router migration.

The file retains Next.js-specific code:

  • Line 1: "use client" directive (Next.js App Router feature)
  • Line 4: useSearchParams imported from next/navigation

In a React Router app, useSearchParams should be imported from react-router-dom. Confirm whether Next.js dependencies will remain available or if these imports should be updated to React Router equivalents.

Run this script to check if other migrated files still reference next/navigation:


🏁 Script executed:

#!/bin/bash
# Description: Check for remaining next/navigation imports in admin app files

# Search for next/navigation imports in the admin app directory
rg --type=typescript --type=tsx -n "from ['\"]next/navigation['\"]" apps/admin/

# Also check for "use client" directives
rg --type=typescript --type=tsx -n "^['\"]use client['\"]" apps/admin/

Length of output: 69


🏁 Script executed:

#!/bin/bash
# Search for remaining Next.js-specific imports and directives in admin app
rg -n "from ['\"]next/navigation['\"]" apps/admin/ -g '*.ts' -g '*.tsx'
rg -n '^"use client"' apps/admin/ -g '*.ts' -g '*.tsx'

Length of output: 4126


Replace Next.js-specific code in the admin app with React Router equivalents.

  • Remove all "use client" directives unless required by your bundler; Vite/React Router doesn’t use this.
  • Replace imports from next/navigation (useSearchParams, useRouter, usePathname) with React Router hooks from react-router-dom (e.g. useSearchParams, useNavigate, useLocation, useParams).
  • Audit apps/admin/ for any remaining next/navigation references and update them accordingly.

Comment on lines 1 to 6
import { observer } from "mobx-react";
import useSWR from "swr";
// hooks
import { useInstance } from "@/hooks/store";

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

Missing React import for React.FC and React.PropsWithChildren.

The code uses React.FC and React.PropsWithChildren on line 6 but does not import React. This will cause a ReferenceError at runtime.

Add the React import:

+import React from "react";
 import { observer } from "mobx-react";
 import useSWR from "swr";
 // hooks
 import { useInstance } from "@/hooks/store";
📝 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
import { observer } from "mobx-react";
import useSWR from "swr";
// hooks
import { useInstance } from "@/hooks/store";
import React from "react";
import { observer } from "mobx-react";
import useSWR from "swr";
// hooks
import { useInstance } from "@/hooks/store";
🤖 Prompt for AI Agents
In apps/admin/app/(all)/instance.provider.tsx around lines 1 to 5, the file uses
React.FC and React.PropsWithChildren but does not import React, causing a
runtime ReferenceError; add an import for React at the top of the file (e.g.,
import React from "react" or import * as React from "react") so those
type/namespace references resolve, keeping existing imports intact.

};

export const InstanceProvider: FC<InstanceProviderProps> = observer((props) => {
export const InstanceProvider = observer<React.FC<React.PropsWithChildren>>((props) => {
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

Incorrect observer generic usage with React.FC.

The pattern observer<React.FC<React.PropsWithChildren>> is incorrect. The observer function's generic parameter expects a props type, not a component type like React.FC<Props>. This may cause type errors or unexpected behavior.

Use one of these correct patterns:

Option 1 (Recommended): Type annotation on the left side:

-export const InstanceProvider = observer<React.FC<React.PropsWithChildren>>((props) => {
+export const InstanceProvider: React.FC<React.PropsWithChildren> = observer((props) => {

Option 2: Pass props type to observer and annotate the parameter:

-export const InstanceProvider = observer<React.FC<React.PropsWithChildren>>((props) => {
+export const InstanceProvider = observer<React.PropsWithChildren>((props: React.PropsWithChildren) => {
📝 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
export const InstanceProvider = observer<React.FC<React.PropsWithChildren>>((props) => {
export const InstanceProvider: React.FC<React.PropsWithChildren> = observer((props) => {
🤖 Prompt for AI Agents
In apps/admin/app/(all)/instance.provider.tsx around line 6, the current use of
observer with a component type generic
(observer<React.FC<React.PropsWithChildren>>) is incorrect because observer
expects a props type, not a component type; remove the generic from observer and
either (a) annotate the component variable with the React.FC/PropsWithChildren
type and pass the plain function into observer, or (b) pass the props type to
observer and annotate the function parameter (e.g., observer((props: YourProps)
=> ...)); update the declaration accordingly so observer receives only a props
type or no generic and the component itself is typed on the left-hand side.

}

export const UserProvider: FC<IUserProvider> = observer(({ children }) => {
export const UserProvider = observer<React.FC<React.PropsWithChildren>>(({ children }) => {
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

Fix observer generic and missing React import.

observer<React.FC<React.PropsWithChildren>> is invalid: the generic expects the props object (P), not a component type, so TypeScript will infer P as the React.FC constructor (which doesn’t extend object). This will break the build. Additionally, React isn’t imported, so the React. namespace is undefined. Drop the generic and type the props explicitly, e.g.:

-import { useEffect } from "react";
+import { type PropsWithChildren, useEffect } from "react";-export const UserProvider = observer<React.FC<React.PropsWithChildren>>(({ children }) => {
+export const UserProvider = observer(({ children }: PropsWithChildren) => {

or declare type UserProviderProps = PropsWithChildren; and use that. This keeps observer happy and removes the need for a React namespace import.

📝 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
export const UserProvider = observer<React.FC<React.PropsWithChildren>>(({ children }) => {
import { type PropsWithChildren, useEffect } from "react";
// … other imports
export const UserProvider = observer(({ children }: PropsWithChildren) => {
// … component implementation
});
🤖 Prompt for AI Agents
In apps/admin/app/(all)/user.provider.tsx around line 9, the observer is
incorrectly given a component-type generic and React is not imported; remove the
incorrect generic from observer, explicitly type the props as PropsWithChildren
(or declare type UserProviderProps = PropsWithChildren) and add an import for
PropsWithChildren from 'react', e.g. change to observer(({ children }:
PropsWithChildren) => { ... }) or use the declared UserProviderProps in the
parameter, which fixes the generic misuse and provides the missing React type
import.

@@ -1,6 +1,6 @@
"use client";
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

Incomplete Next.js to React Router migration.

The file contains Next.js-specific code that must be migrated:

  1. Line 1: The "use client" directive is a Next.js feature and should be removed in a Vite + React Router app.
  2. Line 4: The useSearchParams import from "next/navigation" must be replaced with React Router's equivalent.

Apply this diff to complete the migration:

-"use client";
-
 import { useEffect, useMemo, useState } from "react";
-import { useSearchParams } from "next/navigation";
+import { useSearchParams } from "react-router";

Also applies to: 4-4

🤖 Prompt for AI Agents
In apps/admin/core/components/instance/setup-form.tsx lines 1 and 4, remove the
Next.js "use client" directive and replace the Next-specific import of
useSearchParams from "next/navigation" with React Router's hook by importing
useSearchParams from "react-router-dom"; update any surrounding import list
accordingly so the component uses the React Router hook (no other runtime
changes required if usages already follow the standard useSearchParams API).

"use client";

import { FC, useEffect, useMemo, useState } from "react";
import { useEffect, useMemo, useState } from "react";
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

Missing React import for React.FC usage.

Line 56 uses React.FC but the React namespace is never imported, causing a runtime error.

Apply this diff to import React:

-import { useEffect, useMemo, useState } from "react";
+import React, { useEffect, useMemo, useState } from "react";

Alternatively, import and use FC directly:

-import { useEffect, useMemo, useState } from "react";
+import { type FC, useEffect, useMemo, useState } from "react";

Then update line 56:

-export const InstanceSetupForm: React.FC = (props) => {
+export const InstanceSetupForm: FC = () => {
🤖 Prompt for AI Agents
In apps/admin/core/components/instance/setup-form.tsx around lines 3 and 56, the
file uses React.FC at line 56 but never imports the React namespace; either add
a top import for React (e.g. import React from "react") at the top of the file
or import FC directly (e.g. import { FC } from "react"), then update the
component declaration at line 56 to use the matching identifier (React.FC if you
imported React, or FC if you imported FC) so the type reference resolves.

Comment on lines +83 to +84
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD curl -fsS http://127.0.0.1:3000/ >/dev/null || exit 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

Install curl before using it in the healthcheck.

nginx:1.27-alpine does not ship with curl, so the healthcheck exits 127 immediately and marks the container unhealthy. Add curl (or swap to an installed tool) in the production stage before the healthcheck runs.

 FROM nginx:1.27-alpine AS production
+
+RUN apk add --no-cache curl
🤖 Prompt for AI Agents
In apps/admin/Dockerfile.admin around lines 83-84, the HEALTHCHECK uses curl but
the base image nginx:1.27-alpine doesn’t include curl so the check fails with
exit 127; install curl (e.g., apk add --no-cache curl) in the production stage
before the HEALTHCHECK line or replace the check with a tool already present
(like wget or nc) and ensure the package install is in the same stage so the
binary exists at healthcheck runtime.

Comment on lines +12 to +27
set_real_ip_from 0.0.0.0/0;
real_ip_recursive on;
real_ip_header X-Forward-For;
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;

access_log /dev/stdout;
error_log /dev/stderr;

server {
listen 3000;

location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
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

Do not trust all client IPs & fix real_ip_header

set_real_ip_from 0.0.0.0/0; tells Nginx to trust every client-supplied X-Forwarded-For, so any attacker can spoof their IP and bypass rate limits/logging. Restrict this to your real proxy network or drop it entirely. Also, the header name is misspelled—use X-Forwarded-For.

-  set_real_ip_from        0.0.0.0/0;
-  real_ip_recursive       on;
-  real_ip_header          X-Forward-For;
+  # set_real_ip_from <trusted proxy CIDR>;
+  real_ip_recursive       on;
+  real_ip_header          X-Forwarded-For;
📝 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
set_real_ip_from 0.0.0.0/0;
real_ip_recursive on;
real_ip_header X-Forward-For;
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
access_log /dev/stdout;
error_log /dev/stderr;
server {
listen 3000;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
# set_real_ip_from <trusted proxy CIDR>;
real_ip_recursive on;
real_ip_header X-Forwarded-For;
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
access_log /dev/stdout;
error_log /dev/stderr;
server {
listen 3000;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
🤖 Prompt for AI Agents
In apps/admin/nginx/nginx.conf around lines 12 to 27, the config currently
trusts all client IPs and misspells the header: change or remove the overly
broad set_real_ip_from 0.0.0.0/0 and instead list only your trusted proxy
CIDR(s) (or omit the directive if there is no trusted proxy), and correct the
header name from X-Forward-For to X-Forwarded-For; keep real_ip_recursive on if
you need the left-most non-proxy IP, and ensure any rate-limiting uses the
trusted remote address variable after these changes.

Comment on lines +27 to +36
app.use(
"/api",
createProxyMiddleware({
target: NEXT_PUBLIC_API_BASE_URL,
changeOrigin: true,
secure: false,
pathRewrite: (path: string) =>
NORMALIZED_API_BASE_PATH === "/api" ? path : path.replace(/^\/api/, NORMALIZED_API_BASE_PATH),
})
);
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

Restore TLS verification on the API proxy.

secure: false disables certificate validation for every proxied request, leaving the admin API traffic vulnerable to MITM attacks in production. Please let http-proxy-middleware verify TLS (or only disable it for clearly scoped local targets).

 app.use(
   "/api",
   createProxyMiddleware({
     target: NEXT_PUBLIC_API_BASE_URL,
     changeOrigin: true,
-    secure: false,
+    secure: !/^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?$/i.test(NEXT_PUBLIC_API_BASE_URL),
     pathRewrite: (path: string) =>
       NORMALIZED_API_BASE_PATH === "/api" ? path : path.replace(/^\/api/, NORMALIZED_API_BASE_PATH),
   })
 );
📝 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
app.use(
"/api",
createProxyMiddleware({
target: NEXT_PUBLIC_API_BASE_URL,
changeOrigin: true,
secure: false,
pathRewrite: (path: string) =>
NORMALIZED_API_BASE_PATH === "/api" ? path : path.replace(/^\/api/, NORMALIZED_API_BASE_PATH),
})
);
app.use(
"/api",
createProxyMiddleware({
target: NEXT_PUBLIC_API_BASE_URL,
changeOrigin: true,
secure: !/^https?:\/\/(localhost|127\.0\.0\.1)(:\d+)?$/i.test(NEXT_PUBLIC_API_BASE_URL),
pathRewrite: (path: string) =>
NORMALIZED_API_BASE_PATH === "/api"
? path
: path.replace(/^\/api/, NORMALIZED_API_BASE_PATH),
})
);
🤖 Prompt for AI Agents
In apps/admin/server/app.ts around lines 27 to 36, the proxy is configured with
secure: false which disables TLS certificate validation for all proxied
requests; change this to enable verification by setting secure: true for
production (or remove the option) and only disable it for explicit local targets
— e.g., compute a boolean like isLocalTarget by parsing NEXT_PUBLIC_API_BASE_URL
and set secure: isLocalTarget ? false : true (or secure: !isLocalTarget) so TLS
is verified except when the target host is localhost/127.0.0.1 or a clearly
scoped development URL.

@lifeiscontent lifeiscontent force-pushed the feat/admin-react-router-migration branch from 4ffa5ac to b18aa45 Compare October 9, 2025 22:19
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: 7

Caution

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

⚠️ Outside diff range comments (2)
apps/admin/app/(all)/(dashboard)/email/page.tsx (1)

14-95: Fix observer generic usage

observer<React.FC<Route.ComponentProps>> sets the props type to React.FC<Route.ComponentProps>, so the wrapped component no longer type-checks against Route.ComponentProps. Type the variable (or supply the props type) instead:

-const InstanceEmailPage = observer<React.FC<Route.ComponentProps>>(() => {
+const InstanceEmailPage: React.FC<Route.ComponentProps> = observer(() => {

This keeps the observer wrapper and restores proper route-props typing.

apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx (1)

5-67: Replace Next.js Image with a standard <img> (React Router build will fail).

next/image isn’t available in the new Vite/React Router admin app, so bundling this page will crash with “Cannot resolve module 'next/image'”. Swap the import and usage for a plain <img> (or another router-compatible image helper).

-import Image from "next/image";
...
-            icon={<Image src={GoogleLogo} height={24} width={24} alt="Google Logo" />}
+            icon={<img src={GoogleLogo} height={24} width={24} alt="Google Logo" />}
♻️ Duplicate comments (17)
apps/admin/app/(all)/(home)/auth-helpers.tsx (1)

1-2: Next.js imports remain - issue not addressed.

The Next.js-specific imports from "next/image" and "next/link" are still present despite being flagged in a previous review. These are incompatible with the React Router migration.

As noted in the previous review, replace these with React Router equivalents and update all corresponding component usages (Link href → to, Image → img).

apps/admin/app/(all)/(dashboard)/email/email-config-form.tsx (1)

3-3: Critical: Missing React namespace import (unfixed from previous review).

The component uses React.FC<IInstanceEmailForm> on Line 33 but the React namespace is not imported, causing a ReferenceError: React is not defined at runtime.

This issue was flagged in a previous review but remains unfixed.

Add the React namespace import:

 "use client";
 
+import React from "react";
 import { useMemo, useState } from "react";
apps/admin/core/components/common/logo-spinner.tsx (2)

3-4: Vite asset imports need alias configuration to function.

The ?url suffix is correct for Vite static asset handling. However, the @/app path alias requires configuration in both vite.config.ts and tsconfig.json as noted in the previous review.


1-2: Critical: Next.js dependencies must be removed for Vite compatibility.

This file still imports next/image and next-themes, which will cause runtime failures in the React Router + Vite environment. The previous review comment already identified this issue and provided the necessary fixes.

apps/admin/app/(all)/user.provider.tsx (1)

9-9: Already flagged: Fix observer generic and missing React import.

This issue was already identified in a previous review. The observer<React.FC<React.PropsWithChildren>> syntax is invalid, and the React namespace is not imported. Please refer to the previous review comment for the complete fix.

apps/admin/core/components/instance/instance-not-ready.tsx (2)

1-1: Duplicate: "use client" directive still present.

This issue was already flagged in a previous review. The Next.js-specific "use client" directive must be removed for React Router + Vite compatibility.


3-4: Duplicate: Next.js imports still present.

This issue was already flagged in a previous review. The Next.js Image and Link imports (and their usage on lines 14 and 21) must be replaced with React Router and standard HTML equivalents.

apps/admin/app/(all)/(dashboard)/layout.tsx (1)

5-5: Critical: Fix the Next.js router import (still unresolved from previous reviews).

This critical issue was flagged in previous reviews but remains unfixed. Line 5 still imports useRouter from "next/navigation", which will cause runtime errors in a React Router context. The replace method on line 19 is from the Next.js router API and won't work correctly.

Apply this diff to fix the import and usage:

-import { useRouter } from "next/navigation";
+import { useNavigate } from "react-router";
 import { Outlet } from "react-router";

Then update the router usage:

-  const { replace } = useRouter();
+  const navigate = useNavigate();
-    if (isUserLoggedIn === false) replace("/");
+    if (isUserLoggedIn === false) navigate("/", { replace: true });
-  }, [replace, isUserLoggedIn]);
+  }, [navigate, isUserLoggedIn]);

Also applies to: 19-19, 24-25

apps/admin/core/components/authentication/google-config.tsx (1)

4-52: Swap out next/link for React Router’s Link.

next/link won’t exist once this admin app runs under Vite + React Router, so any navigation here will explode at runtime. Import Link from the router and map hrefto.

-import Link from "next/link";
+import { Link } from "react-router-dom";
...
-          <Link href="/authentication/google" className={cn(getButtonStyling("link-primary", "md"), "font-medium")}>
+          <Link to="/authentication/google" className={cn(getButtonStyling("link-primary", "md"), "font-medium")}>
...
-        <Link
-          href="/authentication/google"
+        <Link
+          to="/authentication/google"
apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx (1)

19-19: observer generic must receive the props shape, not the component type.

Passing React.FC<Route.ComponentProps> makes MobX expect a prop whose value is a component function, so the file won’t type-check. Annotate the component itself and call observer without that generic.

-const InstanceGoogleAuthenticationPage = observer<React.FC<Route.ComponentProps>>(() => {
+const InstanceGoogleAuthenticationPage: React.FC<Route.ComponentProps> = observer(() => {
apps/admin/app/(all)/instance.provider.tsx (1)

6-6: Provide the props type to observer, not the component type.

observer<React.FC<React.PropsWithChildren>> fails because MobX now thinks the component receives a prop named after a component. Pass the props shape instead (or annotate on the left) to restore correct typing.

-export const InstanceProvider = observer<React.FC<React.PropsWithChildren>>((props) => {
-  const { children } = props;
+export const InstanceProvider = observer<React.PropsWithChildren>(({ children }) => {
apps/admin/ce/components/authentication/authentication-modes.tsx (1)

101-121: Restore explicit component typing and import React for React.FC.

The component is still declared with observer<React.FC<TAuthenticationModeProps>>((props) => …), but observer expects the component function as its argument, not a generic typed arrow. This pattern also references the React namespace without importing it, causing TS to error (Cannot find name 'React'). Please revert to the prevailing pattern:

-import { observer } from "mobx-react";
+import { observer } from "mobx-react";
+import type { FC } from "react";-export const AuthenticationModes = observer<React.FC<TAuthenticationModeProps>>((props) => {
+export const AuthenticationModes: FC<TAuthenticationModeProps> = observer((props) => {

This matches the rest of the codebase and fixes the missing type import.

apps/admin/app/(all)/(dashboard)/ai/page.tsx (1)

12-44: Import React type and revert to standard observer wrapping.

observer<React.FC<Route.ComponentProps>> references React without an import and misuses the generic. Follow the established pattern instead:

-import type { Route } from "./+types/page";
+import type { FC } from "react";
+import type { Route } from "./+types/page";-const InstanceAIPage = observer<React.FC<Route.ComponentProps>>(() => {
+const InstanceAIPage: FC<Route.ComponentProps> = observer(() => {

This fixes the missing namespace and aligns with other pages.

apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx (1)

19-104: Add React type import and use the observer wrapper consistently.

The component still uses observer<React.FC<Route.ComponentProps>> without importing the React namespace. Update it to the established pattern:

-import GitlabLogo from "@/app/assets/logos/gitlab-logo.svg?url";
+import GitlabLogo from "@/app/assets/logos/gitlab-logo.svg?url";
+import type { FC } from "react";-const InstanceGitlabAuthenticationPage = observer<React.FC<Route.ComponentProps>>(() => {
+const InstanceGitlabAuthenticationPage: FC<Route.ComponentProps> = observer(() => {

This resolves the missing type import and keeps typings consistent.

apps/admin/server.mjs (1)

15-70: Guard against missing/invalid ports before calling app.listen.

When NEXT_PUBLIC_ADMIN_BASE_URL omits the port (e.g., https://admin.example.com), parsedBaseUrl.port is ""; Number.parseInt("", 10) yields NaN, so app.listen(PORT, …) throws immediately. Prefer process.env.PORT when valid, fall back to a parsed parsedBaseUrl.port only if present, otherwise default:

-const PORT = Number.parseInt(parsedBaseUrl.port, 10);
+const envPort = process.env.PORT ? Number.parseInt(process.env.PORT, 10) : undefined;
+const urlPort = parsedBaseUrl.port ? Number.parseInt(parsedBaseUrl.port, 10) : undefined;
+const PORT = [envPort, urlPort, 3001].find((value) => Number.isFinite(value))!;

This keeps the server booting across environments.

apps/admin/Dockerfile.admin (1)

76-86: Install curl before running the healthcheck.

nginx:1.27-alpine doesn’t ship with curl, so the healthcheck exits with 127 and marks the container unhealthy. Add apk add --no-cache curl in this stage before the HEALTHCHECK.

 FROM nginx:1.27-alpine AS production
+
+RUN apk add --no-cache curl

Without it, the healthcheck never succeeds.

apps/admin/vite.config.ts (1)

1-43: Define __dirname for this ESM config.

You’re using __dirname on Line 40, but in an ES module (the admin package is "type": "module"), that identifier is undefined. Vite will throw a ReferenceError as soon as it evaluates the config. Import fileURLToPath from node:url and compute __dirname explicitly before using it.

-import path from "node:path";
+import path from "node:path";
+import { fileURLToPath } from "node:url";
 ...
-const publicEnv = PUBLIC_ENV_KEYS.reduce<Record<string, string>>((acc, key) => {
+const __dirname = path.dirname(fileURLToPath(import.meta.url));
+
+const publicEnv = PUBLIC_ENV_KEYS.reduce<Record<string, string>>((acc, key) => {
🧹 Nitpick comments (4)
apps/admin/app/types/react-router-virtual.d.ts (1)

1-5: Use official React Router types for virtual:react-router/server-build
React Router v7.4.0+ ships TypeScript definitions for this virtual module. Remove the any declaration and import the provided ServerBuild type:

declare module "virtual:react-router/server-build" {
  import type { ServerBuild } from "react-router";
  const build: ServerBuild;
  export default build;
}
apps/admin/app/compat/next/image.tsx (1)

12-12: Consider requiring alt text or using a more descriptive default.

The alt attribute defaults to an empty string, which may create accessibility issues if call sites don't provide alt text. While this matches the minimal shim goal, consider either:

  1. Making alt required in NextImageProps
  2. Using a placeholder like "Image" as the default
  3. Adding a console warning when alt is not provided

Apply this diff to make alt required:

 type NextImageProps = React.ImgHTMLAttributes<HTMLImageElement> & {
   src: string;
+  alt: string;
 };

-const Image: React.FC<NextImageProps> = ({ src, alt = "", ...rest }) => <img src={src} alt={alt} {...rest} />;
+const Image: React.FC<NextImageProps> = ({ src, alt, ...rest }) => <img src={src} alt={alt} {...rest} />;
apps/admin/package.json (2)

66-73: Consider removing low-level transitive dependencies from direct dependencies.

Lines 66-73 list several low-level packages (token-types, strtok3, @tokenizer/inflate, uint8array-extras, fflate, @borewit/text-codec, ieee754, debug) that appear to be transitive dependencies. These are typically managed by your actual dependencies (like audio/video/file parsing libraries) and shouldn't be listed as direct dependencies unless you're using them directly in your code.

To verify these are not used directly:

#!/bin/bash
# Search for direct imports of these packages
rg -nP --type=ts --type=tsx 'from ["\x27](token-types|strtok3|@tokenizer/inflate|uint8array-extras|fflate|@borewit/text-codec|ieee754|debug)["\x27]' apps/admin

88-89: Verify Node.js version meets Vite 7 requirements.

Vite 7.1.7 requires Node.js 20.19+ or 22.12+. Ensure your development environment, CI/CD pipeline, and deployment environment meet this requirement.

Based on learnings.

Check the Node.js version requirement in your CI configuration and document it in the README if not already present.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4ffa5ac and b18aa45.

⛔ Files ignored due to path filters (21)
  • apps/admin/app/assets/favicon/apple-touch-icon.png is excluded by !**/*.png
  • apps/admin/app/assets/favicon/favicon-16x16.png is excluded by !**/*.png
  • apps/admin/app/assets/favicon/favicon-32x32.png is excluded by !**/*.png
  • apps/admin/app/assets/favicon/favicon.ico is excluded by !**/*.ico
  • apps/admin/app/assets/images/logo-spinner-dark.gif is excluded by !**/*.gif
  • apps/admin/app/assets/images/logo-spinner-light.gif is excluded by !**/*.gif
  • apps/admin/app/assets/images/plane-takeoff.png is excluded by !**/*.png
  • apps/admin/app/assets/instance/instance-failure-dark.svg is excluded by !**/*.svg
  • apps/admin/app/assets/instance/instance-failure.svg is excluded by !**/*.svg
  • apps/admin/app/assets/logos/github-black.png is excluded by !**/*.png
  • apps/admin/app/assets/logos/github-white.png is excluded by !**/*.png
  • apps/admin/app/assets/logos/gitlab-logo.svg is excluded by !**/*.svg
  • apps/admin/app/assets/logos/google-logo.svg is excluded by !**/*.svg
  • apps/admin/app/assets/logos/oidc-logo.svg is excluded by !**/*.svg
  • apps/admin/app/assets/logos/saml-logo.svg is excluded by !**/*.svg
  • apps/admin/app/assets/logos/takeoff-icon-dark.svg is excluded by !**/*.svg
  • apps/admin/app/assets/logos/takeoff-icon-light.svg is excluded by !**/*.svg
  • apps/admin/public/auth/background-pattern-dark.svg is excluded by !**/*.svg
  • apps/admin/public/auth/background-pattern.svg is excluded by !**/*.svg
  • apps/admin/public/instance/plane-takeoff.png is excluded by !**/*.png
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (107)
  • .dockerignore (1 hunks)
  • .gitignore (1 hunks)
  • apps/admin/.dockerignore (1 hunks)
  • apps/admin/.eslintrc.cjs (1 hunks)
  • apps/admin/.eslintrc.js (0 hunks)
  • apps/admin/Dockerfile.admin (1 hunks)
  • apps/admin/app/(all)/(dashboard)/ai/form.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/ai/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/ai/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/github/form.tsx (3 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/github/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/gitlab/form.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/gitlab/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/google/form.tsx (3 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/google/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/email/email-config-form.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/email/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/email/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/email/test-email-modal.tsx (4 hunks)
  • apps/admin/app/(all)/(dashboard)/general/form.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/general/intercom.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/general/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/general/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/header.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/image/form.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/image/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/image/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/layout.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/sidebar-help-section.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/sidebar.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/workspace/create/form.tsx (1 hunks)
  • apps/admin/app/(all)/(dashboard)/workspace/create/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/workspace/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/workspace/page.tsx (2 hunks)
  • apps/admin/app/(all)/(home)/auth-banner.tsx (1 hunks)
  • apps/admin/app/(all)/(home)/auth-helpers.tsx (2 hunks)
  • apps/admin/app/(all)/(home)/layout.tsx (1 hunks)
  • apps/admin/app/(all)/(home)/page.tsx (2 hunks)
  • apps/admin/app/(all)/(home)/sign-in-form.tsx (2 hunks)
  • apps/admin/app/(all)/instance.provider.tsx (1 hunks)
  • apps/admin/app/(all)/store.provider.tsx (2 hunks)
  • apps/admin/app/(all)/user.provider.tsx (1 hunks)
  • apps/admin/app/compat/next/image.tsx (1 hunks)
  • apps/admin/app/compat/next/link.tsx (1 hunks)
  • apps/admin/app/compat/next/navigation.ts (1 hunks)
  • apps/admin/app/layout.tsx (0 hunks)
  • apps/admin/app/providers.tsx (1 hunks)
  • apps/admin/app/root.tsx (1 hunks)
  • apps/admin/app/routes.ts (1 hunks)
  • apps/admin/app/types/next-image.d.ts (1 hunks)
  • apps/admin/app/types/next-link.d.ts (1 hunks)
  • apps/admin/app/types/next-navigation.d.ts (1 hunks)
  • apps/admin/app/types/react-router-virtual.d.ts (1 hunks)
  • apps/admin/ce/components/authentication/authentication-modes.tsx (2 hunks)
  • apps/admin/core/components/authentication/authentication-method-card.tsx (1 hunks)
  • apps/admin/core/components/authentication/email-config-switch.tsx (1 hunks)
  • apps/admin/core/components/authentication/github-config.tsx (1 hunks)
  • apps/admin/core/components/authentication/gitlab-config.tsx (1 hunks)
  • apps/admin/core/components/authentication/google-config.tsx (1 hunks)
  • apps/admin/core/components/authentication/password-config-switch.tsx (1 hunks)
  • apps/admin/core/components/common/banner.tsx (1 hunks)
  • apps/admin/core/components/common/controller-input.tsx (1 hunks)
  • apps/admin/core/components/common/empty-state.tsx (1 hunks)
  • apps/admin/core/components/common/logo-spinner.tsx (1 hunks)
  • apps/admin/core/components/instance/failure.tsx (1 hunks)
  • apps/admin/core/components/instance/instance-not-ready.tsx (1 hunks)
  • apps/admin/core/components/instance/loading.tsx (1 hunks)
  • apps/admin/core/components/instance/setup-form.tsx (2 hunks)
  • apps/admin/core/components/new-user-popup.tsx (1 hunks)
  • apps/admin/core/hooks/store/use-instance.tsx (1 hunks)
  • apps/admin/core/hooks/store/use-theme.tsx (1 hunks)
  • apps/admin/core/hooks/store/use-user.tsx (1 hunks)
  • apps/admin/core/hooks/store/use-workspace.tsx (1 hunks)
  • apps/admin/core/store/instance.store.ts (2 hunks)
  • apps/admin/core/store/root.store.ts (1 hunks)
  • apps/admin/core/store/theme.store.ts (1 hunks)
  • apps/admin/core/store/user.store.ts (1 hunks)
  • apps/admin/core/store/workspace.store.ts (1 hunks)
  • apps/admin/core/utils/public-asset.ts (1 hunks)
  • apps/admin/middleware.js (1 hunks)
  • apps/admin/next-env.d.ts (0 hunks)
  • apps/admin/next.config.js (0 hunks)
  • apps/admin/nginx/nginx.conf (1 hunks)
  • apps/admin/package.json (2 hunks)
  • apps/admin/public/.well-known/appspecific/com.chrome.devtools.json (1 hunks)
  • apps/admin/public/favicon/site.webmanifest (0 hunks)
  • apps/admin/react-router.config.ts (1 hunks)
  • apps/admin/server.mjs (1 hunks)
  • apps/admin/server/app.ts (1 hunks)
  • apps/admin/styles/globals.css (1 hunks)
  • apps/admin/tsconfig.json (1 hunks)
  • apps/admin/vite.config.ts (1 hunks)
  • apps/live/package.json (1 hunks)
  • apps/web/app/(all)/layout.tsx (1 hunks)
  • apps/web/package.json (0 hunks)
  • packages/decorators/package.json (1 hunks)
  • packages/editor/package.json (1 hunks)
  • packages/editor/tsdown.config.ts (1 hunks)
  • packages/eslint-config/package.json (1 hunks)
  • packages/i18n/package.json (1 hunks)
  • packages/logger/package.json (1 hunks)
  • packages/propel/package.json (2 hunks)
⛔ Files not processed due to max files limit (7)
  • packages/propel/tsdown.config.ts
  • packages/services/package.json
  • packages/shared-state/package.json
  • packages/ui/package.json
  • packages/utils/package.json
  • packages/utils/src/string.ts
  • pnpm-workspace.yaml
💤 Files with no reviewable changes (15)
  • apps/admin/app/(all)/(dashboard)/authentication/github/layout.tsx
  • apps/admin/app/(all)/(dashboard)/ai/layout.tsx
  • apps/admin/next.config.js
  • apps/admin/app/layout.tsx
  • apps/admin/app/(all)/(dashboard)/general/layout.tsx
  • apps/admin/app/(all)/(dashboard)/authentication/gitlab/layout.tsx
  • apps/admin/app/(all)/(dashboard)/image/layout.tsx
  • apps/admin/app/(all)/(dashboard)/email/layout.tsx
  • apps/admin/app/(all)/(dashboard)/workspace/layout.tsx
  • apps/admin/public/favicon/site.webmanifest
  • apps/admin/next-env.d.ts
  • apps/admin/app/(all)/(dashboard)/authentication/google/layout.tsx
  • apps/admin/app/(all)/(dashboard)/authentication/layout.tsx
  • apps/admin/.eslintrc.js
  • apps/web/package.json
✅ Files skipped from review due to trivial changes (4)
  • apps/admin/.dockerignore
  • .dockerignore
  • .gitignore
  • apps/admin/public/.well-known/appspecific/com.chrome.devtools.json
🚧 Files skipped from review as they are similar to previous changes (40)
  • apps/admin/core/utils/public-asset.ts
  • apps/admin/core/components/common/empty-state.tsx
  • apps/admin/core/store/workspace.store.ts
  • apps/admin/core/components/common/banner.tsx
  • apps/admin/app/(all)/store.provider.tsx
  • apps/admin/core/store/root.store.ts
  • apps/admin/app/(all)/(dashboard)/general/page.tsx
  • apps/admin/core/components/authentication/authentication-method-card.tsx
  • apps/admin/app/(all)/(dashboard)/sidebar-help-section.tsx
  • apps/admin/app/(all)/(dashboard)/workspace/create/form.tsx
  • packages/i18n/package.json
  • apps/admin/app/(all)/(dashboard)/image/form.tsx
  • apps/admin/tsconfig.json
  • apps/admin/app/types/next-image.d.ts
  • apps/admin/core/components/common/controller-input.tsx
  • apps/admin/app/(all)/(dashboard)/general/form.tsx
  • apps/admin/app/(all)/(dashboard)/authentication/page.tsx
  • apps/admin/server/app.ts
  • apps/admin/core/store/user.store.ts
  • packages/editor/package.json
  • apps/admin/core/components/authentication/github-config.tsx
  • apps/admin/core/components/new-user-popup.tsx
  • apps/admin/app/(all)/(home)/auth-banner.tsx
  • apps/admin/app/(all)/(home)/page.tsx
  • apps/admin/core/hooks/store/use-user.tsx
  • apps/admin/app/types/next-navigation.d.ts
  • packages/editor/tsdown.config.ts
  • packages/eslint-config/package.json
  • apps/admin/app/(all)/(dashboard)/header.tsx
  • apps/admin/.eslintrc.cjs
  • apps/admin/app/providers.tsx
  • apps/admin/core/components/instance/setup-form.tsx
  • apps/admin/app/(all)/(dashboard)/workspace/page.tsx
  • apps/admin/app/(all)/(home)/sign-in-form.tsx
  • apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx
  • apps/admin/app/(all)/(dashboard)/email/test-email-modal.tsx
  • apps/admin/react-router.config.ts
  • apps/admin/core/store/theme.store.ts
  • apps/admin/app/(all)/(dashboard)/sidebar.tsx
  • apps/admin/core/components/instance/failure.tsx
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-10-09T20:42:31.804Z
Learnt from: lifeiscontent
PR: makeplane/plane#7922
File: apps/admin/app/(all)/(dashboard)/ai/form.tsx:19-19
Timestamp: 2025-10-09T20:42:31.804Z
Learning: In the makeplane/plane repository, React types are globally available through TypeScript configuration. Type annotations like React.FC, React.ReactNode, etc. can be used without explicitly importing the React namespace. The codebase uses the modern JSX transform, so React imports are not required for JSX or type references.

Applied to files:

  • apps/admin/app/(all)/(dashboard)/general/intercom.tsx
  • apps/admin/app/(all)/(dashboard)/authentication/github/form.tsx
  • apps/admin/app/(all)/(dashboard)/ai/form.tsx
  • apps/admin/app/(all)/instance.provider.tsx
  • apps/admin/app/(all)/(dashboard)/authentication/gitlab/form.tsx
📚 Learning: 2025-10-09T22:12:26.397Z
Learnt from: lifeiscontent
PR: makeplane/plane#7922
File: apps/admin/app/(all)/(dashboard)/ai/form.tsx:19-19
Timestamp: 2025-10-09T22:12:26.397Z
Learning: When `types/react` is installed in a TypeScript project (which is standard for React + TypeScript codebases), React types (React.FC, React.ReactNode, React.ComponentProps, etc.) are globally available by design. These type annotations can and should be used without explicitly importing the React namespace. This is a TypeScript/DefinitelyTyped feature, not codebase-specific configuration.

Applied to files:

  • apps/admin/app/(all)/(dashboard)/general/intercom.tsx
  • apps/admin/app/(all)/(dashboard)/ai/form.tsx
🧬 Code graph analysis (5)
apps/admin/app/(all)/(dashboard)/email/page.tsx (5)
apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx (1)
  • meta (115-115)
apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx (1)
  • meta (103-103)
apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx (1)
  • meta (104-104)
apps/admin/app/(all)/(dashboard)/authentication/page.tsx (1)
  • meta (115-115)
apps/admin/app/(all)/(dashboard)/workspace/page.tsx (1)
  • meta (171-171)
apps/admin/app/root.tsx (1)
apps/admin/app/providers.tsx (1)
  • AppProviders (19-32)
apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx (5)
apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx (1)
  • meta (115-115)
apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx (1)
  • meta (103-103)
apps/admin/app/(all)/(dashboard)/authentication/page.tsx (1)
  • meta (115-115)
apps/admin/app/(all)/(dashboard)/email/page.tsx (1)
  • meta (95-95)
apps/admin/app/(all)/(dashboard)/workspace/page.tsx (1)
  • meta (171-171)
apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx (5)
apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx (1)
  • meta (115-115)
apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx (1)
  • meta (104-104)
apps/admin/app/(all)/(dashboard)/authentication/page.tsx (1)
  • meta (115-115)
apps/admin/app/(all)/(dashboard)/email/page.tsx (1)
  • meta (95-95)
apps/admin/app/(all)/(dashboard)/workspace/page.tsx (1)
  • meta (171-171)
apps/admin/vite.config.ts (1)
apps/admin/server.mjs (1)
  • __dirname (8-8)
⏰ 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). (2)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (30)
apps/admin/app/(all)/(dashboard)/general/intercom.tsx (3)

3-3: LGTM: Import follows codebase conventions.

The removal of the FC import is correct. Based on learnings, React types (including React.FC) are globally available through the TypeScript configuration in this repository and can be used without explicitly importing the React namespace.


7-7: Good use of type-only import.

Using import type for the interface import correctly indicates that this is only used at the type level, which can improve tree-shaking and makes the intent clearer.


16-16: LGTM: React.FC usage is correct for this codebase.

The use of React.FC without importing the React namespace is valid in this repository. Based on learnings, React types are globally available through TypeScript configuration when @types/react is installed, allowing namespace usage without explicit imports.

apps/admin/core/hooks/store/use-theme.tsx (1)

4-4: LGTM! Type-only import correctly applied.

The conversion to import type is appropriate since IThemeStore is only used for the return type annotation. This improves tree-shaking and clarifies the type-only usage.

apps/admin/core/hooks/store/use-workspace.tsx (1)

4-4: LGTM! Consistent with the type-only import pattern.

Correctly applies import type for IWorkspaceStore, which is only used as a return type annotation.

apps/admin/core/store/instance.store.ts (1)

4-16: LGTM! Correct distinction between runtime and type-only imports.

The refactor correctly:

  • Converts type annotations (TInstanceStatus, interfaces from @plane/types, CoreRootStore) to import type
  • Preserves EInstanceStatus as a runtime import since it's used at line 116 (EInstanceStatus.ERROR)

This improves tree-shaking while maintaining correct runtime behavior.

apps/admin/core/hooks/store/use-instance.tsx (1)

4-4: LGTM! Type-only import correctly applied.

Consistent with the pattern across other hooks—IInstanceStore is only used for the return type annotation.

apps/admin/app/(all)/(home)/auth-helpers.tsx (3)

5-7: Good use of type-only imports.

Using import type for type definitions correctly reduces the runtime bundle size and makes the distinction between types and values explicit.


10-13: Correct Vite asset imports.

The ?url suffix on asset imports is the proper Vite convention for importing static assets as URL strings. This aligns well with the migration to Vite.


30-30: More explicit type usage.

Changing to React.ReactNode is more explicit and avoids potential naming conflicts with other imported types.

apps/admin/app/(all)/(dashboard)/email/email-config-form.tsx (1)

8-8: Good practice: Type-only imports.

The conversion to import type for type-only imports follows TypeScript best practices and helps with build optimization and tree-shaking.

Also applies to: 12-13

apps/admin/core/components/instance/loading.tsx (1)

4-5: LGTM: Correct Vite asset import pattern.

The asset imports correctly use the ?url suffix, which is the proper Vite pattern for importing asset URLs. This ensures the imports resolve to URL strings rather than processed assets, which is appropriate for the src attribute of the Image component.

apps/admin/core/components/instance/instance-not-ready.tsx (1)

7-7: LGTM! Vite asset import convention applied correctly.

The ?url suffix is the correct Vite convention for importing static assets as URLs.

apps/admin/app/(all)/(home)/layout.tsx (1)

3-9: LGTM! Migration to React Router pattern is correct.

The changes properly migrate from Next.js children-based layout to React Router's <Outlet /> pattern. The function signature correctly removes the children prop, and the Outlet component will render nested route content.

apps/admin/app/(all)/(dashboard)/layout.tsx (1)

41-43: LGTM! Outlet rendering pattern is correct.

The migration to render nested routes via <Outlet /> is appropriate for React Router, assuming the router import issue is resolved.

apps/live/package.json (1)

52-57: Confirmed @types/[email protected] is published
No action required.

apps/admin/styles/globals.css (1)

1-1: LGTM! More explicit CSS import path.

The change from @plane/propel/styles/fonts to @plane/propel/styles/fonts.css is a safe refactor that makes the CSS import more explicit. Both export paths map to the same physical file in the propel package, maintaining backward compatibility.

apps/web/app/(all)/layout.tsx (1)

8-8: LGTM! Consistent CSS import path update.

The change to @plane/propel/styles/react-day-picker.css aligns with the explicit CSS extension pattern introduced across the codebase. Both export paths map to the same stylesheet, ensuring backward compatibility.

packages/propel/package.json (2)

164-167: LGTM! Backward-compatible export strategy.

The dual export mappings (with and without .css extensions) provide a smooth migration path. Both variants map to the same physical CSS files, ensuring backward compatibility while allowing new code to use the more explicit .css extension.


171-175: No update required for new dependencies
All caret ranges already cover the latest patch releases and no security advisories were found.

apps/admin/app/(all)/(dashboard)/ai/form.tsx (1)

6-9: LGTM! Type-only imports and React.FC usage are correct.

The type-only imports for configuration types and form field types are properly separated from runtime imports, and the React.FC usage is appropriate given that React types are globally available through @types/react.

Also applies to: 19-19

apps/admin/app/compat/next/navigation.ts (3)

6-23: LGTM! useRouter shim correctly maps Next.js router API to React Router.

The implementation properly memoizes the router object based on the navigate function, and the method mappings (push, replace, back, forward, refresh, prefetch) are appropriate for a compatibility shim. The no-op prefetch is acceptable since React Router doesn't have built-in prefetching.


25-28: LGTM! usePathname correctly returns the current pathname.


30-33: LGTM! useSearchParams correctly wraps React Router's hook.

apps/admin/app/compat/next/link.tsx (1)

6-21: LGTM! Link shim correctly maps Next.js Link API to React Router.

The component properly handles Next.js-specific props (prefetch, scroll, shallow) by ignoring them, and correctly forwards href and replace to the React Router Link. The underscore prefix for ignored props follows best practices.

apps/admin/app/types/next-link.d.ts (1)

1-12: LGTM! Type declaration correctly defines the next/link module interface.

The ambient module declaration properly types the Link component and its Props, providing type safety for code that imports from "next/link".

apps/admin/package.json (2)

7-19: LGTM! Module type and scripts are correctly configured for React Router + Vite.

The ESM module type aligns with Vite requirements, and the updated scripts properly support the new React Router-based development, build, and preview workflows.


42-42: Verify Express 5.x compatibility and JSON body parsing

  • No express.json() or body-parser middleware detected in your server setup; ensure request bodies are parsed before mounting routes.
  • Confirm removal of deprecated APIs (e.g., res.send(status, body), app.del) and compatibility of all third-party Express middleware with Express 5.x.
apps/admin/core/components/authentication/gitlab-config.tsx (1)

9-9: LGTM! Type-only import is correctly applied.

The change to a type-only import for TInstanceAuthenticationMethodKeys aligns with the broader pattern in this PR of separating type imports from runtime imports.

apps/admin/core/components/authentication/password-config-switch.tsx (1)

6-6: LGTM! Type-only import is correctly applied.

The change to a type-only import for TInstanceAuthenticationMethodKeys is consistent with the type-import strategy used throughout this PR.

import { InstanceImageConfigForm } from "./form";

const InstanceImagePage = observer(() => {
const InstanceImagePage = observer<React.FC<Route.ComponentProps>>(() => {
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

Stop passing the component type into observer.

observer<React.FC<Route.ComponentProps>> makes the component expect a React.FC prop and breaks type checking. Annotate the variable and call observer plainly.

-const InstanceImagePage = observer<React.FC<Route.ComponentProps>>(() => {
+const InstanceImagePage: React.FC<Route.ComponentProps> = observer(() => {
📝 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 InstanceImagePage = observer<React.FC<Route.ComponentProps>>(() => {
const InstanceImagePage: React.FC<Route.ComponentProps> = observer(() => {
🤖 Prompt for AI Agents
In apps/admin/app/(all)/(dashboard)/image/page.tsx around line 12, the component
is being passed a generic to observer (observer<React.FC<Route.ComponentProps>>)
which causes incorrect prop expectations; instead, declare the variable with the
proper type (e.g. const InstanceImagePage: React.FC<Route.ComponentProps> = ...)
and call observer without generics (observer(InstanceImagePage)) so the
component retains correct typing and prop checking.

@@ -1,33 +1,28 @@
"use client";
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

Remove the Next.js "use client" directive.

The "use client" directive is specific to Next.js and is not needed in a React Router application.

Apply this diff to remove the directive:

-"use client";
-
 import { useEffect } from "react";
📝 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
"use client";
import { useEffect } from "react";
🤖 Prompt for AI Agents
In apps/admin/app/(all)/(dashboard)/layout.tsx around line 1, remove the Next.js
"use client" directive at the top of the file; this directive is unnecessary for
a React Router app—delete the line containing "use client" and ensure no other
code depends on client-only behavior (if any client-only hooks/components are
present, convert them to proper client components or move them to dedicated
client files).

import { WorkspaceCreateForm } from "./form";

const WorkspaceCreatePage = observer(() => (
const WorkspaceCreatePage = observer<React.FC<Route.ComponentProps>>(() => (
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

Incorrect observer generic breaks typing.

Giving observer a component type leads to props mismatches. Annotate the component itself and leave observer unparameterized.

-const WorkspaceCreatePage = observer<React.FC<Route.ComponentProps>>(() => (
+const WorkspaceCreatePage: React.FC<Route.ComponentProps> = observer(() => (
📝 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 WorkspaceCreatePage = observer<React.FC<Route.ComponentProps>>(() => (
const WorkspaceCreatePage: React.FC<Route.ComponentProps> = observer(() => (
🤖 Prompt for AI Agents
In apps/admin/app/(all)/(dashboard)/workspace/create/page.tsx around line 8, the
observer generic is incorrectly used and causes prop type mismatches; remove the
generic parameter from observer and instead annotate the component variable
itself with the correct React.FC<Route.ComponentProps> type (i.e. declare the
const with that type) so observer is called unparameterized around the
component; update the declaration accordingly so TypeScript sees the component
props on the variable rather than on observer.

@@ -1,16 +1,12 @@
"use client";
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

Remove obsolete Next.js directive.

The "use client" directive is specific to Next.js App Router and has no meaning in a React Router + Vite application. It should be removed as part of the migration.

Apply this diff:

-"use client";
-
 import { useEffect } from "react";
📝 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
"use client";
import { useEffect } from "react";
// …rest of the file unchanged
🤖 Prompt for AI Agents
In apps/admin/app/(all)/user.provider.tsx around line 1, remove the obsolete
Next.js "use client" directive at the top of the file; this file is now part of
a React Router + Vite app so the directive is meaningless—delete that line and
ensure no other top-of-file directives remain.

import PlaneTakeOffImage from "@/app/assets/images/plane-takeoff.png?url";

export const InstanceNotReady: FC = () => (
export const InstanceNotReady: React.FC = () => (
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

Missing React import for React.FC type.

The component uses React.FC but React is not imported. This will cause a ReferenceError at runtime.

Apply this diff to add the React import:

+"use client";
+
+import React from "react";
 import Image from "next/image";
 import Link from "next/link";

Note: Also address the other flagged issues (remove "use client", replace Next.js imports) as indicated in previous comments.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In apps/admin/core/components/instance/instance-not-ready.tsx around line 9, the
component uses the React.FC type but React is not imported, and previous review
flags require removing the "use client" directive and replacing deprecated
Next.js imports; add an import for React (import React from 'react') at the top
so React.FC is defined, remove any "use client" directive from the file, and
swap any Next.js-specific imports to the recommended alternatives per prior
comments (e.g., use next/navigation or framework-agnostic APIs as instructed)
ensuring all imports are valid and the component compiles without runtime
ReferenceError.

Comment on lines 1 to 2
import Image from "next/image";
import { useTheme } from "next-themes";
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

🧩 Analysis chain

Critical: Replace Next.js-specific imports for React Router migration.

This component still imports next/image and next-themes, which are Next.js-specific packages. Since this PR migrates to React Router + Vite, these imports will cause runtime failures.

Replace with:

  • next/image → standard <img> tag or a custom image component
  • next-themes → a React Router/Vite-compatible theming solution (e.g., usehooks-ts theme hook, context-based theme provider, or a library like use-theme)

Run the following script to check if these Next.js packages are shimmed or replaced elsewhere in the codebase:


🏁 Script executed:

#!/bin/bash
# Check for Next.js package usage and potential shims/replacements

echo "=== Checking for next/image usage ==="
rg -n --type=ts --type=tsx -C2 "from [\"']next/image[\"']"

echo -e "\n=== Checking for next-themes usage ==="
rg -n --type=ts --type=tsx -C2 "from [\"']next-themes[\"']"

echo -e "\n=== Checking for potential shims or replacements ==="
fd -e ts -e tsx -e js | xargs rg -l "next/image|next-themes" | head -20

echo -e "\n=== Checking package.json dependencies ==="
fd package.json --exec cat {} \; | jq '.dependencies, .devDependencies' 2>/dev/null | grep -E "next|theme"

Length of output: 1538


Critical: Remove Next.js imports in apps/admin/core/components/instance/loading.tsx, lines 1-2. No shims or replacements detected; next and next-themes remain in dependencies and code. Use a standard <img> or custom image component and a Vite-compatible theming solution instead.

🤖 Prompt for AI Agents
In apps/admin/core/components/instance/loading.tsx lines 1-2, remove the
Next.js-specific imports (import Image from "next/image"; import { useTheme }
from "next-themes";) and replace their usage: switch to a standard <img> element
or your project’s image component and use the Vite-compatible theming solution
(or a simple context/hook) instead of useTheme; update any props or attributes
previously tied to next/image (width/height/layout) to standard HTML attributes
or your image component API, and wire theme detection to the chosen replacement
so the component no longer imports next or next-themes.

cursor[bot]

This comment was marked as outdated.

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: 2

♻️ Duplicate comments (1)
apps/admin/vite.config.ts (1)

1-4: The __dirname issue flagged in the previous review remains unresolved.

As noted in the earlier review, this file uses __dirname at lines 55, 59, 63, 64, and 65, but __dirname is undefined in ES modules. The suggested fix from the previous review should be applied.

For reference, the solution is already implemented correctly in apps/admin/server.mjs at line 7:

const __dirname = path.dirname(fileURLToPath(import.meta.url));

Apply the same pattern here by adding the necessary imports and definition at the top of this file.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b18aa45 and 97083c9.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (3)
  • apps/admin/package.json (2 hunks)
  • apps/admin/server.mjs (1 hunks)
  • apps/admin/vite.config.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/admin/server.mjs
🧰 Additional context used
🧬 Code graph analysis (1)
apps/admin/vite.config.ts (1)
apps/admin/server.mjs (1)
  • __dirname (8-8)
⏰ 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). (2)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (1)
apps/admin/vite.config.ts (1)

59-72: Configuration looks well-structured for the migration.

The plugin configuration, alias mappings, and resolve options are appropriate:

  • React Router and tsconfig path plugins are correctly configured
  • Next.js compatibility shims support the incremental migration
  • preserveSymlinks: true is properly documented for Docker/pnpm workspaces
  • React deduplication prevents common issues with multiple instances

"debug": "4.4.3",
"dom4": "^2.1.6",
"dotenv": "^16.4.5",
"express": "^5.1.0",
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

Align Express runtime and type declarations.

express is pinned to ^5.1.0, but we’re still pulling in @types/[email protected], which models the Express 4 API. This mismatch will surface incorrect typings (and potentially type errors) now that the runtime has moved to v5. Either stay on [email protected] or move fully to v5 by removing the outdated DefinitelyTyped package and relying on the v5-provided types (or the matching v5 typings package, if available).

Also applies to: 82-82

🤖 Prompt for AI Agents
In apps/admin/package.json around lines 45 and 82, the project depends on
express@^5.1.0 while still pulling @types/[email protected] (Express 4 types);
either pin express to a 4.x release to match the @types package or fully adopt
Express 5 by removing @types/express (and any other v4-only type packages) so
TypeScript uses Express 5's built-in types (or the correct v5 typing package if
one exists); update package.json accordingly, run reinstall to refresh lockfile,
and verify no remaining type imports reference the old DefinitelyTyped package.

Comment on lines +32 to +34
define: {
"process.env": JSON.stringify(publicEnv),
},
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

🧩 Analysis chain

Consider individual key replacements for process.env variables.

Defining process.env as a serialized JSON object may cause issues because it replaces the entire process.env reference with a string. Code that accesses process.env.KEY might not work as expected.

The standard Vite approach is to define individual keys:

-    define: {
-      "process.env": JSON.stringify(publicEnv),
-    },
+    define: Object.entries(publicEnv).reduce((acc, [key, value]) => {
+      acc[`process.env.${key}`] = JSON.stringify(value);
+      return acc;
+    }, {} as Record<string, string>),

Alternatively, migrate to Vite's standard import.meta.env pattern for new code while maintaining process.env compatibility only where needed.

Verify that the current serialization approach works correctly with your codebase:


🏁 Script executed:

#!/bin/bash
# Search for process.env usage patterns in the admin app
rg -nP --type=ts --type=tsx -C2 'process\.env\.' apps/admin/

Length of output: 27


🏁 Script executed:

#!/bin/bash
# Search for any process.env.KEY usage in .ts/.tsx files under apps/admin
rg -nP 'process\.env\.' -g '*.ts' -g '*.tsx' -n -C2 apps/admin/

Length of output: 1799


Define individual process.env keys instead of the entire object.

Replacing process.env with a serialized JSON string breaks all process.env.KEY lookups (e.g. in vite.config.ts, server/app.ts, react-router.config.ts). Use a per-key mapping:

define: Object.entries(publicEnv).reduce((acc, [key, value]) => {
  acc[`process.env.${key}`] = JSON.stringify(value);
  return acc;
}, {} as Record<string, string>),

Alternatively, migrate to import.meta.env for new code.

🤖 Prompt for AI Agents
In apps/admin/vite.config.ts around lines 32 to 34, the define currently assigns
the entire process.env object which breaks individual process.env.KEY lookups;
change it to define each env key separately by mapping publicEnv entries into
keys of the form "process.env.KEY" with JSON.stringified values (e.g., using
Object.entries(publicEnv).reduce to build a Record<string,string>), so that Vite
replaces each process.env.KEY correctly (or alternatively migrate code to use
import.meta.env for new code).

cursor[bot]

This comment was marked as outdated.

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: 1

🧹 Nitpick comments (3)
apps/admin/app/root.tsx (1)

59-65: Enhance ErrorBoundary to display error details for better debugging.

The ErrorBoundary currently shows only a generic message without error details, making debugging difficult. Use React Router's useRouteError hook to access and display error information, especially helpful during development.

Apply this diff to improve the ErrorBoundary:

+import { useRouteError, isRouteErrorResponse } from "react-router";
+
 export function ErrorBoundary() {
+  const error = useRouteError();
+  
+  let errorMessage: string;
+  let errorStatus: string | undefined;
+  
+  if (isRouteErrorResponse(error)) {
+    errorMessage = error.data?.message || error.statusText;
+    errorStatus = `${error.status}`;
+  } else if (error instanceof Error) {
+    errorMessage = error.message;
+  } else {
+    errorMessage = "Unknown error";
+  }
+  
   return (
-    <div>
-      <p>Something went wrong.</p>
+    <div className="flex min-h-screen items-center justify-center p-4">
+      <div className="max-w-md space-y-4 text-center">
+        {errorStatus && <h1 className="text-4xl font-bold">{errorStatus}</h1>}
+        <h2 className="text-2xl font-semibold">Something went wrong</h2>
+        <p className="text-gray-600">{errorMessage}</p>
+        {process.env.NODE_ENV === "development" && error instanceof Error && error.stack && (
+          <pre className="mt-4 max-h-96 overflow-auto rounded bg-gray-100 p-4 text-left text-xs">
+            {error.stack}
+          </pre>
+        )}
+      </div>
     </div>
   );
 }
apps/admin/app/compat/next/helper.ts (1)

30-30: Consider structured logging for production.

Using console.warn for error cases may be noisy in production. For a compatibility shim during migration, this is acceptable, but consider using a structured logging library or conditional logging based on environment if this code persists long-term.

apps/admin/app/compat/next/navigation.ts (1)

18-20: Prefetch is a no-op; expected performance impact.

The prefetch method is implemented as a no-op, meaning any code relying on route prefetching for performance optimization will lose that benefit. This is an acceptable trade-off for a compatibility shim, as the app remains functional—just without the prefetch performance enhancement.

If prefetching is critical, consider implementing a custom solution using React Router's route preloading mechanisms or dynamic imports.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 97083c9 and fa73c33.

📒 Files selected for processing (6)
  • apps/admin/app/compat/next/helper.ts (1 hunks)
  • apps/admin/app/compat/next/link.tsx (1 hunks)
  • apps/admin/app/compat/next/navigation.ts (1 hunks)
  • apps/admin/app/error.tsx (0 hunks)
  • apps/admin/app/root.tsx (1 hunks)
  • apps/api/plane/license/api/views/admin.py (3 hunks)
💤 Files with no reviewable changes (1)
  • apps/admin/app/error.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/admin/app/compat/next/link.tsx
🧰 Additional context used
🧬 Code graph analysis (3)
apps/admin/app/compat/next/helper.ts (1)
apps/api/plane/settings/storage.py (1)
  • url (16-17)
apps/admin/app/compat/next/navigation.ts (1)
apps/admin/app/compat/next/helper.ts (1)
  • ensureTrailingSlash (6-33)
apps/admin/app/root.tsx (1)
apps/admin/app/providers.tsx (1)
  • AppProviders (19-32)
⏰ 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). (2)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: Analyze (javascript)
🔇 Additional comments (7)
apps/admin/app/root.tsx (3)

24-39: LGTM! Layout component follows React Router best practices.

The HTML shell structure is correct with proper component placement (Meta/Links in head, Scripts at end of body) and appropriate use of suppressHydrationWarning for hydration consistency. The integration with AppProviders maintains the existing provider setup.


41-53: Verify if hardcoded og:url should be environment-specific.

The og:url is hardcoded to "https://plane.so/" which works for production but might need to be configurable for different environments (staging, preview, development). Verify whether this is intentional or if the URL should be derived from environment variables or window.location.

If environment-specific URLs are needed, consider using an environment variable:

const APP_URL = process.env.APP_URL || "https://plane.so/";

export const meta: Route.MetaFunction = () => [
  // ... other meta tags
  { property: "og:url", content: APP_URL },
  // ...
];

55-57: LGTM! Root component correctly renders Outlet.

The default Root component follows the expected React Router pattern by simply rendering an Outlet for nested routes.

apps/api/plane/license/api/views/admin.py (1)

232-232: Trailing slashes are consistently applied to all admin auth redirects
Both urljoin(..., is_admin=True) calls in admin.py now use "general/"; no other admin redirect paths require adjustment.

apps/admin/app/compat/next/navigation.ts (2)

7-24: LGTM! Solid shim implementation.

The useRouter hook correctly adapts React Router's navigation API to match Next.js conventions. The memoization with [navigate] dependency is appropriate, and the use of ensureTrailingSlash maintains consistency with Next.js routing behavior.


15-17: No usages of refresh() found—ignore this shim.

No call sites reference this refresh() implementation in the admin app; the hard-vs-soft refresh distinction has no impact.

Likely an incorrect or invalid review comment.

apps/admin/app/compat/next/helper.ts (1)

6-33: Verify query-only and hash-only URL handling.
ensureTrailingSlash("?foo=bar") and ensureTrailingSlash("#section") both return full dummy URLs (http://dummy.com?foo=bar, http://dummy.com#section) instead of "/?foo=bar" or "/#section". Confirm whether your routing ever uses query-only or hash-only URLs; if so, add explicit checks to preserve the leading /.

Comment on lines +137 to 139
base_host(request=request, is_admin=True, ),

"?" + urlencode(exc.get_error_dict()),
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

Fix the inconsistent formatting.

The trailing comma and empty line create unnecessary clutter and are inconsistent with other similar urljoin calls in the file (e.g., lines 98-100, 160-162).

Apply this diff to clean up the formatting:

-            url = urljoin(
-                base_host(request=request, is_admin=True, ),
-                
-                "?" + urlencode(exc.get_error_dict()),
-            )
+            url = urljoin(
+                base_host(request=request, is_admin=True),
+                "?" + urlencode(exc.get_error_dict()),
+            )
📝 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
base_host(request=request, is_admin=True, ),
"?" + urlencode(exc.get_error_dict()),
url = urljoin(
base_host(request=request, is_admin=True),
"?" + urlencode(exc.get_error_dict()),
)
🤖 Prompt for AI Agents
In apps/api/plane/license/api/views/admin.py around lines 137 to 139, clean up
inconsistent formatting in the urljoin call: remove the trailing comma after
base_host(...) and the extra blank line so the arguments match the style used
elsewhere (e.g., lines 98-100, 160-162); ensure the third argument remains on
the same logical block as the preceding call and there are no stray commas or
blank lines between arguments.

cursor[bot]

This comment was marked as outdated.

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: 2

♻️ Duplicate comments (3)
apps/admin/vite.config.ts (2)

1-4: Define __dirname in ESM configs.

__dirname is referenced below but never defined in this ESM file (Node sets "type":"module"). This will throw at runtime; import fileURLToPath from node:url and derive __dirname before use.


32-34: Avoid overwriting process.env.

Replacing the entire process.env object with a JSON string breaks every process.env.KEY lookup across the admin app. Map each key individually (or migrate to import.meta.env) so property accesses still work.

apps/admin/nginx/nginx.conf (1)

12-14: Duplicate: Real IP configuration issues already flagged.

Past review comments have already identified critical security issues here: trusting all client IPs (set_real_ip_from 0.0.0.0/0;) allows IP spoofing, and the header name has a typo (X-Forward-For should be X-Forwarded-For). These issues must be addressed as noted in previous feedback.

🧹 Nitpick comments (2)
apps/admin/nginx/nginx.conf (2)

1-1: Consider using auto for worker processes.

The worker processes are hardcoded to 4. Using auto allows nginx to detect the number of available CPU cores, improving portability across different deployment environments.

-worker_processes 4;
+worker_processes auto;

7-11: Enable gzip compression for better performance.

Gzip compression is not configured. Enabling it would reduce bandwidth usage and improve load times for static assets (HTML, CSS, JS).

Add gzip configuration in the http block:

 http {
   include mime.types;
 
   default_type  application/octet-stream;
+
+  # Enable gzip compression
+  gzip on;
+  gzip_vary on;
+  gzip_min_length 1024;
+  gzip_types text/plain text/css text/xml text/javascript application/javascript application/json application/xml+rss;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2894119 and a48c9fd.

📒 Files selected for processing (2)
  • apps/admin/nginx/nginx.conf (1 hunks)
  • apps/admin/vite.config.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
apps/admin/vite.config.ts (1)
apps/admin/server.mjs (1)
  • __dirname (8-8)
⏰ 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). (2)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: Analyze (javascript)

set_real_ip_from 0.0.0.0/0;
real_ip_recursive on;
real_ip_header X-Forward-For;
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
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

Apply the rate limit zone or remove the unused declaration.

The rate limiting zone mylimit is defined but never applied with a limit_req directive in any location block. This declaration allocates 10MB of memory without providing any protection against DoS or brute force attacks.

Either apply the zone in your location block:

   location / {
     root   /usr/share/nginx/html;
     index  index.html index.htm;
+    limit_req zone=mylimit burst=20 nodelay;
     try_files $uri $uri/ /index.html;
   }

Or remove the unused zone definition if rate limiting is not needed:

- limit_req_zone          $binary_remote_addr zone=mylimit:10m rate=10r/s;
📝 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
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
limit_req zone=mylimit burst=20 nodelay;
try_files $uri $uri/ /index.html;
}
🤖 Prompt for AI Agents
In apps/admin/nginx/nginx.conf around line 15, the rate limit zone "mylimit" is
declared but never applied; either apply it with a limit_req directive in the
appropriate location/server blocks (e.g., add limit_req zone=mylimit burst=...
nodelay or similar to the location(s) you want protected) or remove the
limit_req_zone declaration to avoid allocating the 10m zone when unused.

Comment on lines +20 to +28
server {
listen 3000;

location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
}
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 security headers for defense-in-depth.

The server block lacks security headers that protect against XSS, clickjacking, MIME-sniffing, and other attacks. For an admin application, these headers are critical for defense-in-depth.

Add security headers inside the location / block:

   location / {
     root   /usr/share/nginx/html;
     index  index.html index.htm;
+    # Security headers
+    add_header X-Frame-Options "DENY" always;
+    add_header X-Content-Type-Options "nosniff" always;
+    add_header X-XSS-Protection "1; mode=block" always;
+    add_header Referrer-Policy "strict-origin-when-cross-origin" always;
+    add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';" always;
     try_files $uri $uri/ /index.html;
   }

Note: Adjust CSP directives based on your actual resource requirements.

📝 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
server {
listen 3000;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html;
}
}
server {
listen 3000;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
# Security headers
add_header X-Frame-Options "DENY" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; style-src 'self' 'unsafe-inline';" always;
try_files $uri $uri/ /index.html;
}
}
🤖 Prompt for AI Agents
In apps/admin/nginx/nginx.conf around lines 20 to 28, the server block currently
serves the admin UI but lacks security response headers; add header directives
inside the location / block to set at minimum: X-Frame-Options: DENY (prevent
clickjacking), X-Content-Type-Options: nosniff (prevent MIME sniffing),
X-XSS-Protection: 1; mode=block or a modern equivalent, Referrer-Policy:
no-referrer-when-downgrade (or stricter), Strict-Transport-Security with an
appropriate max-age and includeSubDomains if TLS is used, and
Content-Security-Policy with a restrictive default-src and explicit
script/style/resource sources (use a placeholder CSP that you will tune to match
the app’s actual resources); add these header lines using the nginx "add_header"
directive and ensure they apply to responses (use always if needed) and that CSP
is tailored to the app before deploying.

cursor[bot]

This comment was marked as outdated.

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: 1

♻️ Duplicate comments (1)
apps/admin/package.json (1)

41-41: Remove the Express 4 typings when adopting Express 5.

express is now pinned to ^5.1.0, but the dev dependency still pulls in @types/[email protected]. Those legacy types model the Express 4 API and will clash with the runtime’s built-in v5 declarations, producing incorrect typings (and potential build breaks) as soon as you touch v5 APIs. Either downgrade back to Express 4 or, preferably, drop the DefinitelyTyped package so TypeScript consumes the v5-provided types (or replace it with the matching v5 typings package if you need one). Please update package.json accordingly and regenerate the lockfile.

   "express": "^5.1.0",
 ...
-    "@types/express": "4.17.23",

Also applies to: 71-71

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4b2cc27 and 57e324e.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (4)
  • apps/admin/package.json (2 hunks)
  • packages/propel/package.json (1 hunks)
  • packages/services/package.json (2 hunks)
  • packages/ui/package.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/propel/package.json
⏰ 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). (2)
  • GitHub Check: Cursor Bugbot
  • GitHub Check: Analyze (javascript)

Comment on lines +43 to +45
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.cts"
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

Fix the types entry to match the emitted declarations.

tsdown still emits dist/index.d.ts; switching the manifest to dist/index.d.cts makes TypeScript consumers fail resolution immediately. Please keep the types field pointed at the actual .d.ts output (or update the build to emit .d.cts before flipping this).

Apply this diff:

-  "types": "./dist/index.d.cts"
+  "types": "./dist/index.d.ts"
📝 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
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.cts"
"main": "./dist/index.cjs",
"module": "./dist/index.js",
"types": "./dist/index.d.ts"
🤖 Prompt for AI Agents
In packages/services/package.json around lines 43 to 45, the "types" field
incorrectly points to "./dist/index.d.cts" while the build emits
"./dist/index.d.ts"; change the "types" entry to "./dist/index.d.ts" (or
alternatively update the build to emit .d.cts before changing the manifest) so
TypeScript consumers can resolve declarations correctly.

@lifeiscontent lifeiscontent force-pushed the feat/admin-react-router-migration branch 2 times, most recently from 1727e32 to 5e336af Compare October 18, 2025 10:27
@lifeiscontent lifeiscontent force-pushed the feat/admin-react-router-migration branch from 5e336af to 0eda662 Compare October 18, 2025 10:29
@lifeiscontent lifeiscontent self-assigned this Oct 18, 2025
@makeplane
Copy link

makeplane bot commented Oct 18, 2025

Linked to Plane Work Item(s)

References

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: 4

♻️ Duplicate comments (16)
apps/api/plane/license/api/views/admin.py (1)

137-138: Fix the formatting inconsistency (already flagged).

The trailing comma after is_admin=True, and the empty line between function arguments have already been identified by previous reviewers as creating unnecessary clutter and inconsistent formatting.

Apply the fix suggested in previous reviews:

-            url = urljoin(
-                base_host(request=request, is_admin=True, ),
-                
-                "?" + urlencode(exc.get_error_dict()),
-            )
+            url = urljoin(
+                base_host(request=request, is_admin=True),
+                "?" + urlencode(exc.get_error_dict()),
+            )
apps/admin/core/components/instance/instance-not-ready.tsx (1)

1-9: Clarify Next.js compatibility layer approach.

Previous reviews flagged the "use client" directive (line 1) and Next.js imports from next/image and next/link (lines 3-4) as critical issues requiring removal for React Router compatibility. These comments were marked as "✅ Addressed in commits 1a9ebc8 to 1727e32", yet the current code retains all these Next.js-specific patterns.

If compatibility shims now handle these imports (per the PR description mentioning "compatibility layers for next/link and next/image utilities"), please confirm the shim implementations are working correctly. Otherwise, these represent regressions from previously addressed issues.

Run the following to verify if compatibility shims exist and are properly configured:

#!/bin/bash
# Description: Verify Next.js compatibility shims and their configuration

# Check for next/link and next/image module definitions
echo "Searching for Next.js compatibility shim files..."
fd --type f "(link|image)" apps/admin/core/lib apps/admin/core/components --exec rg -l "next/(link|image)"

# Look for Vite alias or resolve configuration
echo -e "\nChecking Vite configuration for module aliases..."
cat apps/admin/vite.config.ts 2>/dev/null || cat apps/admin/vite.config.js 2>/dev/null

# Search for actual shim implementations
echo -e "\nSearching for shim implementation exports..."
ast-grep --pattern 'export default function Image($$$) { $$$ }'
ast-grep --pattern 'export default function Link($$$) { $$$ }'
apps/admin/core/components/instance/failure.tsx (1)

1-1: Remove Next.js-specific "use client" directive.

The "use client" directive is specific to Next.js and serves no purpose in a React Router + Vite environment. It should be removed as part of the migration.

Apply this diff to remove the directive:

-"use client";
 import { observer } from "mobx-react";
apps/admin/ce/components/authentication/authentication-modes.tsx (1)

101-101: Revert to explicit React.FC annotation to match codebase patterns.

As noted in a previous review, the observer typing pattern should use explicit React.FC annotation rather than a generic type parameter to maintain consistency across the codebase.

Apply this diff to revert to the prevailing pattern:

-export const AuthenticationModes = observer<React.FC<TAuthenticationModeProps>>((props) => {
+export const AuthenticationModes: React.FC<TAuthenticationModeProps> = observer((props) => {
apps/admin/app/(all)/(dashboard)/workspace/create/page.tsx (1)

8-8: Incorrect observer generic breaks typing.

The generic type parameter on observer doesn't match the arrow function pattern. Type the component variable itself instead.

Apply this diff:

-const WorkspaceCreatePage = observer<React.FC<Route.ComponentProps>>(() => (
+const WorkspaceCreatePage: React.FC<Route.ComponentProps> = observer(() => (
apps/admin/app/(all)/(dashboard)/ai/page.tsx (1)

12-12: Incorrect observer generic breaks typing.

The generic type parameter on observer doesn't match the arrow function pattern. Type the component variable itself instead.

Apply this diff:

-const InstanceAIPage = observer<React.FC<Route.ComponentProps>>(() => {
+const InstanceAIPage: React.FC<Route.ComponentProps> = observer(() => {
apps/admin/Dockerfile.admin (2)

76-79: Critical: Environment variables needed for nginx template substitution.

The production stage starts fresh from nginx:1.27-alpine and doesn't inherit the ENV declarations from the installer stage (lines 31-59). If the nginx configuration template at /etc/nginx/nginx.conf (line 78) uses variable substitution for NEXT_PUBLIC_* values, these must be declared in the production stage.

Add the required environment variables in the production stage:

 FROM nginx:1.27-alpine AS production
+
+ARG NEXT_PUBLIC_API_BASE_URL=""
+ENV NEXT_PUBLIC_API_BASE_URL=$NEXT_PUBLIC_API_BASE_URL
+ARG NEXT_PUBLIC_API_BASE_PATH="/api"
+ENV NEXT_PUBLIC_API_BASE_PATH=$NEXT_PUBLIC_API_BASE_PATH
+ARG NEXT_PUBLIC_ADMIN_BASE_URL=""
+ENV NEXT_PUBLIC_ADMIN_BASE_URL=$NEXT_PUBLIC_ADMIN_BASE_URL
+ARG NEXT_PUBLIC_ADMIN_BASE_PATH="/god-mode"
+ENV NEXT_PUBLIC_ADMIN_BASE_PATH=$NEXT_PUBLIC_ADMIN_BASE_PATH
 
 COPY apps/admin/nginx/nginx.conf /etc/nginx/nginx.conf

83-84: Install curl before using it in healthcheck.

The nginx:1.27-alpine base image doesn't include curl, causing the healthcheck to fail with exit 127.

Add curl to the production stage:

 FROM nginx:1.27-alpine AS production
+
+RUN apk add --no-cache curl
 
 COPY apps/admin/nginx/nginx.conf /etc/nginx/nginx.conf
apps/admin/vite.config.ts (2)

1-4: Define __dirname for ESM to avoid runtime ReferenceError.

__dirname is used later but not defined under "type": "module". Add the ESM equivalent.

 import path from "node:path";
+import { fileURLToPath } from "node:url";
 import { reactRouter } from "@react-router/dev/vite";
 import { defineConfig } from "vite";
 import tsconfigPaths from "vite-tsconfig-paths";
 
+const __dirname = path.dirname(fileURLToPath(import.meta.url));

31-34: Don’t replace the entire process.env; map individual keys (or migrate to import.meta.env).

Replacing process.env wholesale is brittle and can break code expecting an object. Define per‑key replacements instead.

-    define: {
-      "process.env": JSON.stringify(publicEnv),
-    },
+    define: Object.entries(publicEnv).reduce((acc, [key, value]) => {
+      acc[`process.env.${key}`] = JSON.stringify(value);
+      return acc;
+    }, {} as Record<string, string>),

Optional (recommended): adopt Vite’s envs for new code and keep the above only for legacy:

  • Add envPrefix: ["NEXT_PUBLIC_"] at the top level.
  • Use import.meta.env.NEXT_PUBLIC_* going forward.
packages/services/package.json (1)

45-45: Resolve the types field mismatch (flagged previously).

This was raised in the prior review: tsdown emits dist/index.d.ts, but the manifest points to dist/index.d.cts. This breaks TypeScript consumer resolution. Either keep types pointed at the actual emitted file (./dist/index.d.ts) or update the build to emit .d.cts before flipping this field.

Apply this diff to match the actual build output:

-  "types": "./dist/index.d.cts"
+  "types": "./dist/index.d.ts"
apps/admin/app/(all)/(dashboard)/image/page.tsx (1)

12-12: Stop passing the component type into observer.

observer<React.FC<Route.ComponentProps>> makes the component expect a React.FC prop and breaks type checking. Annotate the variable and call observer plainly.

Apply this diff:

-const InstanceImagePage = observer<React.FC<Route.ComponentProps>>(() => {
+const InstanceImagePage: React.FC<Route.ComponentProps> = observer(() => {
apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx (1)

19-19: Stop passing the component type into observer.

observer<React.FC<Route.ComponentProps>> makes the component expect a React.FC prop and breaks type checking. Annotate the variable and call observer plainly.

Apply this diff:

-const InstanceGitlabAuthenticationPage = observer<React.FC<Route.ComponentProps>>(() => {
+const InstanceGitlabAuthenticationPage: React.FC<Route.ComponentProps> = observer(() => {
apps/admin/server/app.ts (1)

27-36: Previous review comment remains unaddressed.

The secure: false setting on line 32 disables TLS certificate validation, creating a MITM vulnerability in production. This issue was flagged in a previous review but has not been resolved.

apps/admin/server.mjs (1)

26-26: Critical: PORT derivation already flagged by previous reviewers

This line is a duplicate of issues already raised. When NEXT_PUBLIC_ADMIN_BASE_URL omits an explicit port, parsedBaseUrl.port becomes an empty string, causing Number.parseInt to return NaN and server startup to fail.

Refer to the existing review comments above for the recommended fix that checks process.env.PORT before falling back to the parsed URL port and then to a default value.

apps/admin/package.json (1)

41-41: Express v5 runtime with v4 type definitions—mismatch still unresolved.

Line 41 pins express@^5.1.0, but line 71 still references @types/[email protected] (Express 4 types). This mismatch will cause incorrect typings and type errors. Align the version pair: either downgrade to [email protected] (with matching types) or fully adopt v5 by removing @types/express and relying on Express v5's built-in or native type exports.

Verify the correct v5 typing story (run npm info [email protected] to check if v5 ships types internally or if there's a separate @types/[email protected] package), then update accordingly.

Also applies to: 71-71

🧹 Nitpick comments (12)
.gitignore (1)

106-107: Remove duplicate build/ pattern.

Line 106 duplicates the build/ pattern already present at line 21, reducing clarity. Line 107 correctly adds .react-router/ for the new React Router workspace.

Apply this diff to remove the duplicate:

-build/
-.react-router/
+.react-router/
packages/propel/tsdown.config.ts (1)

42-45: Consider adding inline documentation for the dual CSS export pattern.

The explicit .css extension exports (lines 42, 44) alongside the extension-less variants (lines 43, 45) serve a valid purpose for Vite compatibility, as noted in the past review. However, to aid future maintainers, consider adding a brief inline comment explaining why both variants are necessary.

Apply this diff to add clarifying documentation:

     customExports: (exports) => ({
       ...exports,
+      // Explicit .css extensions required for Vite import compatibility
       "./styles/fonts.css": "./dist/styles/fonts/index.css",
       "./styles/fonts": "./dist/styles/fonts/index.css",
       "./styles/react-day-picker.css": "./dist/styles/react-day-picker.css",
       "./styles/react-day-picker": "./dist/styles/react-day-picker.css",
     }),
packages/ui/package.json (1)

36-107: Consider documenting dependency rationale for React Router migration context.

With ~70+ new dependencies added as part of the React Router migration, it would be beneficial to add a comment or accompanying documentation explaining:

  1. Which dependencies are essential for the React Router + Vite architecture
  2. Which are UI framework upgrades (Radix UI expansion)
  3. Which support code generation or utility functions
  4. Any breaking changes from the Next.js architecture that required these additions

This helps future maintainers understand the migration's impact on the dependency tree.

apps/admin/app/(all)/(home)/page.tsx (1)

1-1: Remove obsolete "use client" directive.

This directive is a Next.js React Server Components feature that has no effect in React Router. Consider removing it as part of the migration cleanup.

-"use client";
-
 import { observer } from "mobx-react";
apps/admin/vite.config.ts (1)

6-25: Const‑type the public env key list for stronger types and safer refactors.

Minor improvement: const‑typing the keys enables better inference and avoids accidental typos.

-const PUBLIC_ENV_KEYS = [
+const PUBLIC_ENV_KEYS = [
   "NEXT_PUBLIC_API_BASE_URL",
   "NEXT_PUBLIC_API_BASE_PATH",
   "NEXT_PUBLIC_ADMIN_BASE_URL",
   "NEXT_PUBLIC_ADMIN_BASE_PATH",
   "NEXT_PUBLIC_SPACE_BASE_URL",
   "NEXT_PUBLIC_SPACE_BASE_PATH",
   "NEXT_PUBLIC_LIVE_BASE_URL",
   "NEXT_PUBLIC_LIVE_BASE_PATH",
   "NEXT_PUBLIC_WEB_BASE_URL",
   "NEXT_PUBLIC_WEB_BASE_PATH",
   "NEXT_PUBLIC_WEBSITE_URL",
   "NEXT_PUBLIC_SUPPORT_EMAIL",
-];
+] as const;
 
-const publicEnv = PUBLIC_ENV_KEYS.reduce<Record<string, string>>((acc, key) => {
+const publicEnv = (PUBLIC_ENV_KEYS as readonly string[]).reduce<Record<string, string>>((acc, key) => {
   acc[key] = process.env[key] ?? "";
   return acc;
 }, {});
apps/admin/app/components/404.tsx (1)

9-9: Remove unnecessary template literal backticks.

The backticks serve no purpose here since there's no string interpolation.

Apply this diff:

-  <div className={`h-screen w-full overflow-hidden bg-custom-background-100`}>
+  <div className="h-screen w-full overflow-hidden bg-custom-background-100">
apps/admin/server/app.ts (3)

1-1: Remove unnecessary side-effect import.

The side-effect import of "react-router" on line 1 appears unnecessary—React Router SSR does not require it, and you're already importing the needed exports from @react-router/express on line 2.

Apply this diff to remove the side-effect import:

-import "react-router";
 import { createRequestHandler } from "@react-router/express";

7-9: Consider validating the API base URL.

The environment variable NEXT_PUBLIC_API_BASE_URL is normalized but not validated. A malformed URL could cause the proxy to fail at runtime. Consider validating the URL format during startup (e.g., with new URL()) and exiting with a clear error message if invalid.

Example validation:

const NEXT_PUBLIC_API_BASE_URL = (() => {
  const raw = process.env.NEXT_PUBLIC_API_BASE_URL || "http://127.0.0.1:8000";
  const normalized = raw.replace(/\/$/, "");
  try {
    new URL(normalized); // Validate URL format
    return normalized;
  } catch (err) {
    throw new Error(`Invalid NEXT_PUBLIC_API_BASE_URL: ${raw}`);
  }
})();

40-44: Add error handling for the SSR request handler.

If the virtual module import fails or the React Router request handler throws an error, the server will crash. Consider wrapping the request handler with error handling middleware to gracefully handle SSR failures and return appropriate error responses to clients.

Example error boundary:

router.use(
  createRequestHandler({
    build: () => import("virtual:react-router/server-build"),
  })
);

// Add error handler after the SSR handler
router.use((err: Error, req: express.Request, res: express.Response, next: express.NextFunction) => {
  console.error("SSR error:", err);
  res.status(500).send("Internal Server Error");
});
apps/admin/server.mjs (1)

58-65: Consider absolute paths for build artifacts

The production mode static asset serving and app import use relative paths ("build/client/assets", "build/client", and BUILD_PATH = "./build/server/index.js"). If the Node process is started from a directory other than apps/admin, these imports will fail.

Since line 10 already uses __dirname for the .env path, consider making BUILD_PATH and static asset paths absolute for consistency and reliability.

-const BUILD_PATH = "./build/server/index.js";
+const BUILD_PATH = path.resolve(__dirname, "build/server/index.js");

And in production mode:

-    app.use("/assets", express.static("build/client/assets", { immutable: true, maxAge: "1y" }));
+    app.use("/assets", express.static(path.resolve(__dirname, "build/client/assets"), { immutable: true, maxAge: "1y" }));
     app.use(morgan("tiny"));
-    app.use(express.static("build/client", { maxAge: "1h" }));
+    app.use(express.static(path.resolve(__dirname, "build/client"), { maxAge: "1h" }));
apps/admin/package.json (2)

33-33: Clarify the need for @vercel/[email protected].

Since the migration is shifting away from Vercel's infrastructure (Next.js) to a static Vite + nginx model, this package seems incongruous. Is it still actively used, or is it legacy from the Next.js era? If unused, remove it to reduce the dependency surface.


29-31: Verify React Router and Vite version constraints.

The React Router (^7.9.1, ^7.9.3) and Vite (7.1.7) versions are recent but pinned or loosely constrained. Ensure they are tested and stable for your monorepo's build pipeline; monitor for any peer dependency conflicts with React (catalog:), TypeScript, and other core packages. Also verify that @react-router/dev@^7.9.1 (line 69) matches the runtime versions (@react-router/express and @react-router/node at lines 29–30).

Also applies to: 56-57, 69-69, 78-79

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between bf6fdef and 0eda662.

⛔ Files ignored due to path filters (22)
  • apps/admin/app/assets/favicon/apple-touch-icon.png is excluded by !**/*.png
  • apps/admin/app/assets/favicon/favicon-16x16.png is excluded by !**/*.png
  • apps/admin/app/assets/favicon/favicon-32x32.png is excluded by !**/*.png
  • apps/admin/app/assets/favicon/favicon.ico is excluded by !**/*.ico
  • apps/admin/app/assets/images/404.svg is excluded by !**/*.svg
  • apps/admin/app/assets/images/logo-spinner-dark.gif is excluded by !**/*.gif
  • apps/admin/app/assets/images/logo-spinner-light.gif is excluded by !**/*.gif
  • apps/admin/app/assets/images/plane-takeoff.png is excluded by !**/*.png
  • apps/admin/app/assets/instance/instance-failure-dark.svg is excluded by !**/*.svg
  • apps/admin/app/assets/instance/instance-failure.svg is excluded by !**/*.svg
  • apps/admin/app/assets/logos/github-black.png is excluded by !**/*.png
  • apps/admin/app/assets/logos/github-white.png is excluded by !**/*.png
  • apps/admin/app/assets/logos/gitlab-logo.svg is excluded by !**/*.svg
  • apps/admin/app/assets/logos/google-logo.svg is excluded by !**/*.svg
  • apps/admin/app/assets/logos/oidc-logo.svg is excluded by !**/*.svg
  • apps/admin/app/assets/logos/saml-logo.svg is excluded by !**/*.svg
  • apps/admin/app/assets/logos/takeoff-icon-dark.svg is excluded by !**/*.svg
  • apps/admin/app/assets/logos/takeoff-icon-light.svg is excluded by !**/*.svg
  • apps/admin/public/auth/background-pattern-dark.svg is excluded by !**/*.svg
  • apps/admin/public/auth/background-pattern.svg is excluded by !**/*.svg
  • apps/admin/public/instance/plane-takeoff.png is excluded by !**/*.png
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (78)
  • .dockerignore (1 hunks)
  • .gitignore (1 hunks)
  • apps/admin/.dockerignore (1 hunks)
  • apps/admin/.eslintrc.cjs (2 hunks)
  • apps/admin/Dockerfile.admin (1 hunks)
  • apps/admin/app/(all)/(dashboard)/ai/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/ai/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/github/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/gitlab/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/google/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/authentication/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/email/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/email/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/general/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/general/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/image/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/image/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/layout.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/workspace/create/page.tsx (2 hunks)
  • apps/admin/app/(all)/(dashboard)/workspace/layout.tsx (0 hunks)
  • apps/admin/app/(all)/(dashboard)/workspace/page.tsx (2 hunks)
  • apps/admin/app/(all)/(home)/auth-helpers.tsx (2 hunks)
  • apps/admin/app/(all)/(home)/layout.tsx (1 hunks)
  • apps/admin/app/(all)/(home)/page.tsx (2 hunks)
  • apps/admin/app/compat/next/helper.ts (1 hunks)
  • apps/admin/app/compat/next/image.tsx (1 hunks)
  • apps/admin/app/compat/next/link.tsx (1 hunks)
  • apps/admin/app/compat/next/navigation.ts (1 hunks)
  • apps/admin/app/components/404.tsx (1 hunks)
  • apps/admin/app/error.tsx (0 hunks)
  • apps/admin/app/layout.tsx (0 hunks)
  • apps/admin/app/providers.tsx (1 hunks)
  • apps/admin/app/root.tsx (1 hunks)
  • apps/admin/app/routes.ts (1 hunks)
  • apps/admin/app/types/next-image.d.ts (1 hunks)
  • apps/admin/app/types/next-link.d.ts (1 hunks)
  • apps/admin/app/types/next-navigation.d.ts (1 hunks)
  • apps/admin/app/types/react-router-virtual.d.ts (1 hunks)
  • apps/admin/ce/components/authentication/authentication-modes.tsx (2 hunks)
  • apps/admin/core/components/common/logo-spinner.tsx (1 hunks)
  • apps/admin/core/components/instance/failure.tsx (1 hunks)
  • apps/admin/core/components/instance/instance-not-ready.tsx (1 hunks)
  • apps/admin/core/components/instance/loading.tsx (1 hunks)
  • apps/admin/core/components/new-user-popup.tsx (1 hunks)
  • apps/admin/middleware.js (1 hunks)
  • apps/admin/next-env.d.ts (0 hunks)
  • apps/admin/next.config.js (0 hunks)
  • apps/admin/nginx/nginx.conf (1 hunks)
  • apps/admin/package.json (2 hunks)
  • apps/admin/public/.well-known/appspecific/com.chrome.devtools.json (1 hunks)
  • apps/admin/public/favicon/site.webmanifest (0 hunks)
  • apps/admin/react-router.config.ts (1 hunks)
  • apps/admin/server.mjs (1 hunks)
  • apps/admin/server/app.ts (1 hunks)
  • apps/admin/styles/globals.css (1 hunks)
  • apps/admin/tsconfig.json (1 hunks)
  • apps/admin/vite.config.ts (1 hunks)
  • apps/api/plane/license/api/views/admin.py (3 hunks)
  • apps/live/package.json (1 hunks)
  • apps/web/package.json (0 hunks)
  • packages/decorators/package.json (1 hunks)
  • packages/editor/package.json (1 hunks)
  • packages/editor/tsdown.config.ts (1 hunks)
  • packages/eslint-config/package.json (1 hunks)
  • packages/i18n/package.json (1 hunks)
  • packages/logger/package.json (1 hunks)
  • packages/propel/package.json (1 hunks)
  • packages/propel/tsdown.config.ts (1 hunks)
  • packages/services/package.json (2 hunks)
  • packages/shared-state/package.json (1 hunks)
  • packages/ui/package.json (1 hunks)
  • packages/utils/package.json (1 hunks)
  • packages/utils/src/string.ts (1 hunks)
  • pnpm-workspace.yaml (1 hunks)
💤 Files with no reviewable changes (15)
  • apps/admin/app/(all)/(dashboard)/authentication/github/layout.tsx
  • apps/admin/app/error.tsx
  • apps/admin/app/(all)/(dashboard)/authentication/google/layout.tsx
  • apps/admin/app/(all)/(dashboard)/authentication/layout.tsx
  • apps/admin/app/(all)/(dashboard)/general/layout.tsx
  • apps/admin/next.config.js
  • apps/admin/app/(all)/(dashboard)/authentication/gitlab/layout.tsx
  • apps/admin/app/layout.tsx
  • apps/admin/public/favicon/site.webmanifest
  • apps/admin/app/(all)/(dashboard)/ai/layout.tsx
  • apps/admin/next-env.d.ts
  • apps/admin/app/(all)/(dashboard)/email/layout.tsx
  • apps/web/package.json
  • apps/admin/app/(all)/(dashboard)/image/layout.tsx
  • apps/admin/app/(all)/(dashboard)/workspace/layout.tsx
✅ Files skipped from review due to trivial changes (6)
  • .dockerignore
  • apps/admin/public/.well-known/appspecific/com.chrome.devtools.json
  • packages/utils/src/string.ts
  • packages/shared-state/package.json
  • apps/admin/styles/globals.css
  • packages/i18n/package.json
🚧 Files skipped from review as they are similar to previous changes (27)
  • apps/admin/.dockerignore
  • apps/admin/app/(all)/(home)/layout.tsx
  • apps/admin/app/providers.tsx
  • apps/admin/app/(all)/(dashboard)/authentication/github/page.tsx
  • apps/admin/app/compat/next/image.tsx
  • apps/admin/app/(all)/(dashboard)/workspace/page.tsx
  • packages/editor/package.json
  • apps/admin/app/(all)/(home)/auth-helpers.tsx
  • apps/admin/app/types/react-router-virtual.d.ts
  • apps/admin/app/(all)/(dashboard)/authentication/google/page.tsx
  • apps/admin/middleware.js
  • apps/admin/.eslintrc.cjs
  • apps/admin/core/components/common/logo-spinner.tsx
  • apps/admin/app/types/next-image.d.ts
  • apps/admin/app/types/next-navigation.d.ts
  • packages/logger/package.json
  • packages/decorators/package.json
  • apps/admin/app/(all)/(dashboard)/general/page.tsx
  • apps/admin/app/(all)/(dashboard)/authentication/page.tsx
  • apps/admin/nginx/nginx.conf
  • packages/propel/package.json
  • apps/admin/app/root.tsx
  • apps/admin/app/(all)/(dashboard)/email/page.tsx
  • apps/admin/app/compat/next/link.tsx
  • apps/admin/core/components/new-user-popup.tsx
  • apps/admin/app/(all)/(dashboard)/layout.tsx
  • packages/editor/tsdown.config.ts
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-09T20:42:31.843Z
Learnt from: lifeiscontent
PR: makeplane/plane#7922
File: apps/admin/app/(all)/(dashboard)/ai/form.tsx:19-19
Timestamp: 2025-10-09T20:42:31.843Z
Learning: In the makeplane/plane repository, React types are globally available through TypeScript configuration. Type annotations like React.FC, React.ReactNode, etc. can be used without explicitly importing the React namespace. The codebase uses the modern JSX transform, so React imports are not required for JSX or type references.

Applied to files:

  • apps/admin/core/components/instance/failure.tsx
🔇 Additional comments (40)
packages/utils/package.json (1)

30-30: Verify dompurify compatibility across all usage contexts in this utils package.

The dependency change from isomorphic-dompurify (browser + Node.js compatible) to dompurify (browser-focused) represents a significant shift. dompurify requires DOM APIs and will not work in Node.js server contexts without polyfills. Given that this is a utilities package, verify:

  1. Whether packages/utils is now exclusively used in client-side/browser contexts (consistent with the Vite/React Router migration)
  2. That no code in packages/utils/src/string.ts or elsewhere is invoked on the server side after this migration
  3. That version 3.2.7 is a stable release without known vulnerabilities
apps/live/package.json (1)

56-56: Verify intentionality of version constraint change outside PR scope.

The caret operator is removed from @types/express, pinning the version to an exact release rather than allowing compatible semver updates. While pinning type package versions is a sound practice for reproducibility, this change appears orthogonal to the PR objectives (admin React Router migration).

Confirm this is intentional and part of a broader dependency pinning effort across the monorepo, rather than accidental scope creep.

packages/eslint-config/package.json (1)

16-20: Validate major version bumps in ESLint packages.

Several ESLint-related dependencies have been bumped to new major versions, which may introduce breaking changes:

  • eslint-config-prettier: 9.x → 10.x
  • eslint-config-turbo: 1.x → 2.x
  • eslint-plugin-react-hooks: 5.x → 6.x

These upgrades align with the React Router migration, but verify that lint rules and configurations remain compatible and that linting passes without new errors or deprecation warnings.

Run linting with the updated ESLint tooling across the codebase to ensure no new errors or warnings are introduced:

packages/propel/tsdown.config.ts (1)

40-41: LGTM! Parameter rename improves clarity.

The parameter rename from out to exports better reflects the purpose of the parameter and improves code readability.

packages/ui/package.json (3)

39-39: Verify necessity of utility library proliferation and consider consolidation.

The dependencies include many single-purpose utility libraries (camel-case, capital-case, constant-case, no-case, param-case, pascal-case, path-case, sentence-case, snake-case, upper-case, upper-case-first) that provide case conversion functionality. This represents significant potential for:

  1. Duplication: lodash (line 79) already provides string casing utilities and covers many of these use cases.
  2. Bundle bloat: These single-purpose packages add overhead that might be consolidatable.
  3. Maintenance complexity: Multiple overlapping dependencies increase maintenance burden.

If these case conversion utilities are truly necessary, consider documenting why lodash utilities are insufficient. Otherwise, refactor to use lodash's built-in case functions.

Also applies to: 64-106


49-49: Verify @plane/propel dependency is necessary.

The new workspace dependency @plane/propel is added but no context is provided about its purpose or whether it's actually imported in the UI package. This could be:

  1. A build-time-only dependency (should be in devDependencies)
  2. An accidental/duplicate addition
  3. A legitimate runtime dependency that needs clarification

Ensure this dependency is actively used in the UI package code.


53-63: Verify Radix UI sub-package coverage is complete and intentional.

Multiple granular Radix UI sub-packages are added individually (react-compose-refs, react-context, react-direction, react-presence, react-primitive, react-slot, react-use-callback-ref, react-use-layout-effect). These are typically auto-installed as dependencies of higher-level Radix UI components but adding them explicitly suggests either:

  1. Direct usage: The code directly imports from these low-level packages (check for this)
  2. Version pinning: Explicit versions are needed to avoid conflicts (verify if versions differ from transitive dependencies)
  3. Redundancy: They're included unnecessarily (should be removed)

Consider documenting why these low-level Radix primitives are pinned explicitly in this package rather than relying on Radix UI component dependencies.

apps/api/plane/license/api/views/admin.py (1)

232-232: Verify consistent trailing slash usage across all admin redirects.

The addition of trailing slashes to the "general/" redirect paths aligns with the React Router migration and URL normalization goals of this PR.

Please verify that all admin redirect URLs in this file consistently use trailing slashes. Run the following script to check for other urljoin calls with admin redirects:

Also applies to: 351-351

apps/admin/app/(all)/(home)/page.tsx (1)

11-11: LGTM! Meta function follows React Router pattern.

The type import and meta function export correctly implement React Router's route metadata pattern. The title and description appropriately describe the admin home page functionality.

Also applies to: 43-46

apps/admin/core/components/instance/loading.tsx (2)

4-5: LGTM! Correct Vite asset import syntax.

The ?url suffix correctly instructs Vite to return the asset URL as a string, which is the proper syntax for this migration.


1-2: Verify Next.js compatibility shims exist for this migration.

While the PR objectives mention compatibility layers for next/image, the past review found no shims and both next and next-themes packages remaining in dependencies. The import on line 2 for next-themes is not mentioned in the compatibility layer list.

Run the following script to verify whether compatibility shims have been added:

apps/admin/core/components/instance/failure.tsx (3)

8-9: Asset imports correctly updated for Vite.

The asset imports using the ?url suffix are the correct pattern for Vite, loading SVG files as URL strings. This properly replaces the previous Next.js public path approach.


11-11: Component typing is correct.

The use of React.FC without an explicit React import is correct for this codebase, as React types are globally available through TypeScript configuration.

Based on learnings.


3-4: Verify Next.js compatibility shims are in place.

The component continues to import from next/image and next-themes. Per the PR description, compatibility layers were introduced for Next.js utilities. Ensure these shims exist and properly handle these imports in the React Router + Vite environment.

Run the following script to verify the compatibility shims:

apps/admin/core/components/instance/instance-not-ready.tsx (1)

7-7: Asset import correctly updated for Vite.

The asset import using ?url correctly follows the Vite pattern for loading images as URL strings.

apps/admin/tsconfig.json (3)

2-6: Configuration update aligns with React Router + Vite migration.

The tsconfig changes appropriately reflect the architectural shift:

  • Extending react-router.json instead of Next.js config
  • Adding rootDirs to include React Router type generation directory (.react-router/types)
  • Adding vite/client types for Vite-based development

These changes look correct for the target architecture.


15-15: Verify the updated include pattern doesn't unintentionally exclude needed files.

The include now explicitly lists ["**/*", "**/.server/**/*", "**/.client/**/*", ".react-router/types/**/*"] instead of relying on default includes. Confirm that:

  1. All necessary source directories under apps/admin/ (like app/, core/, ce/) are captured by the **/* glob
  2. The .react-router/types/**/* directory will exist and contain expected generated types
  3. No source files are unintentionally excluded

1-16: Verify that the @/public/* path removal doesn't break admin app imports.

A past review flagged the removal of the "public/*" path mapping as breaking asset imports, though the example cited was from apps/web/. For the admin app, verify whether apps/admin/ itself contains imports using @/public/* or /public/* paths before confirming this change is safe.

Run the following script to check if the admin app has any imports relying on the removed @/public/* mapping:

If the admin app does use @/public/* imports, restore the mapping:

  "paths": {
    "@/app/*": ["app/*"],
    "@/*": ["core/*"],
    "@/plane-admin/*": ["ce/*"],
+   "public/*": ["public/*"],
    "@/styles/*": ["styles/*"]
  }
apps/admin/ce/components/authentication/authentication-modes.tsx (1)

13-18: Verify the asset paths exist in the new location.

The asset imports have been migrated from public assets to local @/app/assets/logos/ with Vite's ?url suffix. Ensure all these logo files exist at the specified paths.

pnpm-workspace.yaml (3)

1-37: Address the past formatting concern before merging.

A previous reviewer (sriramveeraghanta) flagged that your formatter uses double quotes and requested a revert of the file changes. This PR still appears to have formatting changes. Please either:

  1. Confirm that the formatter configuration has been updated to accept this format, or
  2. Revert the formatting to match your project's formatter expectations.

7-33: Verify dependency versions are compatible across the workspace.

Several dependencies have been updated to newer versions that could affect other packages in the workspace:

  • TypeScript 5.8.3: Ensure all workspace packages support TS 5.x (breaking changes possible)
  • React 18.3.1 & react-dom 18.3.1: Confirm compatibility with existing apps
  • Vite 7.1.7: New major version; verify build scripts and configurations are compatible
  • mobx 6.12.0, mobx-react 9.1.1, mobx-utils 6.0.8: These are new to the workspace; confirm they're only used by admin or if they're shared dependencies

Additionally, verify that pinning these versions doesn't create conflicts with other parts of the monorepo (e.g., apps/web, apps/space).


2-5: Ensure hyphen list format is intentional and consistent.

The YAML lists are formatted with hyphens (lines 2–5, 35–37), which is correct YAML syntax. However, given the past formatting concern, confirm this aligns with your project's formatting rules and linter configuration.

Also applies to: 35-37

apps/admin/app/(all)/(dashboard)/workspace/create/page.tsx (1)

22-23: LGTM: Route metadata follows React Router conventions.

The meta export correctly provides page title metadata for the React Router migration.

apps/admin/app/(all)/(dashboard)/ai/page.tsx (1)

46-47: LGTM: Route metadata correctly defined.

The meta export follows the established pattern for React Router page metadata.

apps/admin/app/types/next-link.d.ts (2)

2-11: Type definition structure is sound.

The ambient module correctly declares Next.js Link compatibility types, extending anchor props with Next.js-specific options. This provides type safety for the migration compatibility layer.


1-12: Verify href type completeness for migration needs.

The href property is typed as string, but Next.js Link also accepts UrlObject (from 'url'). Ensure your codebase doesn't pass URL objects to Link components.

Run this script to check for URL object usage in Link components:

apps/admin/vite.config.ts (1)

35-43: Use build.ssr for the SSR entry; rollupOptions.input is not the SSR entry point.

Vite expects build.ssr for SSR builds. Using rollupOptions.input here can yield a wrong or missing server bundle.

-      rollupOptions:
-        isSsrBuild && enableSsrBuild
-          ? {
-              input: path.resolve(__dirname, "server/app.ts"),
-            }
-          : undefined,
+      ssr: isSsrBuild && enableSsrBuild
+        ? path.resolve(__dirname, "server/app.ts")
+        : undefined,

Please verify locally:

  • Run: pnpm vite build --ssr apps/admin/server/app.ts (or your equivalent) and confirm the server output is emitted when ADMIN_ENABLE_SSR_BUILD=true.
packages/services/package.json (2)

6-10: ESM migration and exports structure look good.

The addition of "type": "module" and the updated import/require mappings follow standard dual-package conventions for ESM-first libraries.


25-35: Clarify the purpose of new file-handling dependencies.

The PR adds several binary/media handling libraries (fflate, ieee754, strtok3, token-types, @tokenizer/inflate, @borewit/text-codec, uint8array-extras) and upgrades file-type to ^21.0.0. These appear orthogonal to the admin React Router + Vite migration described in the PR objectives.

Confirm whether these dependencies are:

  1. Intentionally introduced as part of this PR (if so, document their usage)
  2. Accidentally included from another feature branch
  3. Required by an updated transitive dependency

Also verify that these additions do not bloat the services package unnecessarily.

apps/admin/app/components/404.tsx (1)

1-6: LGTM!

The imports are clean and the SVG asset import using Vite's ?url suffix is the correct approach for static assets.

apps/admin/react-router.config.ts (2)

1-2: LGTM!

The import is correct for React Router configuration.


5-5: Verify basename behavior when environment variable is undefined.

The basename is set to process.env.NEXT_PUBLIC_ADMIN_BASE_PATH, which will be undefined if the environment variable is not set. While React Router typically defaults to "/" when basename is undefined, please verify that:

  1. The environment variable is properly configured in all deployment environments (development, staging, production)
  2. The app routes correctly when the variable is not set (if that's an expected scenario)
  3. Consider providing an explicit fallback if undefined is not the desired behavior:
-  basename: process.env.NEXT_PUBLIC_ADMIN_BASE_PATH,
+  basename: process.env.NEXT_PUBLIC_ADMIN_BASE_PATH || "/",

Run the following script to check how this environment variable is used across the admin app:

apps/admin/app/compat/next/helper.ts (1)

6-33: Verify trailing slash enforcement is intentional across all routes.

The function enforces trailing slashes on all URLs (except root). React Router typically works with or without trailing slashes, so this normalization must be applied consistently throughout the app to avoid routing mismatches.

Verify that:

  1. All route definitions in apps/admin/app/routes.ts are consistent with this trailing slash convention
  2. Existing links and navigation calls throughout the codebase work correctly with this normalization
  3. The React Router configuration expects trailing slashes

Run this script to check for potential inconsistencies:

apps/admin/app/routes.ts (1)

18-19: Verify the 404 component path and catch-all route behavior.

The catch-all route uses a relative path to the 404 component. Ensure this path is correct and the component is exported properly.

Verify that:

  1. The 404 component exists at the specified path
  2. It's exported as a default export
  3. The catch-all route correctly handles unmatched paths
apps/admin/app/compat/next/navigation.ts (1)

11-12: Verify trailing slash enforcement doesn't break existing routes.

The navigation shim applies ensureTrailingSlash to all push and replace calls. This must be consistent with how routes are defined and how existing navigation logic expects paths to be formatted.

Search for existing navigation calls to verify they work correctly with trailing slashes:

apps/admin/server/app.ts (1)

22-25: Document the trust proxy assumption.

Setting trust proxy: true unconditionally tells Express to honor X-Forwarded-* headers from any upstream. This is safe when the admin app is always behind nginx (as your comment suggests), but if it's ever exposed directly, clients could spoof those headers. Consider documenting this deployment requirement or making it conditional based on an environment flag.

apps/admin/server.mjs (2)

67-70: Origin string construction assumes valid PORT

Line 68 constructs the origin URL using PORT, which will produce invalid output like http://example.com:NaN if the PORT derivation issue (line 26) is not fixed. Additionally, for standard ports (80/443), explicitly including the port in the origin URL is unusual, though not incorrect.

This will be resolved once the PORT derivation logic is corrected.


46-57: Development SSR middleware looks correct

The Vite SSR setup properly loads the server app module and handles errors with stack trace fixing. Ensure that server/app.ts exports a function named app that accepts (req, res, next) parameters as expected on line 49.

apps/admin/package.json (2)

7-7: Verify ES module compatibility across the codebase.

Adding "type": "module" enables ES modules for .js and .ts files in this workspace. Ensure all entry points, imports, and tooling (e.g., server.mjs, Vite config, test runners) are compatible with ESM-only mode; review any .cjs files, require() calls, or CommonJS-dependent dependencies.


9-15: Script refactoring aligns well with React Router + Vite architecture.

The migration from Next.js to React Router is reflected cleanly: custom server.mjs entry points, react-router build replacing Next.js build, and react-router typegen for type safety are appropriate. The serve package for static serving and cross-env for environment consistency are solid choices.

Comment on lines +15 to +17
refresh: () => {
location.reload();
},
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

refresh() causes full page reload and loses client state.

location.reload() triggers a full page refresh, which differs from Next.js router.refresh() behavior. Next.js router.refresh() re-fetches data while preserving client state, but this implementation loses all React state, SWR cache, and in-memory data.

Consider these alternatives:

  1. If route-level data refetch is needed: Implement using React Router's revalidate pattern or a custom invalidation mechanism
  2. If this is a stub: Document the limitation clearly with a comment
  3. If full reload is intentional: Keep current implementation but add a comment explaining this is not equivalent to Next.js behavior

Example for option 2:

 refresh: () => {
+  // NOTE: This is a full page reload and NOT equivalent to Next.js router.refresh()
+  // which re-fetches data while preserving client state. Migrate code using
+  // router.refresh() to use React Router's revalidation pattern instead.
   location.reload();
 },
📝 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
refresh: () => {
location.reload();
},
refresh: () => {
// NOTE: This is a full page reload and NOT equivalent to Next.js router.refresh()
// which re-fetches data while preserving client state. Migrate code using
// router.refresh() to use React Router's revalidation pattern instead.
location.reload();
},
🤖 Prompt for AI Agents
In apps/admin/app/compat/next/navigation.ts around lines 15 to 17, the refresh()
implementation calls location.reload() which forces a full page reload and loses
client React state, SWR cache and other in-memory data; replace this with a
non-destructive refresh strategy (preferably call the app's router.refresh() or
a revalidation/invalidation function that re-fetches data without unmounting
components), or if route-level revalidation isn’t available yet, add a clear
comment that this is a temporary stub and that full reload is intentional;
choose one: (A) wire into the framework's revalidate/refresh API to preserve
client state, (B) document as a stub with a TODO explaining limitations, or (C)
keep location.reload() but add a comment stating it intentionally forces a full
reload and is not equivalent to Next.js router.refresh().

Comment on lines +22 to +28
<Link to="/general/">
<span className="flex justify-center py-4">
<Button variant="neutral-primary" size="md">
Go to general settings
</Button>
</span>
</Link>
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

Fix nested interactive elements accessibility issue.

Wrapping a Button inside a Link creates nested interactive elements, which is an accessibility anti-pattern. This can confuse screen readers and keyboard navigation, as it's unclear which element should receive focus and handle the click event.

Consider one of these solutions:

Option 1: Style the Link as a button

-        <Link to="/general/">
-          <span className="flex justify-center py-4">
-            <Button variant="neutral-primary" size="md">
-              Go to general settings
-            </Button>
-          </span>
-        </Link>
+        <div className="flex justify-center py-4">
+          <Button variant="neutral-primary" size="md" asChild>
+            <Link to="/general/">Go to general settings</Link>
+          </Button>
+        </div>

Option 2: If Button doesn't support asChild, use Link with button styling

-        <Link to="/general/">
-          <span className="flex justify-center py-4">
-            <Button variant="neutral-primary" size="md">
-              Go to general settings
-            </Button>
-          </span>
-        </Link>
+        <div className="flex justify-center py-4">
+          <Link 
+            to="/general/" 
+            className="inline-flex items-center justify-center rounded-md px-4 py-2 text-sm font-medium"
+          >
+            Go to general settings
+          </Link>
+        </div>

Note: Check if the @plane/propel/button component supports an asChild prop (common in Radix-based component libraries) for composition with routing links.

📝 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
<Link to="/general/">
<span className="flex justify-center py-4">
<Button variant="neutral-primary" size="md">
Go to general settings
</Button>
</span>
</Link>
<div className="flex justify-center py-4">
<Link
to="/general/"
className="inline-flex items-center justify-center rounded-md px-4 py-2 text-sm font-medium"
>
Go to general settings
</Link>
</div>
🤖 Prompt for AI Agents
In apps/admin/app/components/404.tsx around lines 22 to 28, the Button is nested
inside a Link which creates nested interactive elements; to fix, either (A) use
the Button's composition API (e.g., asChild) so the Button renders the Link
(check if @plane/propel/button supports asChild and if so render Button as the
outer component with Link as its child), or (B) remove the Button component and
style the Link to look and behave like the button (apply the Button
classes/variant/size to the Link and ensure it has role/button and proper
keyboard handling), then remove the nested interactive element to restore
correct accessibility and focus behavior.

Comment on lines +31 to +35
return {
define: {
"process.env": JSON.stringify(publicEnv),
},
build: {
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

Set base from NEXT_PUBLIC_ADMIN_BASE_PATH for subpath deployments.

Without base, asset URLs will point to /assets/... and break when the app is served under a subpath (e.g., /admin). You already expose NEXT_PUBLIC_ADMIN_BASE_PATH; wire it in.

   return {
+    base: publicEnv.NEXT_PUBLIC_ADMIN_BASE_PATH || "/",

Note: ensure this path ends with a trailing slash in your env (e.g., /admin/) to generate correct asset URLs.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In apps/admin/vite.config.ts around lines 31–35, the Vite config currently
doesn't set the base path so asset URLs break when the app is served under a
subpath; update the config to read NEXT_PUBLIC_ADMIN_BASE_PATH from the
environment and assign it to the Vite `base` option (use a safe fallback of '/'
if the env var is unset) and ensure the env value includes a trailing slash
(e.g., '/admin/').

"eslint-plugin-import": "^2.32.0",
"eslint-plugin-react": "^7.37.5",
"eslint-plugin-react-hooks": "^6.1.1",
"typescript": "catalog:"
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

Incomplete or malformed TypeScript version reference.

Line 21 shows "typescript": "catalog:" which appears to be incomplete. If this is a monorepo version catalog reference, it needs a proper identifier (e.g., "catalog:default" or similar). If it's meant to be a semantic version constraint, it should specify a version range.

Please verify and correct the TypeScript version reference.

🤖 Prompt for AI Agents
In packages/eslint-config/package.json around line 21, the TypeScript version
entry is malformed as "typescript": "catalog:"; replace this with a valid
version reference—either a proper version-catalog identifier used by your
monorepo (for example "catalog:default" or the exact catalog key your build
uses) or a semantic version string (for example "^5.1.0")—so the package.json
contains a correct, resolvable TypeScript reference consistent with your repo's
dependency management.

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.

2 participants