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 { ErrorBoundary } from '@/components/common/ErrorBoundary'
|
||||||
import { PageLoader } from '@/components/common/PageLoader'
|
import { PageLoader } from '@/components/common/PageLoader'
|
||||||
import { lazyWithRetry } from '@/lib/lazyWithRetry'
|
import { lazyWithRetry } from '@/lib/lazyWithRetry'
|
||||||
|
import { L1RouteGuard } from '@/components/layout/L1RouteGuard'
|
||||||
|
|
||||||
const sentryCreateBrowserRouter = Sentry.wrapCreateBrowserRouterV7(createBrowserRouter)
|
const sentryCreateBrowserRouter = Sentry.wrapCreateBrowserRouterV7(createBrowserRouter)
|
||||||
import {
|
import {
|
||||||
@@ -95,6 +96,12 @@ const AdminSurveyInvitesPage = lazyWithRetry(() => import('@/pages/admin/SurveyI
|
|||||||
const AdminSurveyResponsesPage = lazyWithRetry(() => import('@/pages/admin/SurveyResponsesPage'))
|
const AdminSurveyResponsesPage = lazyWithRetry(() => import('@/pages/admin/SurveyResponsesPage'))
|
||||||
const AdminGalleryManagementPage = lazyWithRetry(() => import('@/pages/admin/GalleryManagementPage'))
|
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
|
// Account pages
|
||||||
const AccountLayout = lazyWithRetry(() => import('@/components/account/AccountLayout'))
|
const AccountLayout = lazyWithRetry(() => import('@/components/account/AccountLayout'))
|
||||||
const ProfileSettingsPage = lazyWithRetry(() => import('@/pages/account/ProfileSettingsPage'))
|
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-1', element: page(WelcomeStep1) },
|
||||||
{ path: 'welcome/step-2', element: page(WelcomeStep2) },
|
{ path: 'welcome/step-2', element: page(WelcomeStep2) },
|
||||||
{ path: 'welcome/step-3', element: page(WelcomeStep3) },
|
{ 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
|
// Admin routes
|
||||||
{
|
{
|
||||||
path: 'admin',
|
path: 'admin',
|
||||||
|
|||||||
Reference in New Issue
Block a user