Merge branch 'main' into feat/l1-workspace
# Conflicts: # frontend/src/router.tsx
This commit is contained in:
@@ -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 { useAuthStore } from '@/store/authStore'
|
||||
import { L1RouteGuard } from '@/components/layout/L1RouteGuard'
|
||||
|
||||
const sentryCreateBrowserRouter = Sentry.wrapCreateBrowserRouterV7(createBrowserRouter)
|
||||
@@ -125,10 +126,27 @@ function page(Component: React.LazyExoticComponent<React.ComponentType>) {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Public `/` wrapper — sends authenticated users to /home before LandingPage
|
||||
* mounts, so they never see marketing-frame flicker.
|
||||
*/
|
||||
// eslint-disable-next-line react-refresh/only-export-components -- router.tsx exports a router instance, not a component
|
||||
function PublicLanding() {
|
||||
const isAuthed = useAuthStore((s) => s.isAuthenticated)
|
||||
if (isAuthed) return <Navigate to="/home" replace />
|
||||
return page(LandingPage)
|
||||
}
|
||||
|
||||
export const router = sentryCreateBrowserRouter([
|
||||
{
|
||||
path: '/',
|
||||
element: <PublicLanding />,
|
||||
errorElement: <RouteError />,
|
||||
},
|
||||
// Stale-bookmark redirect — keep one release, delete in a follow-up.
|
||||
{
|
||||
path: '/landing',
|
||||
element: page(LandingPage),
|
||||
element: <Navigate to="/" replace />,
|
||||
errorElement: <RouteError />,
|
||||
},
|
||||
{
|
||||
@@ -236,7 +254,6 @@ export const router = sentryCreateBrowserRouter([
|
||||
errorElement: <RouteError />,
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
element: (
|
||||
<ProtectedRoute>
|
||||
<AppLayout />
|
||||
@@ -244,61 +261,61 @@ export const router = sentryCreateBrowserRouter([
|
||||
),
|
||||
errorElement: <RouteError />,
|
||||
children: [
|
||||
{ index: true, element: page(QuickStartPage) },
|
||||
{ path: 'trees', element: page(TreeLibraryPage) },
|
||||
{ path: 'my-trees', element: page(MyTreesPage) },
|
||||
{ path: 'trees/new', element: page(TreeEditorPage) },
|
||||
{ path: 'trees/:id/edit', element: page(TreeEditorPage) },
|
||||
{ path: 'flows/new', element: page(ProceduralEditorPage) },
|
||||
{ path: 'flows/:id/edit', element: page(ProceduralEditorPage) },
|
||||
{ path: 'flows/:id/navigate', element: page(ProceduralNavigationPage) },
|
||||
{ path: 'flows/:id/maintenance', element: page(MaintenanceFlowDetailPage) },
|
||||
{ path: 'flows/:id/batches/:batchId', element: page(BatchStatusPage) },
|
||||
{ path: 'trees/:id/navigate', element: page(TreeNavigationPage) },
|
||||
{ path: 'sessions', element: page(SessionHistoryPage) },
|
||||
{ path: 'sessions/:id', element: page(SessionDetailPage) },
|
||||
{ path: 'tickets', element: page(TicketsPage) },
|
||||
{ path: 'shares', element: page(MySharesPage) },
|
||||
{ path: 'analytics', element: page(TeamAnalyticsPage) },
|
||||
{ path: 'analytics/me', element: page(MyAnalyticsPage) },
|
||||
{ path: 'feedback', element: page(FeedbackPage) },
|
||||
{ path: 'step-library', element: page(StepLibraryPage) },
|
||||
{ path: 'scripts', element: page(ScriptLibraryPage) },
|
||||
{ path: 'scripts/manage', element: page(ScriptManagePage) },
|
||||
{ path: 'script-builder', element: page(ScriptBuilderPage) },
|
||||
{ path: 'network-diagrams', element: page(NetworkDiagramsPage) },
|
||||
{ path: 'network-diagrams/new', element: page(DiagramEditorPage) },
|
||||
{ path: 'network-diagrams/:id', element: page(DiagramEditorPage) },
|
||||
{ path: 'kb-accelerator', element: page(KBAcceleratorPage) },
|
||||
{ path: '/home', element: page(QuickStartPage) },
|
||||
{ path: '/trees', element: page(TreeLibraryPage) },
|
||||
{ path: '/my-trees', element: page(MyTreesPage) },
|
||||
{ path: '/trees/new', element: page(TreeEditorPage) },
|
||||
{ path: '/trees/:id/edit', element: page(TreeEditorPage) },
|
||||
{ path: '/flows/new', element: page(ProceduralEditorPage) },
|
||||
{ path: '/flows/:id/edit', element: page(ProceduralEditorPage) },
|
||||
{ path: '/flows/:id/navigate', element: page(ProceduralNavigationPage) },
|
||||
{ path: '/flows/:id/maintenance', element: page(MaintenanceFlowDetailPage) },
|
||||
{ path: '/flows/:id/batches/:batchId', element: page(BatchStatusPage) },
|
||||
{ path: '/trees/:id/navigate', element: page(TreeNavigationPage) },
|
||||
{ path: '/sessions', element: page(SessionHistoryPage) },
|
||||
{ path: '/sessions/:id', element: page(SessionDetailPage) },
|
||||
{ path: '/tickets', element: page(TicketsPage) },
|
||||
{ path: '/shares', element: page(MySharesPage) },
|
||||
{ path: '/analytics', element: page(TeamAnalyticsPage) },
|
||||
{ path: '/analytics/me', element: page(MyAnalyticsPage) },
|
||||
{ path: '/feedback', element: page(FeedbackPage) },
|
||||
{ path: '/step-library', element: page(StepLibraryPage) },
|
||||
{ path: '/scripts', element: page(ScriptLibraryPage) },
|
||||
{ path: '/scripts/manage', element: page(ScriptManagePage) },
|
||||
{ path: '/script-builder', element: page(ScriptBuilderPage) },
|
||||
{ path: '/network-diagrams', element: page(NetworkDiagramsPage) },
|
||||
{ path: '/network-diagrams/new', element: page(DiagramEditorPage) },
|
||||
{ path: '/network-diagrams/:id', element: page(DiagramEditorPage) },
|
||||
{ path: '/kb-accelerator', element: page(KBAcceleratorPage) },
|
||||
// Phase 1 — FlowPilot migration. The unified chat-primary surface lives at
|
||||
// /pilot; /assistant permanently redirects. FlowPilotSessionPage (old
|
||||
// guided surface) is no longer mounted.
|
||||
{ path: 'pilot', element: page(AssistantChatPage) },
|
||||
{ path: 'pilot/:sessionId', element: page(AssistantChatPage) },
|
||||
{ path: 'assistant', element: <Navigate to="/pilot" replace /> },
|
||||
{ path: 'assistant/:sessionId', element: <AssistantSessionRedirect /> },
|
||||
{ path: 'flow-assist', element: page(FlowAssistPage) },
|
||||
{ path: 'escalations', element: page(EscalationQueuePage) },
|
||||
{ path: 'queue', element: page(SessionQueuePage) },
|
||||
{ path: 'review-queue', element: page(ReviewQueuePage) },
|
||||
{ path: 'analytics/flowpilot', element: page(FlowPilotAnalyticsPage) },
|
||||
{ path: 'dev/branching', element: page(DevBranchingPage) },
|
||||
{ path: 'guides', element: page(GuidesHubPage) },
|
||||
{ path: 'guides/:slug', element: page(GuideDetailPage) },
|
||||
{ path: '/pilot', element: page(AssistantChatPage) },
|
||||
{ path: '/pilot/:sessionId', element: page(AssistantChatPage) },
|
||||
{ path: '/assistant', element: <Navigate to="/pilot" replace /> },
|
||||
{ path: '/assistant/:sessionId', element: <AssistantSessionRedirect /> },
|
||||
{ path: '/flow-assist', element: page(FlowAssistPage) },
|
||||
{ path: '/escalations', element: page(EscalationQueuePage) },
|
||||
{ path: '/queue', element: page(SessionQueuePage) },
|
||||
{ path: '/review-queue', element: page(ReviewQueuePage) },
|
||||
{ path: '/analytics/flowpilot', element: page(FlowPilotAnalyticsPage) },
|
||||
{ path: '/dev/branching', element: page(DevBranchingPage) },
|
||||
{ path: '/guides', element: page(GuidesHubPage) },
|
||||
{ path: '/guides/:slug', element: page(GuideDetailPage) },
|
||||
// Welcome wizard (Phase 2). Mounted inside AppLayout so the email-
|
||||
// verification banner persists above each step.
|
||||
{ path: 'welcome', element: page(WelcomeRouter) },
|
||||
{ path: 'welcome/step-1', element: page(WelcomeStep1) },
|
||||
{ path: 'welcome/step-2', element: page(WelcomeStep2) },
|
||||
{ path: 'welcome/step-3', element: page(WelcomeStep3) },
|
||||
{ path: '/welcome', element: page(WelcomeRouter) },
|
||||
{ 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> },
|
||||
{ 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',
|
||||
path: '/admin',
|
||||
element: (
|
||||
<ErrorBoundary>
|
||||
<Suspense fallback={<PageLoader />}>
|
||||
@@ -327,7 +344,7 @@ export const router = sentryCreateBrowserRouter([
|
||||
},
|
||||
// Account routes
|
||||
{
|
||||
path: 'account',
|
||||
path: '/account',
|
||||
element: (
|
||||
<ErrorBoundary>
|
||||
<Suspense fallback={<PageLoader />}>
|
||||
|
||||
Reference in New Issue
Block a user