refactor(frontend): read the panel's navigation from one route manifest (#6922)

The router, the sidebar, the page titles and the command palette each kept
their own list of the panel's pages, so a page added to one could go
missing from another without any test noticing. They all read
src/routes/panelRoutes.tsx now, and a contract test holds the manifest to
the router, the translation bundle and the palette keywords.

The unused useZodForm helper goes, and CONTRIBUTING, frontend/CLAUDE.md and
the frontend README describe the useForm + rhfZodValidate pattern the
panel's forms actually use.
This commit is contained in:
25f2002654
2026-10-25 00:18:31 +05:30
committed by GitHub
parent 746bdbe9f4
commit 14c9bdff89
14 changed files with 242 additions and 254 deletions
+7 -27
View File
@@ -1,19 +1,9 @@
import { lazy, Suspense } from 'react';
import { Suspense } from 'react';
import { createBrowserRouter, type RouteObject } from 'react-router';
import { Spin } from 'antd';
import PanelLayout from '@/layouts/PanelLayout';
const IndexPage = lazy(() => import('@/pages/index/IndexPage'));
const InboundsPage = lazy(() => import('@/pages/inbounds/InboundsPage'));
const ClientsPage = lazy(() => import('@/pages/clients/ClientsPage'));
const GroupsPage = lazy(() => import('@/pages/groups/GroupsPage'));
const NodesPage = lazy(() => import('@/pages/nodes/NodesPage'));
const HostsPage = lazy(() => import('@/pages/hosts/HostsPage'));
const SettingsPage = lazy(() => import('@/pages/settings/SettingsPage'));
const XrayPage = lazy(() => import('@/pages/xray/XrayPage'));
const ApiDocsPage = lazy(() => import('@/pages/api-docs/ApiDocsPage'));
const SponsorsPage = lazy(() => import('@/pages/sponsors/SponsorsPage'));
import { PANEL_ROUTES } from '@/routes/panelRoutes';
function withSuspense(node: React.ReactNode) {
return (
@@ -36,24 +26,14 @@ function withSuspense(node: React.ReactNode) {
);
}
const routes: RouteObject[] = [
export const routes: RouteObject[] = [
{
path: '/',
element: <PanelLayout />,
children: [
{ index: true, element: withSuspense(<IndexPage />) },
{ path: 'inbounds', element: withSuspense(<InboundsPage />) },
{ path: 'clients', element: withSuspense(<ClientsPage />) },
{ path: 'groups', element: withSuspense(<GroupsPage />) },
{ path: 'nodes', element: withSuspense(<NodesPage />) },
{ path: 'hosts', element: withSuspense(<HostsPage />) },
{ path: 'settings', element: withSuspense(<SettingsPage />) },
{ path: 'xray', element: withSuspense(<XrayPage />) },
{ path: 'outbound', element: withSuspense(<XrayPage />) },
{ path: 'routing', element: withSuspense(<XrayPage />) },
{ path: 'api-docs', element: withSuspense(<ApiDocsPage />) },
{ path: 'sponsors', element: withSuspense(<SponsorsPage />) },
],
children: PANEL_ROUTES.map((route) => {
const element = withSuspense(<route.component />);
return route.path === '/' ? { index: true, element } : { path: route.path.slice(1), element };
}),
},
];