feat(l1): L1 workspace Phase 1 — role, seat enforcement, adhoc walker, audit #189

Merged
chihlasm merged 43 commits from feat/l1-workspace into main 2026-05-29 05:18:48 +00:00
6 changed files with 74 additions and 0 deletions
Showing only changes of commit d0561be6a1 - Show all commits

View 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}</>
}

View 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>
)
}

View 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>
)
}

View 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>
)
}

View 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>
)
}

View File

@@ -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',