feat(l1): register /l1/* routes + L1RouteGuard + page stubs
L1RouteGuard wraps the new routes and redirects users without canUseL1Surface back to /. Page components are stubs in this task (real UI in T21-T24): L1Dashboard, L1WalkPage, L1DraftsPage, L1TicketsPage. Routes: /l1, /l1/walk/:sessionId, /l1/drafts, /l1/tickets — all gated. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
10
frontend/src/components/layout/L1RouteGuard.tsx
Normal file
10
frontend/src/components/layout/L1RouteGuard.tsx
Normal file
@@ -0,0 +1,10 @@
|
||||
import { Navigate } from 'react-router-dom'
|
||||
import { usePermissions } from '@/hooks/usePermissions'
|
||||
|
||||
export function L1RouteGuard({ children }: { children: React.ReactNode }) {
|
||||
const { canUseL1Surface } = usePermissions()
|
||||
if (!canUseL1Surface) {
|
||||
return <Navigate to="/" replace />
|
||||
}
|
||||
return <>{children}</>
|
||||
}
|
||||
13
frontend/src/pages/l1/L1Dashboard.tsx
Normal file
13
frontend/src/pages/l1/L1Dashboard.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { PageMeta } from '@/components/common/PageMeta'
|
||||
|
||||
export default function L1Dashboard() {
|
||||
return (
|
||||
<div className="overflow-y-auto h-full">
|
||||
<PageMeta title="L1 Workspace" />
|
||||
<div className="max-w-4xl mx-auto px-6 pt-12 pb-12">
|
||||
<h1 className="font-heading text-2xl font-bold">L1 Workspace</h1>
|
||||
<p className="text-muted-foreground mt-2">Loading…</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
13
frontend/src/pages/l1/L1DraftsPage.tsx
Normal file
13
frontend/src/pages/l1/L1DraftsPage.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { PageMeta } from '@/components/common/PageMeta'
|
||||
|
||||
export default function L1DraftsPage() {
|
||||
return (
|
||||
<div className="overflow-y-auto h-full">
|
||||
<PageMeta title="My Drafts" />
|
||||
<div className="max-w-4xl mx-auto px-6 pt-12 pb-12">
|
||||
<h1 className="font-heading text-2xl font-bold">My Drafts</h1>
|
||||
<p className="text-muted-foreground mt-2">Loading…</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
13
frontend/src/pages/l1/L1TicketsPage.tsx
Normal file
13
frontend/src/pages/l1/L1TicketsPage.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { PageMeta } from '@/components/common/PageMeta'
|
||||
|
||||
export default function L1TicketsPage() {
|
||||
return (
|
||||
<div className="overflow-y-auto h-full">
|
||||
<PageMeta title="L1 Tickets" />
|
||||
<div className="max-w-4xl mx-auto px-6 pt-12 pb-12">
|
||||
<h1 className="font-heading text-2xl font-bold">L1 Tickets</h1>
|
||||
<p className="text-muted-foreground mt-2">Loading…</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
13
frontend/src/pages/l1/L1WalkPage.tsx
Normal file
13
frontend/src/pages/l1/L1WalkPage.tsx
Normal file
@@ -0,0 +1,13 @@
|
||||
import { PageMeta } from '@/components/common/PageMeta'
|
||||
|
||||
export default function L1WalkPage() {
|
||||
return (
|
||||
<div className="overflow-y-auto h-full">
|
||||
<PageMeta title="L1 Walk" />
|
||||
<div className="max-w-4xl mx-auto px-6 pt-12 pb-12">
|
||||
<h1 className="font-heading text-2xl font-bold">L1 Walk</h1>
|
||||
<p className="text-muted-foreground mt-2">Loading…</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -7,6 +7,7 @@ import { RouteError } from '@/components/common/RouteError'
|
||||
import { ErrorBoundary } from '@/components/common/ErrorBoundary'
|
||||
import { PageLoader } from '@/components/common/PageLoader'
|
||||
import { lazyWithRetry } from '@/lib/lazyWithRetry'
|
||||
import { L1RouteGuard } from '@/components/layout/L1RouteGuard'
|
||||
|
||||
const sentryCreateBrowserRouter = Sentry.wrapCreateBrowserRouterV7(createBrowserRouter)
|
||||
import {
|
||||
@@ -95,6 +96,12 @@ const AdminSurveyInvitesPage = lazyWithRetry(() => import('@/pages/admin/SurveyI
|
||||
const AdminSurveyResponsesPage = lazyWithRetry(() => import('@/pages/admin/SurveyResponsesPage'))
|
||||
const AdminGalleryManagementPage = lazyWithRetry(() => import('@/pages/admin/GalleryManagementPage'))
|
||||
|
||||
// L1 workspace pages
|
||||
const L1Dashboard = lazyWithRetry(() => import('@/pages/l1/L1Dashboard'))
|
||||
const L1WalkPage = lazyWithRetry(() => import('@/pages/l1/L1WalkPage'))
|
||||
const L1DraftsPage = lazyWithRetry(() => import('@/pages/l1/L1DraftsPage'))
|
||||
const L1TicketsPage = lazyWithRetry(() => import('@/pages/l1/L1TicketsPage'))
|
||||
|
||||
// Account pages
|
||||
const AccountLayout = lazyWithRetry(() => import('@/components/account/AccountLayout'))
|
||||
const ProfileSettingsPage = lazyWithRetry(() => import('@/pages/account/ProfileSettingsPage'))
|
||||
@@ -284,6 +291,11 @@ export const router = sentryCreateBrowserRouter([
|
||||
{ path: 'welcome/step-1', element: page(WelcomeStep1) },
|
||||
{ path: 'welcome/step-2', element: page(WelcomeStep2) },
|
||||
{ path: 'welcome/step-3', element: page(WelcomeStep3) },
|
||||
// L1 workspace routes — gated by canUseL1Surface
|
||||
{ path: 'l1', element: <L1RouteGuard>{page(L1Dashboard)}</L1RouteGuard> },
|
||||
{ path: 'l1/walk/:sessionId', element: <L1RouteGuard>{page(L1WalkPage)}</L1RouteGuard> },
|
||||
{ path: 'l1/drafts', element: <L1RouteGuard>{page(L1DraftsPage)}</L1RouteGuard> },
|
||||
{ path: 'l1/tickets', element: <L1RouteGuard>{page(L1TicketsPage)}</L1RouteGuard> },
|
||||
// Admin routes
|
||||
{
|
||||
path: 'admin',
|
||||
|
||||
Reference in New Issue
Block a user