Files
leocrm/frontend/src/routes/index.tsx
T
2026-07-30 20:03:10 +02:00

209 lines
14 KiB
TypeScript

import React, { Suspense } from 'react';
import { createBrowserRouter, RouterProvider, Navigate } from 'react-router-dom';
import { AppShell } from '@/components/layout/AppShell';
import { StartLayout } from '@/components/layout/StartLayout';
import { ProtectedRoute } from './ProtectedRoute';
import { ProtectedRoute as PermissionRoute } from '@/components/common/ProtectedRoute';
import { LoginPage } from '@/pages/Login';
import { GuestLoginPage } from '@/pages/GuestLogin';
import { GuestContactsPage } from '@/pages/GuestContacts';
import { PasswordResetRequestPage } from '@/pages/PasswordResetRequest';
import { PasswordResetConfirmPage } from '@/pages/PasswordResetConfirm';
import { Loader2 } from 'lucide-react';
import { PluginRouteRenderer } from '@/components/plugins/PluginRouteRenderer';
import { ErrorBoundary } from '@/components/ErrorBoundary';
// Lazy-loaded pages (code-splitting)
const DashboardPage = React.lazy(() => import('@/pages/Dashboard').then(m => ({ default: m.DashboardPage })));
const ContactsListPage = React.lazy(() => import('@/pages/ContactsList').then(m => ({ default: m.ContactsListPage })));
const ContactDetailPage = React.lazy(() => import('@/pages/ContactDetailPage').then(m => ({ default: m.ContactDetailPage })));
const AuditLogPage = React.lazy(() => import('@/pages/AuditLog').then(m => ({ default: m.AuditLogPage })));
const GlobalSearchResultsPage = React.lazy(() => import('@/pages/GlobalSearchResults').then(m => ({ default: m.GlobalSearchResultsPage })));
const SettingsPage = React.lazy(() => import('@/pages/Settings').then(m => ({ default: m.SettingsPage })));
const SettingsProfilePage = React.lazy(() => import('@/pages/SettingsProfile').then(m => ({ default: m.SettingsProfilePage })));
const SettingsRolesPage = React.lazy(() => import('@/pages/SettingsRoles').then(m => ({ default: m.SettingsRolesPage })));
const SettingsUsersPage = React.lazy(() => import('@/pages/SettingsUsers').then(m => ({ default: m.SettingsUsersPage })));
const SettingsGroupsPage = React.lazy(() => import('@/pages/SettingsGroups').then(m => ({ default: m.SettingsGroupsPage })));
const SettingsPluginsPage = React.lazy(() => import('@/pages/SettingsPlugins').then(m => ({ default: m.SettingsPluginsPage })));
const SettingsSystemPage = React.lazy(() => import('@/pages/SettingsSystem').then(m => ({ default: m.SettingsSystemPage })));
const SettingsCurrenciesPage = React.lazy(() => import('@/pages/SettingsCurrencies').then(m => ({ default: m.SettingsCurrenciesPage })));
const SettingsTaxesPage = React.lazy(() => import('@/pages/SettingsTaxes').then(m => ({ default: m.SettingsTaxesPage })));
const SettingsSequencesPage = React.lazy(() => import('@/pages/SettingsSequences').then(m => ({ default: m.SettingsSequencesPage })));
const CalendarPage = React.lazy(() => import('@/pages/Calendar').then(m => ({ default: m.CalendarPage })));
const CalendarKanbanPage = React.lazy(() => import('@/pages/CalendarKanban').then(m => ({ default: m.CalendarKanbanPage })));
const DmsPage = React.lazy(() => import('@/pages/Dms').then(m => ({ default: m.DmsPage })));
const DmsTrashPage = React.lazy(() => import('@/pages/DmsTrash').then(m => ({ default: m.DmsTrashPage })));
const MailPage = React.lazy(() => import('@/pages/Mail').then(m => ({ default: m.MailPage })));
const MailSettingsPage = React.lazy(() => import('@/pages/MailSettings').then(m => ({ default: m.MailSettingsPage })));
const SettingsNotificationsPage = React.lazy(() => import('@/pages/SettingsNotifications').then(m => ({ default: m.SettingsNotificationsPage })));
const AIAssistantPage = React.lazy(() => import('@/pages/AIAssistant').then(m => ({ default: m.AIAssistantPage })));
const AISettingsPage = React.lazy(() => import('@/pages/AISettings').then(m => ({ default: m.AISettingsPage })));
const ProactiveAISettings = React.lazy(() => import('@/pages/ProactiveAISettings').then(m => ({ default: m.ProactiveAISettings })));
const SettingsThemePage = React.lazy(() => import('@/pages/SettingsTheme').then(m => ({ default: m.SettingsThemePage })));
const SettingsMcpPage = React.lazy(() => import('@/pages/SettingsMcp').then(m => ({ default: m.SettingsMcpPage })));
const AIAssistantStandalonePage = React.lazy(() => import('@/pages/AIAssistantStandalone').then(m => ({ default: m.AIAssistantStandalonePage })));
const DmsStandalonePage = React.lazy(() => import('@/pages/DmsStandalone').then(m => ({ default: m.DmsStandalonePage })));
const CalendarStandalonePage = React.lazy(() => import('@/pages/CalendarStandalone').then(m => ({ default: m.CalendarStandalonePage })));
const MailStandalonePage = React.lazy(() => import('@/pages/MailStandalone').then(m => ({ default: m.MailStandalonePage })));
const ContactsStandalonePage = React.lazy(() => import('@/pages/ContactsStandalone').then(m => ({ default: m.ContactsStandalonePage })));
const SettingsAIPage = React.lazy(() => import('@/pages/SettingsAI').then(m => ({ default: m.SettingsAIPage })));
const SettingsMenuOrderPage = React.lazy(() => import('@/pages/SettingsMenuOrder').then(m => ({ default: m.SettingsMenuOrderPage })));
const SettingsStammdatenPage = React.lazy(() => import('@/pages/SettingsStammdaten').then(m => ({ default: m.SettingsStammdatenPage })));
const SettingsUserManagementPage = React.lazy(() => import('@/pages/SettingsUserManagement').then(m => ({ default: m.SettingsUserManagementPage })));
const AutomationDashboardPage = React.lazy(() => import('@/pages/AutomationDashboard').then(m => ({ default: m.AutomationDashboardPage })));
const AgentDashboardPage = React.lazy(() => import('@/pages/AgentDashboard').then(m => ({ default: m.AgentDashboardPage })));
const AutomationSettingsPage = React.lazy(() => import('@/pages/AutomationSettings').then(m => ({ default: m.AutomationSettingsPage })));
const ReportsPage = React.lazy(() => import('@/pages/Reports').then(m => ({ default: m.ReportsPage })));
const TasksPage = React.lazy(() => import('@/pages/Tasks').then(m => ({ default: m.TasksPage })));
const CommunicationPage = React.lazy(() => import('@/pages/Communication').then(m => ({ default: m.CommunicationPage })));
const WorkflowsPage = React.lazy(() => import('@/pages/Workflows').then(m => ({ default: m.WorkflowsPage })));
const DedupMergePage = React.lazy(() => import('@/pages/DedupMerge').then(m => ({ default: m.DedupMergePage })));
const ImportExportPage = React.lazy(() => import('@/pages/ImportExport').then(m => ({ default: m.ImportExportPage })));
const TagsPage = React.lazy(() => import('@/pages/Tags').then(m => ({ default: m.TagsPage })));
const CustomFieldsPage = React.lazy(() => import('@/pages/CustomFields').then(m => ({ default: m.CustomFieldsPage })));
const ActivityTimelinePage = React.lazy(() => import('@/pages/ActivityTimeline').then(m => ({ default: m.ActivityTimelinePage })));
const SettingsWebhooksPage = React.lazy(() => import('@/pages/SettingsWebhooks').then(m => ({ default: m.SettingsWebhooksPage })));
const SettingsBackupPage = React.lazy(() => import('@/pages/SettingsBackup').then(m => ({ default: m.SettingsBackupPage })));
const SettingsRechtePage = React.lazy(() => import('@/pages/SettingsRechte').then(m => ({ default: m.SettingsRechtePage })));
const NoAccessPage = React.lazy(() => import('@/pages/NoAccessPage').then(m => ({ default: m.NoAccessPage })));
const StartPage = React.lazy(() => import('@/pages/StartPage').then(m => ({ default: m.StartPage })));
/** Centered spinner fallback for lazy-loaded routes */
function PageLoader() {
return (
<div className="flex items-center justify-center min-h-[50vh]" role="status" aria-label="Seite wird geladen">
<Loader2 className="animate-spin h-8 w-8 text-primary-500" aria-hidden="true" />
</div>
);
}
/** Wrap a lazy element in Suspense with PageLoader fallback */
function withSuspense(element: React.ReactElement) {
return <Suspense fallback={<PageLoader />}>{element}</Suspense>;
}
const router = createBrowserRouter([
{
path: '/login',
element: <LoginPage />,
},
{
path: '/ai-assistant-standalone',
element: <ErrorBoundary>{withSuspense(<AIAssistantStandalonePage />)}</ErrorBoundary>,
},
{
path: '/dms-standalone',
element: <ErrorBoundary>{withSuspense(<DmsStandalonePage />)}</ErrorBoundary>,
},
{
path: '/calendar-standalone',
element: <ErrorBoundary>{withSuspense(<CalendarStandalonePage />)}</ErrorBoundary>,
},
{
path: '/mail-standalone',
element: <ErrorBoundary>{withSuspense(<MailStandalonePage />)}</ErrorBoundary>,
},
{
path: '/contacts-standalone',
element: <ErrorBoundary>{withSuspense(<ContactsStandalonePage />)}</ErrorBoundary>,
},
{
path: '/password-reset',
element: <PasswordResetRequestPage />,
},
{
path: '/password-reset/confirm',
element: <PasswordResetConfirmPage />,
},
{
path: '/guest/login',
element: <GuestLoginPage />,
},
{
path: '/guest/contacts',
element: <GuestContactsPage />,
},
{
path: '/kein-zugriff',
element: <ErrorBoundary>{withSuspense(<NoAccessPage />)}</ErrorBoundary>,
},
{
element: (
<ProtectedRoute>
<StartLayout />
</ProtectedRoute>
),
children: [
{ path: '/start', element: withSuspense(<StartPage />) },
],
},
{
element: (
<ProtectedRoute>
<AppShell />
</ProtectedRoute>
),
children: [
{ path: '/', element: <Navigate to="/start" replace /> },
{ path: '/dashboard', element: withSuspense(<DashboardPage />) },
{ path: '/contacts', element: <PermissionRoute permission="contacts:read">{withSuspense(<ContactsListPage />)}</PermissionRoute> },
{ path: '/contacts/:id', element: <PermissionRoute permission="contacts:read">{withSuspense(<ContactDetailPage />)}</PermissionRoute> },
{ path: '/audit-log', element: <PermissionRoute permission="audit:read">{withSuspense(<AuditLogPage />)}</PermissionRoute> },
{ path: '/search', element: withSuspense(<GlobalSearchResultsPage />) },
{ path: '/calendar', element: <PermissionRoute permission="calendar:read">{withSuspense(<CalendarPage />)}</PermissionRoute> },
{ path: '/calendar/kanban', element: <PermissionRoute permission="calendar:read">{withSuspense(<CalendarKanbanPage />)}</PermissionRoute> },
{ path: '/dms', element: <PermissionRoute permission="dms:read">{withSuspense(<DmsPage />)}</PermissionRoute> },
{ path: '/dms/trash', element: <PermissionRoute permission="dms:read">{withSuspense(<DmsTrashPage />)}</PermissionRoute> },
{ path: '/mail', element: <PermissionRoute permission="mail:read">{withSuspense(<MailPage />)}</PermissionRoute> },
{ path: '/mail/settings', element: <PermissionRoute permission="mail:read">{withSuspense(<MailSettingsPage />)}</PermissionRoute> },
{ path: '/ai-assistant', element: <PermissionRoute permission="ai:read">{withSuspense(<AIAssistantPage />)}</PermissionRoute> },
{ path: '/automation', element: <PermissionRoute permission="automation:read">{withSuspense(<AutomationDashboardPage />)}</PermissionRoute> },
{ path: '/agents', element: <PermissionRoute permission="automation:read">{withSuspense(<AgentDashboardPage />)}</PermissionRoute> },
{ path: '/reports', element: <PermissionRoute permission="reports:read">{withSuspense(<ReportsPage />)}</PermissionRoute> },
{ path: '/tasks', element: <PermissionRoute permission="tasks:read">{withSuspense(<TasksPage />)}</PermissionRoute> },
{ path: '/communication', element: <PermissionRoute permission="communication:read">{withSuspense(<CommunicationPage />)}</PermissionRoute> },
{ path: '/workflows', element: <PermissionRoute permission="workflows:read">{withSuspense(<WorkflowsPage />)}</PermissionRoute> },
{ path: '/contacts/dedup', element: <PermissionRoute permission="contacts:read">{withSuspense(<DedupMergePage />)}</PermissionRoute> },
{ path: '/import-export', element: <PermissionRoute permission="contacts:read">{withSuspense(<ImportExportPage />)}</PermissionRoute> },
{ path: '/tags', element: <PermissionRoute permission="tags:read">{withSuspense(<TagsPage />)}</PermissionRoute> },
{ path: '/activity', element: <PermissionRoute permission="activity:read">{withSuspense(<ActivityTimelinePage />)}</PermissionRoute> },
{ path: '/profile', element: withSuspense(<SettingsProfilePage />) },
{
path: '/settings',
element: <PermissionRoute permission="settings:read">{withSuspense(<SettingsPage />)}</PermissionRoute>,
children: [
{ path: 'stammdaten', element: withSuspense(<SettingsStammdatenPage />) },
{ path: 'user-management', element: withSuspense(<SettingsUserManagementPage />) },
{ path: 'roles', element: withSuspense(<SettingsRolesPage />) },
{ path: 'users', element: withSuspense(<SettingsUsersPage />) },
{ path: 'groups', element: withSuspense(<SettingsGroupsPage />) },
{ path: 'plugins', element: withSuspense(<SettingsPluginsPage />) },
{ path: 'system', element: withSuspense(<SettingsSystemPage />) },
{ path: 'currencies', element: withSuspense(<SettingsCurrenciesPage />) },
{ path: 'taxes', element: withSuspense(<SettingsTaxesPage />) },
{ path: 'sequences', element: withSuspense(<SettingsSequencesPage />) },
{ path: 'mail', element: withSuspense(<MailSettingsPage />) },
{ path: 'notifications', element: withSuspense(<SettingsNotificationsPage />) },
{ path: 'ai', element: withSuspense(<AISettingsPage />) },
{ path: 'ai-proactive', element: withSuspense(<ProactiveAISettings />) },
{ path: 'theme', element: withSuspense(<SettingsThemePage />) },
{ path: 'automation', element: withSuspense(<AutomationSettingsPage />) },
{ path: 'mcp', element: withSuspense(<SettingsMcpPage />) },
{ path: 'ai-settings', element: withSuspense(<SettingsAIPage />) },
{ path: 'menu', element: withSuspense(<SettingsMenuOrderPage />) },
{ path: 'custom-fields', element: withSuspense(<CustomFieldsPage />) },
{ path: 'webhooks', element: withSuspense(<SettingsWebhooksPage />) },
{ path: 'backup', element: withSuspense(<SettingsBackupPage />) },
{ path: 'rechte', element: <PermissionRoute permission="settings:read">{withSuspense(<SettingsRechtePage />)}</PermissionRoute> },
{ path: '*', element: <PluginRouteRenderer /> },
],
},
{ path: '*', element: <PluginRouteRenderer /> },
],
},
]);
export function AppRouter() {
return <RouterProvider router={router} />;
}