Files
CODE_ASSISTANT/2_frontend/src/routes.tsx
T
lee-hyeon-cheolandClaude Fable 5.1 d4a5f20787 feat: 관리자 대시보드 — 토큰·비용·응답시간 집계 API + 앱 /admin 페이지
- backend apps/stats: GET /api/v1/admin/stats?from&to → totals / byUser / byDay. ChatMessage(assistant) 집계만, is_superuser 아니면 403. 테스트 3개
- frontend features/admin: 요약 카드 4개(요청·토큰·비용·평균 응답), 일별 토큰 막대(div), 사용자별 표, 기간 프리셋(오늘/7/30/90일). 헤더에 ADMIN 만 아이콘. 테스트 2개

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-21 13:39:06 +09:00

53 lines
2.2 KiB
TypeScript

import { lazy, Suspense } from "react"
import { CenterSpinner } from "@/shared/components/CenterSpinner"
import { Navigate, type RouteObject } from "react-router-dom"
import Layout from "@/shared/components/Layout"
import ProtectedRoute from "@/shared/components/ProtectedRoute"
import { SnapLayout } from "@/features/snap/components/SnapLayout"
import { PaletteShell } from "@/shared/components/PaletteShell"
import { PATHS } from "@/config/routes"
const LoginPage = lazy(() => import("@/features/auth/pages/LoginPage"))
const SessionListPage = lazy(() => import("@/features/snap/pages/SessionListPage"))
const NewChatPage = lazy(() => import("@/features/snap/pages/NewChatPage"))
const SessionChatPage = lazy(() => import("@/features/snap/pages/SessionChatPage"))
const SnippetPalettePage = lazy(() => import("@/features/snippets/pages/SnippetPalettePage"))
const AdminStatsPage = lazy(() => import("@/features/admin/pages/AdminStatsPage"))
const fallback = <CenterSpinner />
const wrap = (el: React.ReactNode) => <Suspense fallback={fallback}>{el}</Suspense>
export const routes: RouteObject[] = [
{
element: <Layout />,
children: [{ path: PATHS.LOGIN.slice(1), element: wrap(<LoginPage />) }],
},
{
element: <ProtectedRoute />,
children: [
{
// 챗봇(snap)·스니펫 공통 래퍼 — 붙여넣기 대상 배지 등 공통 UI 를 여기 한 곳에.
element: <PaletteShell />,
children: [
{
element: <SnapLayout />,
children: [
{ path: PATHS.SNAP.slice(1), element: wrap(<SessionListPage />) },
{ path: PATHS.SNAP_NEW.slice(1), element: wrap(<NewChatPage />) },
{ path: PATHS.SNAP_SESSION.slice(1), element: wrap(<SessionChatPage />) },
{ path: PATHS.ADMIN.slice(1), element: wrap(<AdminStatsPage />) },
],
},
{
// snap 헤더·Ctrl+N 없이 풀윈도우(Raycast). h-full 체인은 PaletteShell 의 h-screen 에 기댐.
path: PATHS.SNIPPET.slice(1),
element: wrap(<SnippetPalettePage />),
},
],
},
],
},
{ path: "*", element: <Navigate to={PATHS.SNAP} replace /> },
]