template-saas-frontend-v2/src/router/index.tsx
Adrian Flores Cortes 2bfd90cdff [TASK-007] feat: P1 complete - MLM and Goals UI modules
## T-02.1: MLM Structure Pages (4 pages)
- MLMPage.tsx - Dashboard
- StructuresPage.tsx - List structures
- RanksPage.tsx - Manage ranks
- MyNetworkPage.tsx - User network view

## T-02.2: MLM Detail Pages (3 pages)
- StructureDetailPage.tsx
- NodeDetailPage.tsx
- MyEarningsPage.tsx

## T-02.3: Goals Structure Pages (3 pages)
- GoalsPage.tsx - Dashboard
- DefinitionsPage.tsx - Create/edit goals
- MyGoalsPage.tsx - User's assigned goals

## T-02.4: Goals Detail Pages (3 pages)
- GoalDetailPage.tsx
- AssignmentDetailPage.tsx
- ReportsPage.tsx

## T-02.5 & T-02.6: Route Integration
- router/index.tsx: Added 13 new routes with lazy loading
- DashboardLayout.tsx: Added MLM and Goals to sidebar nav

Total: 13 new pages, 13 new routes

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 12:53:56 -06:00

232 lines
12 KiB
TypeScript

import { lazy, Suspense } from 'react';
import { Routes, Route, Navigate } from 'react-router-dom';
import { useAuthStore } from '@/stores/auth.store';
// Layouts - kept as static imports since they're used on every page
import { AuthLayout } from '@/layouts/AuthLayout';
import { DashboardLayout } from '@/layouts/DashboardLayout';
// Loading fallback component
function PageLoader() {
return (
<div className="flex items-center justify-center min-h-[400px]">
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-primary-600"></div>
</div>
);
}
// Lazy loaded pages - Auth
const LoginPage = lazy(() => import('@/pages/auth/LoginPage').then(m => ({ default: m.LoginPage })));
const RegisterPage = lazy(() => import('@/pages/auth/RegisterPage').then(m => ({ default: m.RegisterPage })));
const ForgotPasswordPage = lazy(() => import('@/pages/auth/ForgotPasswordPage').then(m => ({ default: m.ForgotPasswordPage })));
const OAuthCallbackPage = lazy(() => import('@/pages/auth/OAuthCallbackPage').then(m => ({ default: m.OAuthCallbackPage })));
// Lazy loaded pages - Dashboard
const DashboardPage = lazy(() => import('@/pages/dashboard/DashboardPage').then(m => ({ default: m.DashboardPage })));
const BillingPage = lazy(() => import('@/pages/dashboard/BillingPage').then(m => ({ default: m.BillingPage })));
const UsersPage = lazy(() => import('@/pages/dashboard/UsersPage').then(m => ({ default: m.UsersPage })));
const AIPage = lazy(() => import('@/pages/dashboard/AIPage').then(m => ({ default: m.AIPage })));
const StoragePage = lazy(() => import('@/pages/dashboard/StoragePage').then(m => ({ default: m.StoragePage })));
const WebhooksPage = lazy(() => import('@/pages/dashboard/WebhooksPage').then(m => ({ default: m.WebhooksPage })));
const AuditLogsPage = lazy(() => import('@/pages/dashboard/AuditLogsPage').then(m => ({ default: m.AuditLogsPage })));
const FeatureFlagsPage = lazy(() => import('@/pages/dashboard/FeatureFlagsPage').then(m => ({ default: m.FeatureFlagsPage })));
// Lazy loaded pages - Sales
const SalesPage = lazy(() => import('@/pages/dashboard/sales').then(m => ({ default: m.SalesPage })));
const LeadsPage = lazy(() => import('@/pages/dashboard/sales').then(m => ({ default: m.LeadsPage })));
const LeadDetailPage = lazy(() => import('@/pages/dashboard/sales').then(m => ({ default: m.LeadDetailPage })));
const OpportunitiesPage = lazy(() => import('@/pages/dashboard/sales').then(m => ({ default: m.OpportunitiesPage })));
const OpportunityDetailPage = lazy(() => import('@/pages/dashboard/sales').then(m => ({ default: m.OpportunityDetailPage })));
const ActivitiesPage = lazy(() => import('@/pages/dashboard/sales').then(m => ({ default: m.ActivitiesPage })));
// Lazy loaded pages - Commissions
const CommissionsPage = lazy(() => import('@/pages/dashboard/commissions').then(m => ({ default: m.CommissionsPage })));
const SchemesPage = lazy(() => import('@/pages/dashboard/commissions').then(m => ({ default: m.SchemesPage })));
const EntriesPage = lazy(() => import('@/pages/dashboard/commissions').then(m => ({ default: m.EntriesPage })));
const PeriodsPage = lazy(() => import('@/pages/dashboard/commissions').then(m => ({ default: m.PeriodsPage })));
const MyEarningsPage = lazy(() => import('@/pages/dashboard/commissions').then(m => ({ default: m.MyEarningsPage })));
// Lazy loaded pages - Goals
const GoalsPage = lazy(() => import('@/pages/dashboard/goals').then(m => ({ default: m.GoalsPage })));
const GoalDefinitionsPage = lazy(() => import('@/pages/dashboard/goals').then(m => ({ default: m.DefinitionsPage })));
const MyGoalsPage = lazy(() => import('@/pages/dashboard/goals').then(m => ({ default: m.MyGoalsPage })));
const GoalDetailPage = lazy(() => import('@/pages/dashboard/goals').then(m => ({ default: m.GoalDetailPage })));
const AssignmentDetailPage = lazy(() => import('@/pages/dashboard/goals').then(m => ({ default: m.AssignmentDetailPage })));
const GoalReportsPage = lazy(() => import('@/pages/dashboard/goals').then(m => ({ default: m.ReportsPage })));
// Lazy loaded pages - MLM
const MLMPage = lazy(() => import('@/pages/dashboard/mlm').then(m => ({ default: m.MLMPage })));
const StructuresPage = lazy(() => import('@/pages/dashboard/mlm').then(m => ({ default: m.StructuresPage })));
const StructureDetailPage = lazy(() => import('@/pages/dashboard/mlm').then(m => ({ default: m.StructureDetailPage })));
const RanksPage = lazy(() => import('@/pages/dashboard/mlm').then(m => ({ default: m.RanksPage })));
const MyNetworkPage = lazy(() => import('@/pages/dashboard/mlm').then(m => ({ default: m.MyNetworkPage })));
const NodeDetailPage = lazy(() => import('@/pages/dashboard/mlm').then(m => ({ default: m.NodeDetailPage })));
const MLMMyEarningsPage = lazy(() => import('@/pages/dashboard/mlm').then(m => ({ default: m.MyEarningsPage })));
// Lazy loaded pages - Admin
const WhatsAppSettings = lazy(() => import('@/pages/admin/WhatsAppSettings').then(m => ({ default: m.WhatsAppSettings })));
const AnalyticsDashboardPage = lazy(() => import('@/pages/admin/AnalyticsDashboardPage').then(m => ({ default: m.AnalyticsDashboardPage })));
// Lazy loaded pages - Settings
const SettingsPage = lazy(() => import('@/pages/settings').then(m => ({ default: m.SettingsPage })));
// Lazy loaded pages - Superadmin
const TenantsPage = lazy(() => import('@/pages/superadmin').then(m => ({ default: m.TenantsPage })));
const TenantDetailPage = lazy(() => import('@/pages/superadmin').then(m => ({ default: m.TenantDetailPage })));
const MetricsPage = lazy(() => import('@/pages/superadmin').then(m => ({ default: m.MetricsPage })));
// Lazy loaded pages - Onboarding
const OnboardingPage = lazy(() => import('@/pages/onboarding').then(m => ({ default: m.OnboardingPage })));
// Protected Route wrapper
function ProtectedRoute({ children }: { children: React.ReactNode }) {
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
if (!isAuthenticated) {
return <Navigate to="/auth/login" replace />;
}
return <>{children}</>;
}
// Superadmin Route wrapper (requires superadmin role)
function SuperadminRoute({ children }: { children: React.ReactNode }) {
const { isAuthenticated, user } = useAuthStore();
if (!isAuthenticated) {
return <Navigate to="/auth/login" replace />;
}
// Check if user has superadmin role
// In a real app, this would check against the user's roles from the API
if (user?.role !== 'superadmin') {
return <Navigate to="/dashboard" replace />;
}
return <>{children}</>;
}
// Guest Route wrapper (redirect to dashboard if already logged in)
function GuestRoute({ children }: { children: React.ReactNode }) {
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
if (isAuthenticated) {
return <Navigate to="/dashboard" replace />;
}
return <>{children}</>;
}
// Suspense wrapper for lazy loaded pages
function SuspensePage({ children }: { children: React.ReactNode }) {
return <Suspense fallback={<PageLoader />}>{children}</Suspense>;
}
export function AppRouter() {
return (
<Routes>
{/* Public routes */}
<Route path="/" element={<Navigate to="/auth/login" replace />} />
{/* Auth routes */}
<Route
path="/auth"
element={
<GuestRoute>
<AuthLayout />
</GuestRoute>
}
>
<Route path="login" element={<SuspensePage><LoginPage /></SuspensePage>} />
<Route path="register" element={<SuspensePage><RegisterPage /></SuspensePage>} />
<Route path="forgot-password" element={<SuspensePage><ForgotPasswordPage /></SuspensePage>} />
</Route>
{/* OAuth callback route - outside GuestRoute since user may be authenticated after callback */}
<Route path="/auth/oauth/callback/:provider" element={<SuspensePage><OAuthCallbackPage /></SuspensePage>} />
{/* Dashboard routes */}
<Route
path="/dashboard"
element={
<ProtectedRoute>
<DashboardLayout />
</ProtectedRoute>
}
>
<Route index element={<SuspensePage><DashboardPage /></SuspensePage>} />
<Route path="settings" element={<SuspensePage><SettingsPage /></SuspensePage>} />
<Route path="billing" element={<SuspensePage><BillingPage /></SuspensePage>} />
<Route path="users" element={<SuspensePage><UsersPage /></SuspensePage>} />
<Route path="ai" element={<SuspensePage><AIPage /></SuspensePage>} />
<Route path="storage" element={<SuspensePage><StoragePage /></SuspensePage>} />
<Route path="webhooks" element={<SuspensePage><WebhooksPage /></SuspensePage>} />
<Route path="audit" element={<SuspensePage><AuditLogsPage /></SuspensePage>} />
<Route path="feature-flags" element={<SuspensePage><FeatureFlagsPage /></SuspensePage>} />
<Route path="whatsapp" element={<SuspensePage><WhatsAppSettings /></SuspensePage>} />
<Route path="analytics" element={<SuspensePage><AnalyticsDashboardPage /></SuspensePage>} />
{/* Sales routes */}
<Route path="sales" element={<SuspensePage><SalesPage /></SuspensePage>} />
<Route path="sales/leads" element={<SuspensePage><LeadsPage /></SuspensePage>} />
<Route path="sales/leads/:id" element={<SuspensePage><LeadDetailPage /></SuspensePage>} />
<Route path="sales/opportunities" element={<SuspensePage><OpportunitiesPage /></SuspensePage>} />
<Route path="sales/opportunities/:id" element={<SuspensePage><OpportunityDetailPage /></SuspensePage>} />
<Route path="sales/activities" element={<SuspensePage><ActivitiesPage /></SuspensePage>} />
{/* Commissions routes */}
<Route path="commissions" element={<SuspensePage><CommissionsPage /></SuspensePage>} />
<Route path="commissions/schemes" element={<SuspensePage><SchemesPage /></SuspensePage>} />
<Route path="commissions/entries" element={<SuspensePage><EntriesPage /></SuspensePage>} />
<Route path="commissions/periods" element={<SuspensePage><PeriodsPage /></SuspensePage>} />
<Route path="commissions/my-earnings" element={<SuspensePage><MyEarningsPage /></SuspensePage>} />
{/* Goals routes */}
<Route path="goals" element={<SuspensePage><GoalsPage /></SuspensePage>} />
<Route path="goals/definitions" element={<SuspensePage><GoalDefinitionsPage /></SuspensePage>} />
<Route path="goals/definitions/:id" element={<SuspensePage><GoalDetailPage /></SuspensePage>} />
<Route path="goals/my-goals" element={<SuspensePage><MyGoalsPage /></SuspensePage>} />
<Route path="goals/assignments/:id" element={<SuspensePage><AssignmentDetailPage /></SuspensePage>} />
<Route path="goals/reports" element={<SuspensePage><GoalReportsPage /></SuspensePage>} />
{/* MLM routes */}
<Route path="mlm" element={<SuspensePage><MLMPage /></SuspensePage>} />
<Route path="mlm/structures" element={<SuspensePage><StructuresPage /></SuspensePage>} />
<Route path="mlm/structures/:id" element={<SuspensePage><StructureDetailPage /></SuspensePage>} />
<Route path="mlm/ranks" element={<SuspensePage><RanksPage /></SuspensePage>} />
<Route path="mlm/my-network" element={<SuspensePage><MyNetworkPage /></SuspensePage>} />
<Route path="mlm/nodes/:id" element={<SuspensePage><NodeDetailPage /></SuspensePage>} />
<Route path="mlm/my-earnings" element={<SuspensePage><MLMMyEarningsPage /></SuspensePage>} />
</Route>
{/* Superadmin routes */}
<Route
path="/superadmin"
element={
<SuperadminRoute>
<DashboardLayout />
</SuperadminRoute>
}
>
<Route index element={<Navigate to="/superadmin/tenants" replace />} />
<Route path="tenants" element={<SuspensePage><TenantsPage /></SuspensePage>} />
<Route path="tenants/:id" element={<SuspensePage><TenantDetailPage /></SuspensePage>} />
<Route path="metrics" element={<SuspensePage><MetricsPage /></SuspensePage>} />
</Route>
{/* Onboarding route */}
<Route
path="/onboarding"
element={
<ProtectedRoute>
<SuspensePage><OnboardingPage /></SuspensePage>
</ProtectedRoute>
}
/>
{/* 404 */}
<Route path="*" element={<Navigate to="/" replace />} />
</Routes>
);
}