fix: flatten React Router structure — no nested Routes, each page is a top-level route

This commit is contained in:
enzo
2026-07-22 19:21:29 +08:00
parent 07ff8c2a12
commit 1de5fcf698
3720 changed files with 1254827 additions and 49 deletions
+30 -48
View File
@@ -27,8 +27,8 @@ function Loading() {
)
}
/** Redirect to /login if not authenticated. */
function RequireAuth({ children }: { children: ReactNode }) {
/** Protected route wrapper — redirects to /login if not authenticated */
function ProtectedRoute({ children }: { children: ReactNode }) {
const { isAuthenticated } = useAuth()
const location = useLocation()
@@ -38,14 +38,13 @@ function RequireAuth({ children }: { children: ReactNode }) {
return <>{children}</>
}
/** Redirect to /dashboard if already authenticated. */
function RedirectIfAuth({ children }: { children: ReactNode }) {
/** Login page wrapper — redirects to / if already authenticated */
function LoginRoute() {
const { isAuthenticated } = useAuth()
if (isAuthenticated) {
return <Navigate to="/" replace />
}
return <>{children}</>
return <LoginPage />
}
function AppRoutes() {
@@ -60,14 +59,12 @@ function AppRoutes() {
if (res.data.profiles.length > 0) {
groups.push({
label: 'Profiles',
items: res.data.profiles.map((p) => ({
items: res.data.profiles.map((p: any) => ({
value: '/gateway',
label: (
<span className="flex items-center gap-2">
{p.name}
<Badge variant={p.status === 'online' ? 'success' : 'danger'}>
{p.status}
</Badge>
<Badge variant={p.status === 'online' ? 'success' : 'danger'}>{p.status}</Badge>
</span>
),
})),
@@ -76,7 +73,7 @@ function AppRoutes() {
if (res.data.kanban_tasks.length > 0) {
groups.push({
label: 'Kanban 任务',
items: res.data.kanban_tasks.map((t) => ({
items: res.data.kanban_tasks.map((t: any) => ({
value: '/kanban',
label: (
<span className="flex items-center gap-2">
@@ -90,7 +87,7 @@ function AppRoutes() {
if (res.data.cron_jobs.length > 0) {
groups.push({
label: 'Cron 任务',
items: res.data.cron_jobs.map((j) => ({
items: res.data.cron_jobs.map((j: any) => ({
value: '/cron',
label: (
<span className="flex items-center gap-2">
@@ -104,7 +101,7 @@ function AppRoutes() {
if (res.data.events.length > 0) {
groups.push({
label: '事件',
items: res.data.events.map((e) => ({
items: res.data.events.map((e: any) => ({
value: '/events',
label: (
<span className="flex items-center gap-2">
@@ -127,42 +124,27 @@ function AppRoutes() {
return (
<Routes>
<Route
path="/login"
element={
<RedirectIfAuth>
<LoginPage />
</RedirectIfAuth>
}
/>
{/* Public */}
<Route path="/login" element={<LoginRoute />} />
<Route path="/404" element={<NotFoundPage />} />
<Route
path="/"
element={
<RequireAuth>
<AppLayout onSearch={handleSearch} onSearchNavigate={handleSearchNavigate}>
<Suspense fallback={<Loading />}>
<Routes>
<Route index element={<Navigate to="dashboard" replace />} />
<Route path="dashboard" element={<DashboardPage />} />
<Route path="gateway" element={<GatewayPage />} />
<Route path="health" element={<HealthPage />} />
<Route path="kanban" element={<KanbanPage />} />
<Route path="cron" element={<CronPage />} />
<Route path="keys" element={<KeysPage />} />
<Route path="events" element={<EventArchivePage />} />
{user?.role === 'owner' && (
<>
<Route path="users" element={<UserManagementPage />} />
<Route path="audit" element={<AuditLogPage />} />
</>
)}
</Routes>
</Suspense>
</AppLayout>
</RequireAuth>
}
/>
{/* Protected — each page is its own route (no nested Routes) */}
<Route path="/" element={<ProtectedRoute><AppLayout onSearch={handleSearch} onSearchNavigate={handleSearchNavigate}><DashboardPage /></AppLayout></ProtectedRoute>} />
<Route path="/dashboard" element={<ProtectedRoute><AppLayout onSearch={handleSearch} onSearchNavigate={handleSearchNavigate}><DashboardPage /></AppLayout></ProtectedRoute>} />
<Route path="/gateway" element={<ProtectedRoute><AppLayout onSearch={handleSearch} onSearchNavigate={handleSearchNavigate}><GatewayPage /></AppLayout></ProtectedRoute>} />
<Route path="/health" element={<ProtectedRoute><AppLayout onSearch={handleSearch} onSearchNavigate={handleSearchNavigate}><HealthPage /></AppLayout></ProtectedRoute>} />
<Route path="/kanban" element={<ProtectedRoute><AppLayout onSearch={handleSearch} onSearchNavigate={handleSearchNavigate}><KanbanPage /></AppLayout></ProtectedRoute>} />
<Route path="/cron" element={<ProtectedRoute><AppLayout onSearch={handleSearch} onSearchNavigate={handleSearchNavigate}><CronPage /></AppLayout></ProtectedRoute>} />
<Route path="/keys" element={<ProtectedRoute><AppLayout onSearch={handleSearch} onSearchNavigate={handleSearchNavigate}><KeysPage /></AppLayout></ProtectedRoute>} />
<Route path="/events" element={<ProtectedRoute><AppLayout onSearch={handleSearch} onSearchNavigate={handleSearchNavigate}><EventArchivePage /></AppLayout></ProtectedRoute>} />
{user?.role === 'owner' && (
<>
<Route path="/users" element={<ProtectedRoute><AppLayout onSearch={handleSearch} onSearchNavigate={handleSearchNavigate}><UserManagementPage /></AppLayout></ProtectedRoute>} />
<Route path="/audit" element={<ProtectedRoute><AppLayout onSearch={handleSearch} onSearchNavigate={handleSearchNavigate}><AuditLogPage /></AppLayout></ProtectedRoute>} />
</>
)}
{/* Catch-all */}
<Route path="*" element={<Navigate to="/404" replace />} />
</Routes>
)
+1 -1
View File
@@ -269,7 +269,7 @@ export default function HealthPage() {
{/* table */}
<Table columns={columns} data={tableData as any} rowKey="key"
onRowClick={(record) => setExpandedRow(expandedRow === record.key ? null : String(record.key))}
rowClassName={(record) => String(record.grade?.props?.grade || '') === 'D' ? 'row-danger' : ''} />
rowClassName={(record: any) => String(record._grade || '') === 'D' ? 'row-danger' : ''} />
{/* expanded detail */}
{expandedRow && (