## 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>
232 lines
12 KiB
TypeScript
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>
|
|
);
|
|
}
|