Skip to main content

Routing

The frontend uses React Router 7 with nested routes, auth guards, role-based route protection, and lazy-loaded page components.


Route Architecture


Route Protection

AuthGuard

Wraps all authenticated routes. Checks for a valid JWT in the auth store and redirects to /login if not authenticated.

<Route element={<AuthGuard />}>
<Route element={<AppLayout />}>
<Route path="/" element={<Dashboard />} />
<Route path="/composer" element={<Composer />} />
<Route path="/posts" element={<Posts />} />
<Route path="/analytics" element={<Analytics />} />
{/* ... all authenticated routes */}
</Route>
</Route>

RoleGuard

Restricts routes to users with a minimum role level in the current workspace:

{/* EDITOR+ routes */}
<Route element={<RoleGuard minRole="EDITOR" />}>
<Route path="/composer" element={<Composer />} />
<Route path="/composer/:id" element={<Composer />} />
<Route path="/workflows/:id" element={<WorkflowEditor />} />
</Route>

{/* ADMIN+ routes */}
<Route element={<RoleGuard minRole="ADMIN" />}>
<Route path="/accounts" element={<Accounts />} />
<Route path="/conversation-engine" element={<ConversationEngine />} />
<Route path="/checkout" element={<Checkout />} />
</Route>

{/* Super admin routes */}
<Route element={<RoleGuard minRole="SUPER_ADMIN" />}>
<Route path="/admin" element={<AdminDashboard />} />
<Route path="/admin/users" element={<AdminUsers />} />
<Route path="/admin/ai-usage" element={<AdminAIUsage />} />
<Route path="/admin/billing" element={<AdminBilling />} />
</Route>

Role Hierarchy

RoleLevelAccess
OWNER5Everything
ADMIN4All except owner-only
EDITOR3Content creation and management
VIEWER2Read-only dashboards
CLIENT1Client portal only

Lazy Loading

All page components are lazy-loaded for optimal bundle size:

import { lazy, Suspense } from 'react';

const Composer = lazy(() => import('./pages/composer/Composer'));
const Analytics = lazy(() => import('./pages/analytics/Analytics'));
const WorkflowEditor = lazy(() => import('./pages/scheduler/WorkflowEditor'));

// Wrapped in Suspense with a loading fallback
<Suspense fallback={<PageSkeleton />}>
<Route path="/composer" element={<Composer />} />
</Suspense>

The sidebar navigation is driven by a configuration array that respects role-based visibility:

const navigationItems = [
{ label: 'Dashboard', path: '/', icon: LayoutDashboard, minRole: 'VIEWER' },
{ label: 'Composer', path: '/composer', icon: PenSquare, minRole: 'EDITOR' },
{ label: 'Posts', path: '/posts', icon: FileText, minRole: 'VIEWER' },
{ label: 'Scheduler', path: '/scheduler', icon: Calendar, minRole: 'VIEWER' },
{ label: 'Analytics', path: '/analytics', icon: BarChart3, minRole: 'VIEWER' },
{ label: 'Conversations', path: '/conversations', icon: MessageSquare, minRole: 'EDITOR' },
{ label: 'Workflows', path: '/workflows', icon: GitBranch, minRole: 'EDITOR' },
{ label: 'Audience', path: '/audience', icon: Users, minRole: 'VIEWER' },
{ label: 'Competitors', path: '/competitors', icon: Target, minRole: 'VIEWER' },
{ label: 'E-Commerce', path: '/ecommerce', icon: ShoppingBag, minRole: 'EDITOR' },
{ label: 'Ads', path: '/ads', icon: Megaphone, minRole: 'EDITOR' },
// ... filtered by current user's role
];

Items are filtered at render time -- users only see navigation for pages they have access to.


Route Parameters

Route PatternParamUsage
/composer/:ididEdit an existing post
/analytics/posts/:ididView analytics for a specific post
/workflows/:ididEdit a workflow in the visual builder
/competitors/:ididView competitor detail and history
/reset-password/:tokentokenPassword reset token from email link

Public Routes (No Auth Required)

RoutePage
/loginLogin
/registerRegister
/forgot-passwordForgot Password
/reset-password/:tokenReset Password
/landingLanding Page
/pricingPlan Comparison

Cross-Reference