feat(admin): bump react-router and adapt new routes package (#11887)

<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Added new admin routes: "auth", "setup", and "notFound" for improved navigation and access within the admin interface.
  - Introduced a utility for simplified and flexible lazy loading of React components with fallback support.

- **Improvements**
  - Updated routing structure in the admin frontend for clearer route management and enhanced Sentry integration.
  - Centralized route definitions for easier maintenance and consistency.
  - Upgraded dependencies to support the latest React Router and React versions.
  - Enhanced asynchronous handling in setup form navigation.

- **Chores**
  - Updated project references and workspace dependencies for better package management.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
forehalo
2025-04-24 09:17:59 +00:00
parent 5d76be6736
commit 45df4568a4
12 changed files with 157 additions and 73 deletions

View File

@@ -7,6 +7,7 @@
"@affine/core": "workspace:*",
"@affine/error": "workspace:*",
"@affine/graphql": "workspace:*",
"@affine/routes": "workspace:*",
"@blocksuite/icons": "^2.2.12",
"@radix-ui/react-accordion": "^1.2.2",
"@radix-ui/react-alert-dialog": "^1.1.3",
@@ -50,7 +51,7 @@
"react-dom": "^19.0.0",
"react-hook-form": "^7.54.1",
"react-resizable-panels": "^2.1.7",
"react-router-dom": "^6.28.0",
"react-router-dom": "^7.5.1",
"sonner": "^2.0.0",
"swr": "^2.2.5",
"vaul": "^1.1.1",

View File

@@ -1,11 +1,13 @@
import { Toaster } from '@affine/admin/components/ui/sonner';
import { wrapCreateBrowserRouterV6 } from '@sentry/react';
import { lazy, ROUTES } from '@affine/routes';
import { withSentryReactRouterV7Routing } from '@sentry/react';
import { useEffect } from 'react';
import {
createBrowserRouter as reactRouterCreateBrowserRouter,
BrowserRouter,
Navigate,
Outlet,
RouterProvider,
Route,
Routes as ReactRouterRoutes,
useLocation,
} from 'react-router-dom';
import { toast } from 'sonner';
@@ -15,13 +17,28 @@ import { TooltipProvider } from './components/ui/tooltip';
import { isAdmin, useCurrentUser, useServerConfig } from './modules/common';
import { Layout } from './modules/layout';
const createBrowserRouter = wrapCreateBrowserRouterV6(
reactRouterCreateBrowserRouter
export const Setup = lazy(
() => import(/* webpackChunkName: "setup" */ './modules/setup')
);
export const Accounts = lazy(
() => import(/* webpackChunkName: "accounts" */ './modules/accounts')
);
export const AI = lazy(
() => import(/* webpackChunkName: "ai" */ './modules/ai')
);
export const About = lazy(
() => import(/* webpackChunkName: "about" */ './modules/about')
);
export const Settings = lazy(
() => import(/* webpackChunkName: "settings" */ './modules/settings')
);
export const Auth = lazy(
() => import(/* webpackChunkName: "auth" */ './modules/auth')
);
const _createBrowserRouter = window.SENTRY_RELEASE
? createBrowserRouter
: reactRouterCreateBrowserRouter;
const Routes = window.SENTRY_RELEASE
? withSentryReactRouterV7Routing(ReactRouterRoutes)
: ReactRouterRoutes;
function AuthenticatedRoutes() {
const user = useCurrentUser();
@@ -58,57 +75,6 @@ function RootRoutes() {
return <Outlet />;
}
export const router = _createBrowserRouter(
[
{
path: '/admin',
element: <RootRoutes />,
children: [
{
path: '/admin/auth',
lazy: () => import('./modules/auth'),
},
{
path: '/admin/setup',
lazy: () => import('./modules/setup'),
},
{
path: '/admin/*',
element: <AuthenticatedRoutes />,
children: [
{
path: 'accounts',
lazy: () => import('./modules/accounts'),
},
{
path: 'ai',
lazy: () => import('./modules/ai'),
},
{
path: 'about',
lazy: () => import('./modules/about'),
},
{
path: 'settings',
children: [
{
path: '*',
lazy: () => import('./modules/settings'),
},
],
},
],
},
],
},
],
{
future: {
v7_normalizeFormMethod: true,
},
}
);
export const App = () => {
return (
<TooltipProvider>
@@ -118,7 +84,28 @@ export const App = () => {
revalidateOnMount: false,
}}
>
<RouterProvider router={router} />
<BrowserRouter basename={environment.subPath}>
<Routes>
<Route path={ROUTES.admin.index} element={<RootRoutes />}>
<Route path={ROUTES.admin.auth} element={<Auth />} />
<Route path={ROUTES.admin.setup} element={<Setup />} />
<Route element={<AuthenticatedRoutes />}>
<Route path={ROUTES.admin.accounts} element={<Accounts />} />
<Route path={ROUTES.admin.ai} element={<AI />} />
<Route path={ROUTES.admin.about} element={<About />} />
<Route
path={ROUTES.admin.settings.index}
element={<Settings />}
>
<Route
path={ROUTES.admin.settings.module}
element={<Settings />}
/>
</Route>
</Route>
</Route>
</Routes>
</BrowserRouter>
</SWRConfig>
<Toaster />
</TooltipProvider>

View File

@@ -62,7 +62,7 @@ export function SettingsPage() {
);
}
export const AdminPanel = ({
const AdminPanel = ({
appConfig,
patchedAppConfig,
onUpdate,

View File

@@ -165,7 +165,7 @@ export const Form = () => {
passwordValue,
]);
const onPrevious = useCallback(() => {
const onPrevious = useAsyncCallback(async () => {
if (current === count) {
if (serverConfig.initialized === true) {
return navigate('/admin', { replace: true });

View File

@@ -12,6 +12,7 @@
{ "path": "../core" },
{ "path": "../../common/error" },
{ "path": "../../common/graphql" },
{ "path": "../routes" },
{ "path": "../../common/infra" }
]
}

View File

@@ -4,7 +4,7 @@
"private": true,
"type": "module",
"exports": {
".": "./src/routes.ts"
".": "./src/index.ts"
},
"scripts": {
"build": "r build.ts"
@@ -16,7 +16,8 @@
"query-string": "^9.1.1",
"vitest": "^3.0.6"
},
"optionalDependencies": {
"react-router-dom": "^6.28.0"
"peerDependencies": {
"react": "^19.1.0",
"react-router-dom": "^7.5.1"
}
}

View File

@@ -5,6 +5,8 @@
"admin": {
"route": "admin",
"children": {
"auth": "auth",
"setup": "setup",
"accounts": "accounts",
"ai": "ai",
"settings": {

View File

@@ -0,0 +1,2 @@
export * from './lazy';
export * from './routes';

View File

@@ -0,0 +1,37 @@
import React, {
type ComponentProps,
type ComponentType,
lazy as reactLazy,
Suspense,
} from 'react';
export function lazy<T extends ComponentType<any>>(
factory: () => Promise<Record<any, T>>,
fallback?: React.ReactNode
) {
const LazyComponent = reactLazy(() =>
factory().then(mod => {
if ('default' in mod) {
return { default: mod.default };
} else {
const components = Object.values(mod);
if (components.length > 1) {
console.warn('Lazy loaded module has more then one exports');
}
return {
default: components[0],
};
}
})
);
return function LazyRoute(props: ComponentProps<T>) {
return React.createElement(
Suspense,
{
fallback,
},
React.createElement(LazyComponent, props)
);
};
}

View File

@@ -9,10 +9,13 @@ export const ROUTES = {
index: '/',
admin: {
index: '/admin',
auth: '/admin/auth',
setup: '/admin/setup',
accounts: '/admin/accounts',
ai: '/admin/ai',
settings: { index: '/admin/settings', module: '/admin/settings/:module' },
about: '/admin/about',
notFound: '/admin/404',
},
};
// #endregion
@@ -22,10 +25,13 @@ export const RELATIVE_ROUTES = {
index: '/',
admin: {
index: 'admin',
auth: 'auth',
setup: 'setup',
accounts: 'accounts',
ai: 'ai',
settings: { index: 'settings', module: ':module' },
about: 'about',
notFound: '404',
},
};
// #endregion
@@ -33,6 +39,8 @@ export const RELATIVE_ROUTES = {
// #region Path Factories
const home = () => '/';
const admin = () => '/admin';
admin.auth = () => '/admin/auth';
admin.setup = () => '/admin/setup';
admin.accounts = () => '/admin/accounts';
admin.ai = () => '/admin/ai';
const admin_settings = () => '/admin/settings';
@@ -40,5 +48,6 @@ admin_settings.module = (params: { module: string }) =>
`/admin/settings/${params.module}`;
admin.settings = admin_settings;
admin.about = () => '/admin/about';
admin.notFound = () => '/admin/404';
export const FACTORIES = { admin, home };
// #endregion