209 lines
14 KiB
TypeScript
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} />;
|
|
}
|