Appearance
Routes & Guards
DoCurious uses React Router v7 with createBrowserRouter. The application defines 195 routes, organized into public and protected sections. All page components are lazy-loaded for code splitting, and route access is controlled by composable guard components.
For the complete auto-generated route listing, see the Route Map (195 routes with component and guard info).
Route Organization
Routes are defined in src/routes/index.tsx and organized into two top-level groups:
Public Routes (no auth required)
These routes are accessible without authentication:
| Path | Page | Notes |
|---|---|---|
/login | Login | Eager-loaded for fast initial paint |
/register | Register | Eager-loaded |
/forgot-password | ForgotPassword | Eager-loaded |
/reset-password/:token | ResetPassword | Eager-loaded |
/verify-email | VerifyEmail | Eager-loaded |
/demo | DemoLanding | Demo persona selector |
/welcome | Landing | Public landing page |
/terms | TermsOfService | |
/privacy | PrivacyPolicy | |
/accessibility | AccessibilityStatement | |
/acceptable-use | AcceptableUsePolicy | |
/community-guidelines | CommunityGuidelines | |
/vendor-terms | VendorTerms | |
/onboarding | Onboarding | |
/onboarding/:role | RoleOnboarding | Role-specific onboarding |
/age-verification | AgeVerification | COPPA age check |
/consent/:token | ParentalConsent | Parent consent flow |
/vendor/apply | VendorApplication | Public vendor application |
Protected Routes (auth required)
All protected routes are children of the root / layout route, which is wrapped in AuthGuard and renders AppLayout (sidebar + header + content area).
Core: See Accounts
| Path | Page | Guard |
|---|---|---|
/ (index) | Dashboard | AuthGuard |
/account | Account | AuthGuard |
/account/delete | AccountDeletion | AuthGuard |
/account/data-export | DataExportRequest | AuthGuard |
/account/appeal | AppealForm | AuthGuard |
/account/linked-accounts | LinkedAccounts | AuthGuard |
/account/data-access-request | DataAccessRequest | AuthGuard |
/notifications | Notifications | AuthGuard |
Explore: See Explore
| Path | Page | Guard |
|---|---|---|
/explore | Explore | AuthGuard |
/explore/search | Explore (with query params) | AuthGuard |
/explore/best-fit | ExploreCategoryView | AuthGuard |
/explore/featured | ExploreCategoryView | AuthGuard |
/explore/free | ExploreCategoryView | AuthGuard |
/explore/popular | ExploreCategoryView | AuthGuard |
/explore/new | ExploreCategoryView | AuthGuard |
/explore/category/:slug | ExploreCategoryView | AuthGuard |
/explore/map | MapView | AuthGuard |
/dealers-choice | DealersChoicePage | AuthGuard |
Challenges: See Challenges and Track Records
| Path | Page | Guard |
|---|---|---|
/challenges/:id | ChallengeDetail | AuthGuard |
/my-challenges | MyChallenges | AuthGuard |
/my-challenges/:id/track | TrackRecord | AuthGuard |
/challenges/gift/:code | GiftRedemption | AuthGuard |
/challenges/group/:id | GroupChallenge | AuthGuard |
/challenges/submit | SubmitChallenge | AuthGuard |
Social: See Communities and Gifting
| Path | Page | Guard |
|---|---|---|
/saved | SavedList | AuthGuard + TierGuard |
/communities | Communities | AuthGuard |
/communities/:id | CommunityDetail | AuthGuard |
/communities/school | SchoolCommunity | AuthGuard |
/communities/grade | SchoolCommunity | AuthGuard |
/communities/class | SchoolCommunity | AuthGuard |
/communities/private | PrivateCommunities | AuthGuard + TierGuard |
/communities/:id/assignments/:id | AssignmentProgress | AuthGuard |
/communities/:id/goals | CommunityGoals | AuthGuard |
/gifts | Gifts | AuthGuard + TierGuard |
Progress & Gamification: See Gamification and Reflection
| Path | Page | Guard |
|---|---|---|
/learning-paths | LearningPaths | AuthGuard |
/learning-paths/:id | LearningPathDetail | AuthGuard |
/portfolios | Portfolios | AuthGuard |
/portfolios/:id | PortfolioView | AuthGuard |
/journey-map | JourneyMap | AuthGuard |
/badges | Badges | AuthGuard |
/xp-history | XPHistory | AuthGuard |
/reflections | MyReflections | AuthGuard |
Parent: See Parent role guide
| Path | Page | Guard |
|---|---|---|
/parent | ParentDashboard | RoleGuard (parent) |
Vendor: See Vendor guide and Vendor role guide
| Path | Page | Guard |
|---|---|---|
/vendor | VendorDashboard | RoleGuard (vendor) |
/vendor/challenges | VendorChallenges | RoleGuard (vendor) |
/vendor/challenges/create | VendorChallengeCreate | RoleGuard (vendor) |
/vendor/orders | VendorOrders | RoleGuard (vendor) |
/vendor/analytics | VendorAnalytics | RoleGuard (vendor) |
/vendor/events | VendorEvents | RoleGuard (vendor) |
/vendor/settings | VendorSettings | RoleGuard (vendor) |
School Administration: See School guide, Teacher role guide, and School Admin role guide
| Path | Page | Guard |
|---|---|---|
/school | SchoolDashboard | RoleGuard (head_school_admin, school_admin, teacher) |
/school/roster | Roster | RoleGuard (head_school_admin, school_admin) |
/school/classes | Classes | RoleGuard (head_school_admin, school_admin) |
/school/grades | Grades | RoleGuard (head_school_admin, school_admin, teacher) |
/school/teachers | Teachers | RoleGuard (head_school_admin, school_admin) |
/school/assignments | Assignments | RoleGuard (head_school_admin, school_admin, teacher) |
/school/classes/:id/progress | ClassProgress | RoleGuard (head_school_admin, school_admin, teacher) |
/school/reviews | TeacherReviews | RoleGuard (head_school_admin, school_admin, teacher) |
/school/surveys | Surveys | RoleGuard (head_school_admin, school_admin) |
/school/reflection-analytics | ReflectionAnalytics | RoleGuard (head_school_admin, school_admin, teacher) |
/school/purchase-requests | PurchaseRequestQueue | RoleGuard (head_school_admin, school_admin) |
/school/bulk-gift | BulkGiftChallenges | RoleGuard (head_school_admin, school_admin) |
/school/alumni-transition | AlumniTransition | RoleGuard (head_school_admin, school_admin) |
/school/my-assignments | Assignments | RoleGuard (student) |
/school/my-grades | Grades | RoleGuard (student) |
/school/child-grades | Grades | RoleGuard (parent) |
Platform Admin: See Admin guide and Platform Admin role guide
| Path | Page | Guard |
|---|---|---|
/admin/users | UserManagement | RoleGuard (platform_admin) |
/admin/users/:userId | UserDetail | RoleGuard (platform_admin) |
/admin/vendors | VendorManagement | RoleGuard (platform_admin, staff) |
/admin/challenges | ChallengeManagement | RoleGuard (platform_admin) |
/admin/challenge-series | ChallengeSeriesManagement | RoleGuard (platform_admin) |
/admin/challenge-queue | ChallengeApprovalQueue | RoleGuard (platform_admin, staff) |
/admin/verification | VerificationQueue | RoleGuard (platform_admin, staff) |
/admin/reviews | ReviewModeration | RoleGuard (platform_admin, staff) |
/admin/orders | OrderManagement | RoleGuard (platform_admin) |
/admin/coupons | CouponManagement | RoleGuard (platform_admin) |
/admin/schools | SchoolsManagement | RoleGuard (platform_admin) |
/admin/settings | PlatformSettings | RoleGuard (platform_admin) |
/admin/analytics | AnalyticsDashboard | RoleGuard (platform_admin) |
/admin/audit-log | AuditLog | RoleGuard (platform_admin) |
/admin/flagged-content | FlaggedContentQueue | RoleGuard (platform_admin, staff) |
/admin/roles | AdminRoles | RoleGuard (platform_admin) |
/admin/reflection-analytics | ReflectionAnalyticsAdmin | RoleGuard (platform_admin) |
/admin/featured-content | FeaturedContent | RoleGuard (platform_admin) |
/admin/school-health | SchoolHealthDashboard | RoleGuard (platform_admin) |
/admin/product-analytics | ProductAnalytics | RoleGuard (platform_admin) |
/admin/content-analytics | ContentAnalytics | RoleGuard (platform_admin) |
/admin/engineering-analytics | EngineeringAnalytics | RoleGuard (platform_admin) |
/admin/report-builder | ReportBuilder | RoleGuard (platform_admin) |
/admin/theme-editor | DesignTokenEditor | RoleGuard (platform_admin) |
/admin/component-showcase | ComponentShowcase | RoleGuard (platform_admin) |
/admin/feature-flags | FeatureFlagManagement | RoleGuard (platform_admin) |
Catch-all:
| Path | Behavior |
|---|---|
* | Redirect to / |
Guard Types
Guards are React components that wrap route content. They check conditions and either render children or redirect/show a fallback.
AuthGuard
Ensures the user is authenticated. Wraps the entire protected route tree.
tsx
// src/routes/guards/AuthGuard.tsx
interface AuthGuardProps {
children: React.ReactNode
}- Initializes auth state on mount
- Shows a spinner while loading
- Redirects to
/loginif not authenticated (saves attempted location for post-login redirect)
RoleGuard
Restricts access to specific user roles. Must be used inside AuthGuard.
tsx
// src/routes/guards/RoleGuard.tsx
interface RoleGuardProps {
children: React.ReactNode
roles: UserRole[] // Allowed roles
redirectTo?: string // Default: '/'
fallback?: React.ReactNode
}Example:
tsx
<RoleGuard roles={['platform_admin', 'staff']}>
<VerificationQueue />
</RoleGuard>TierGuard
Restricts Tier 1 (school-only) accounts from accessing personal features like saved lists, gifts, and private communities.
tsx
// src/routes/guards/TierGuard.tsx
interface TierGuardProps {
children: React.ReactNode
redirectTo?: string
fallback?: React.ReactNode // Default: TierRestrictedFallback
}Shows a "Feature Not Available" message for school-only accounts.
AgeGuard
Restricts content based on user age for COPPA compliance.
tsx
// src/routes/guards/AgeGuard.tsx
interface AgeGuardProps {
children: React.ReactNode
minAge?: number // Default: 13
redirectTo?: string
fallback?: React.ReactNode // Default: AgeRestrictedFallback
}ConsentGuard
Ensures under-13 users have parental consent for Tier 2 features.
tsx
// src/routes/guards/ConsentGuard.tsx
interface ConsentGuardProps {
children: React.ReactNode
redirectTo?: string
fallback?: React.ReactNode // Default: ConsentRequiredFallback
}Only blocks under-13 users whose parentalConsentStatus is not 'granted'.
AdminRoleGuard
Provides granular admin role checking beyond the basic platform_admin RoleGuard.
tsx
// src/routes/guards/AdminRoleGuard.tsx
interface AdminRoleGuardProps {
children: React.ReactNode
roles?: AdminRole[] // Required admin roles
requiredFeature?: AdminFeatureArea // Required feature area access
redirectTo?: string
fallback?: React.ReactNode
}Super admins (adminRole === 'super') always pass. Other admin roles are checked against the ADMIN_PERMISSIONS map.
ContextGuard
Protects routes based on the multi-context user model. Used by all school, admin, and vendor routes. Replaces RoleGuard for context-aware routing.
tsx
// src/routes/guards/ContextGuard.tsx
interface ContextGuardProps {
children: React.ReactNode
context: ContextType // 'personal' | 'school' | 'business' | 'platform'
roles?: string[] // Optional: required roles within that context
redirectTo?: string
fallback?: React.ReactNode
}Examples:
tsx
// School routes — user must have school context with admin or teacher role
<ContextGuard context="school" roles={['school_admin', 'head_school_admin', 'teacher']}>
<SchoolDashboard />
</ContextGuard>
// Platform admin routes — user must have platform context
<ContextGuard context="platform" roles={['admin']}>
<UserManagement />
</ContextGuard>
// Vendor routes — any business context
<ContextGuard context="business">
<VendorDashboard />
</ContextGuard>Uses hasContext and hasRoleInContext from src/lib/contextHelpers.ts for context resolution. Shows an "Access Restricted" fallback with a link to the appropriate context home route.
ParentGuard
Restricts access to routes that require linked child accounts. Parent is a person-level relationship, not a context.
tsx
// src/routes/guards/ParentGuard.tsx
interface ParentGuardProps {
children: React.ReactNode
redirectTo?: string
fallback?: React.ReactNode // Default: NotAParentFallback
}Uses hasLinkedChildren() from src/lib/contextHelpers.ts. Shows a "Parent Access Required" message for non-parent users.
Guard Composition Pattern
Guards compose by nesting. The outermost guard checks first:
tsx
// AuthGuard wraps the entire layout
<AuthGuard>
<AppLayout>
{/* RoleGuard checks role within authenticated context */}
<RoleGuard roles={['school_admin', 'teacher']}>
{/* TierGuard checks account tier */}
<TierGuard>
<LazyPage><SomePage /></LazyPage>
</TierGuard>
</RoleGuard>
</AppLayout>
</AuthGuard>Lazy Loading Pattern
All page components use React.lazy() with named exports:
typescript
const Dashboard = lazy(() =>
import('../pages/Dashboard').then(m => ({ default: m.Dashboard }))
)Every lazy route is wrapped in LazyPage, which provides Suspense with a skeleton fallback and an ErrorBoundary:
tsx
function LazyPage({ children }: { children: React.ReactNode }) {
return (
<ErrorBoundary>
<Suspense fallback={<PageSkeleton />}>
{children}
</Suspense>
</ErrorBoundary>
)
}Auth pages (Login, Register, ForgotPassword, ResetPassword, VerifyEmail) are eagerly imported for fast initial load since they are the first pages users see.
How to Add a New Route
- Create the page component in
src/pages/myDomain/MyPage.tsx:
tsx
export function MyPage() {
return <div>My Page Content</div>
}- Add lazy import at the top of
src/routes/index.tsx:
typescript
const MyPage = lazy(() =>
import('../pages/myDomain/MyPage').then(m => ({ default: m.MyPage }))
)- Add the route in the appropriate section:
tsx
// Inside the protected routes children array:
{
path: 'my-page',
element: (
<RoleGuard roles={['platform_admin']}>
<LazyPage><MyPage /></LazyPage>
</RoleGuard>
),
},- Add navigation -- update the sidebar navigation in
src/components/layout/Sidebar.tsxto include a link to the new route.