198 lines
8.3 KiB
TypeScript
198 lines
8.3 KiB
TypeScript
import React, { Suspense, lazy } from 'react';
|
|
import { BrowserRouter, Routes, Route, Navigate } from 'react-router-dom';
|
|
import { QueryClientProvider } from '@tanstack/react-query';
|
|
import { queryClient } from './lib/queryClient';
|
|
import { AuthProvider, ProtectedRoute, TenantAdminProtectedRoute } from './auth/AuthProvider';
|
|
import { ClaimProtectedRoute, TenantManagementProtectedRoute, useRbac } from './auth/rbac';
|
|
import { ToastProvider } from './components/ui/Toast';
|
|
import { AppLayout } from './components/layout/AppLayout';
|
|
|
|
const LandingPage = lazy(() => import('./pages/LandingPage').then(m => ({ default: m.LandingPage })));
|
|
const ExecutiveDashboard = lazy(() => import('./pages/ExecutiveDashboard').then(m => ({ default: m.ExecutiveDashboard })));
|
|
const GovernancePage = lazy(() => import('./pages/GovernancePage').then(m => ({ default: m.GovernancePage })));
|
|
const ProjectsPage = lazy(() => import('./pages/ProjectsPage').then(m => ({ default: m.ProjectsPage })));
|
|
const BeneficiariesPage = lazy(() => import('./pages/BeneficiariesPage').then(m => ({ default: m.BeneficiariesPage })));
|
|
const ChangeManagementPage = lazy(() => import('./pages/ChangeManagementPage').then(m => ({ default: m.ChangeManagementPage })));
|
|
const SiteVisitsPage = lazy(() => import('./pages/SiteVisitsPage').then(m => ({ default: m.SiteVisitsPage })));
|
|
const CompliancePage = lazy(() => import('./pages/CompliancePage').then(m => ({ default: m.CompliancePage })));
|
|
const GrantsPage = lazy(() => import('./pages/GrantsPage').then(m => ({ default: m.GrantsPage })));
|
|
const MonitoringPage = lazy(() => import('./pages/MonitoringPage').then(m => ({ default: m.MonitoringPage })));
|
|
const TenantManagementPage = lazy(() => import('./pages/TenantManagementPage').then(m => ({ default: m.TenantManagementPage })));
|
|
const SettingsPage = lazy(() => import('./pages/SettingsPage').then(m => ({ default: m.SettingsPage })));
|
|
|
|
const RouteLoading = () => (
|
|
<div className="flex items-center justify-center min-h-[400px] w-full text-slate-400">
|
|
<div className="flex flex-col items-center gap-3">
|
|
<div className="w-7 h-7 border-2 border-blue-500 border-t-transparent rounded-full animate-spin" />
|
|
<span className="text-xs font-medium text-slate-400">Loading module...</span>
|
|
</div>
|
|
</div>
|
|
);
|
|
|
|
const DefaultRouteRedirect = () => {
|
|
const { canView } = useRbac();
|
|
if (canView('dashboard')) return <Navigate to="/dashboard" replace />;
|
|
if (canView('projects_milestones')) return <Navigate to="/projects" replace />;
|
|
if (canView('grant_financials')) return <Navigate to="/grants" replace />;
|
|
if (canView('evidence_risks')) return <Navigate to="/monitoring" replace />;
|
|
if (canView('governance')) return <Navigate to="/governance" replace />;
|
|
if (canView('beneficiaries')) return <Navigate to="/projects/beneficiaries" replace />;
|
|
if (canView('settings_users') || canView('settings_invites')) return <Navigate to="/settings" replace />;
|
|
return <Navigate to="/projects" replace />;
|
|
};
|
|
|
|
export default function App() {
|
|
return (
|
|
<QueryClientProvider client={queryClient}>
|
|
<BrowserRouter>
|
|
<AuthProvider>
|
|
<ToastProvider>
|
|
<Suspense fallback={<RouteLoading />}>
|
|
<Routes>
|
|
{/* Public OIDC Login / Landing Page */}
|
|
<Route path="/" element={<LandingPage />} />
|
|
<Route path="/login" element={<LandingPage />} />
|
|
|
|
{/* Protected Backoffice Application Shell */}
|
|
<Route
|
|
element={
|
|
<ProtectedRoute>
|
|
<AppLayout />
|
|
</ProtectedRoute>
|
|
}
|
|
>
|
|
<Route
|
|
path="/dashboard"
|
|
element={
|
|
<ClaimProtectedRoute claim="dashboard">
|
|
<ExecutiveDashboard />
|
|
</ClaimProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/governance"
|
|
element={
|
|
<ClaimProtectedRoute claim="governance">
|
|
<GovernancePage />
|
|
</ClaimProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/projects"
|
|
element={
|
|
<ClaimProtectedRoute claim="projects_milestones">
|
|
<ProjectsPage />
|
|
</ClaimProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/projects/beneficiaries"
|
|
element={
|
|
<ClaimProtectedRoute claim="beneficiaries">
|
|
<BeneficiariesPage />
|
|
</ClaimProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/beneficiaries"
|
|
element={
|
|
<ClaimProtectedRoute claim="beneficiaries">
|
|
<BeneficiariesPage />
|
|
</ClaimProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/projects/change-management"
|
|
element={
|
|
<ClaimProtectedRoute claim="change_management">
|
|
<ChangeManagementPage />
|
|
</ClaimProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/change-management"
|
|
element={
|
|
<ClaimProtectedRoute claim="change_management">
|
|
<ChangeManagementPage />
|
|
</ClaimProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/projects/site-visits"
|
|
element={
|
|
<ClaimProtectedRoute claim="site_visits">
|
|
<SiteVisitsPage />
|
|
</ClaimProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/site-visits"
|
|
element={
|
|
<ClaimProtectedRoute claim="site_visits">
|
|
<SiteVisitsPage />
|
|
</ClaimProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/projects/compliance"
|
|
element={
|
|
<ClaimProtectedRoute claim="compliance">
|
|
<CompliancePage />
|
|
</ClaimProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/compliance"
|
|
element={
|
|
<ClaimProtectedRoute claim="compliance">
|
|
<CompliancePage />
|
|
</ClaimProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/grants"
|
|
element={
|
|
<ClaimProtectedRoute claim="grant_financials">
|
|
<GrantsPage />
|
|
</ClaimProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/monitoring"
|
|
element={
|
|
<ClaimProtectedRoute claim="evidence_risks">
|
|
<MonitoringPage />
|
|
</ClaimProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/tenants"
|
|
element={
|
|
<TenantManagementProtectedRoute>
|
|
<TenantManagementPage />
|
|
</TenantManagementProtectedRoute>
|
|
}
|
|
/>
|
|
<Route
|
|
path="/governance/tenants"
|
|
element={
|
|
<TenantManagementProtectedRoute>
|
|
<TenantManagementPage />
|
|
</TenantManagementProtectedRoute>
|
|
}
|
|
/>
|
|
<Route path="/settings" element={<SettingsPage />} />
|
|
</Route>
|
|
|
|
{/* Fallback Redirect */}
|
|
<Route path="*" element={<DefaultRouteRedirect />} />
|
|
</Routes>
|
|
</Suspense>
|
|
</ToastProvider>
|
|
</AuthProvider>
|
|
</BrowserRouter>
|
|
</QueryClientProvider>
|
|
);
|
|
}
|
|
|