diff --git a/app/Http/Middleware/RequestTimingMiddleware.php b/app/Http/Middleware/RequestTimingMiddleware.php
new file mode 100644
index 0000000..2f7fc45
--- /dev/null
+++ b/app/Http/Middleware/RequestTimingMiddleware.php
@@ -0,0 +1,45 @@
+attributes->set('_timing_start', microtime(true));
+
+ return $next($request);
+ }
+
+ public function terminate(Request $request, Response $response): void
+ {
+ if (! app()->environment('local')) {
+ return;
+ }
+
+ $start = $request->attributes->get('_timing_start');
+
+ if (! is_float($start)) {
+ return;
+ }
+
+ $duration = microtime(true) - $start;
+
+ Log::debug('request_timing', [
+ 'path' => $request->path(),
+ 'method' => $request->method(),
+ 'status' => $response->getStatusCode(),
+ 'duration_ms' => round($duration * 1000, 2),
+ ]);
+ }
+}
diff --git a/bootstrap/app.php b/bootstrap/app.php
index a15b258..b28f081 100644
--- a/bootstrap/app.php
+++ b/bootstrap/app.php
@@ -46,6 +46,7 @@ return Application::configure(basePath: dirname(__DIR__))
\App\Http\Middleware\ContentSecurityPolicy::class,
HandleInertiaRequests::class,
AddLinkHeadersForPreloadedAssets::class,
+ \App\Http\Middleware\RequestTimingMiddleware::class,
]);
$middleware->api(append: []);
diff --git a/resources/js/admin/main.tsx b/resources/js/admin/main.tsx
index 0bedb7e..299e3d1 100644
--- a/resources/js/admin/main.tsx
+++ b/resources/js/admin/main.tsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { Suspense } from 'react';
import { createRoot } from 'react-dom/client';
import { RouterProvider } from 'react-router-dom';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
@@ -9,7 +9,8 @@ import './i18n';
import './dev-tools';
import { initializeTheme } from '@/hooks/use-appearance';
import { OnboardingProgressProvider } from './onboarding';
-import { DevTenantSwitcher } from './components/DevTenantSwitcher';
+
+const DevTenantSwitcher = React.lazy(() => import('./components/DevTenantSwitcher'));
const enableDevSwitcher = import.meta.env.DEV || import.meta.env.VITE_ENABLE_TENANT_SWITCHER === 'true';
@@ -28,10 +29,22 @@ createRoot(rootEl).render(
-
+
+ Oberfläche wird geladen …
+
+ )}
+ >
+
+
- {enableDevSwitcher ? : null}
+ {enableDevSwitcher ? (
+
+
+
+ ) : null}
);
diff --git a/resources/js/admin/router.tsx b/resources/js/admin/router.tsx
index cd909b9..1b1863b 100644
--- a/resources/js/admin/router.tsx
+++ b/resources/js/admin/router.tsx
@@ -1,26 +1,5 @@
import React from 'react';
import { createBrowserRouter, Outlet, Navigate, useLocation } from 'react-router-dom';
-import LoginPage from './pages/LoginPage';
-import DashboardPage from './pages/DashboardPage';
-import EventsPage from './pages/EventsPage';
-import SettingsPage from './pages/SettingsPage';
-import EventFormPage from './pages/EventFormPage';
-import EventPhotosPage from './pages/EventPhotosPage';
-import EventDetailPage from './pages/EventDetailPage';
-import EventMembersPage from './pages/EventMembersPage';
-import EventTasksPage from './pages/EventTasksPage';
-import EventToolkitPage from './pages/EventToolkitPage';
-import EventInvitesPage from './pages/EventInvitesPage';
-import EngagementPage from './pages/EngagementPage';
-import BillingPage from './pages/BillingPage';
-import TasksPage from './pages/TasksPage';
-import TaskCollectionsPage from './pages/TaskCollectionsPage';
-import EmotionsPage from './pages/EmotionsPage';
-import AuthCallbackPage from './pages/AuthCallbackPage';
-import WelcomeTeaserPage from './pages/WelcomeTeaserPage';
-import LoginStartPage from './pages/LoginStartPage';
-import ProfilePage from './pages/ProfilePage';
-import LogoutPage from './pages/LogoutPage';
import { useAuth } from './auth/context';
import {
ADMIN_BASE_PATH,
@@ -31,10 +10,31 @@ import {
ADMIN_PUBLIC_LANDING_PATH,
} from './constants';
import { consumeLastDestination } from './lib/returnTo';
-import WelcomeLandingPage from './onboarding/pages/WelcomeLandingPage';
-import WelcomePackagesPage from './onboarding/pages/WelcomePackagesPage';
-import WelcomeEventSetupPage from './onboarding/pages/WelcomeEventSetupPage';
-import WelcomeOrderSummaryPage from './onboarding/pages/WelcomeOrderSummaryPage';
+const LoginPage = React.lazy(() => import('./pages/LoginPage'));
+const DashboardPage = React.lazy(() => import('./pages/DashboardPage'));
+const EventsPage = React.lazy(() => import('./pages/EventsPage'));
+const SettingsPage = React.lazy(() => import('./pages/SettingsPage'));
+const EventFormPage = React.lazy(() => import('./pages/EventFormPage'));
+const EventPhotosPage = React.lazy(() => import('./pages/EventPhotosPage'));
+const EventDetailPage = React.lazy(() => import('./pages/EventDetailPage'));
+const EventMembersPage = React.lazy(() => import('./pages/EventMembersPage'));
+const EventTasksPage = React.lazy(() => import('./pages/EventTasksPage'));
+const EventToolkitPage = React.lazy(() => import('./pages/EventToolkitPage'));
+const EventInvitesPage = React.lazy(() => import('./pages/EventInvitesPage'));
+const EngagementPage = React.lazy(() => import('./pages/EngagementPage'));
+const BillingPage = React.lazy(() => import('./pages/BillingPage'));
+const TasksPage = React.lazy(() => import('./pages/TasksPage'));
+const TaskCollectionsPage = React.lazy(() => import('./pages/TaskCollectionsPage'));
+const EmotionsPage = React.lazy(() => import('./pages/EmotionsPage'));
+const AuthCallbackPage = React.lazy(() => import('./pages/AuthCallbackPage'));
+const WelcomeTeaserPage = React.lazy(() => import('./pages/WelcomeTeaserPage'));
+const LoginStartPage = React.lazy(() => import('./pages/LoginStartPage'));
+const ProfilePage = React.lazy(() => import('./pages/ProfilePage'));
+const LogoutPage = React.lazy(() => import('./pages/LogoutPage'));
+const WelcomeLandingPage = React.lazy(() => import('./onboarding/pages/WelcomeLandingPage'));
+const WelcomePackagesPage = React.lazy(() => import('./onboarding/pages/WelcomePackagesPage'));
+const WelcomeEventSetupPage = React.lazy(() => import('./onboarding/pages/WelcomeEventSetupPage'));
+const WelcomeOrderSummaryPage = React.lazy(() => import('./onboarding/pages/WelcomeOrderSummaryPage'));
function RequireAuth() {
const { status } = useAuth();
diff --git a/resources/js/guest/i18n/messages.ts b/resources/js/guest/i18n/messages.ts
index 646713a..0e6fb56 100644
--- a/resources/js/guest/i18n/messages.ts
+++ b/resources/js/guest/i18n/messages.ts
@@ -308,7 +308,7 @@ export const messages: Record = {
limitWarning: 'Nur noch {remaining} von {max} Fotos möglich. Bitte kontaktiere die Veranstalter für ein Upgrade.',
galleryWarningDay: 'Galerie läuft in {days} Tag ab. Teile deine Fotos rechtzeitig!',
galleryWarningDays: 'Galerie läuft in {days} Tagen ab. Teile deine Fotos rechtzeitig!',
- status: {
+ packageStatus: {
title: 'Dein Paketstatus',
subtitle: 'Behalte deine Kontingente im Blick, bevor es eng wird.',
badges: {
@@ -736,7 +736,7 @@ export const messages: Record = {
limitWarning: 'Only {remaining} of {max} photos left. Please contact the organizers for an upgrade.',
galleryWarningDay: 'Gallery expires in {days} day. Upload your photos soon!',
galleryWarningDays: 'Gallery expires in {days} days. Upload your photos soon!',
- status: {
+ packageStatus: {
title: 'Your package status',
subtitle: 'Keep an eye on your remaining allowances.',
badges: {
diff --git a/resources/js/guest/main.tsx b/resources/js/guest/main.tsx
index cc93461..33c487d 100644
--- a/resources/js/guest/main.tsx
+++ b/resources/js/guest/main.tsx
@@ -1,4 +1,4 @@
-import React from 'react';
+import React, { Suspense } from 'react';
import { createRoot } from 'react-dom/client';
import { RouterProvider } from 'react-router-dom';
import { router } from './router';
@@ -28,7 +28,15 @@ createRoot(rootEl).render(
-
+
+ Erlebnisse werden geladen …
+
+ )}
+ >
+
+
diff --git a/resources/js/guest/router.tsx b/resources/js/guest/router.tsx
index e727369..6868cbd 100644
--- a/resources/js/guest/router.tsx
+++ b/resources/js/guest/router.tsx
@@ -8,27 +8,28 @@ import { AlertTriangle, Loader2 } from 'lucide-react';
import { EventStatsProvider } from './context/EventStatsContext';
import { GuestIdentityProvider } from './context/GuestIdentityContext';
import { EventBrandingProvider } from './context/EventBrandingContext';
-import LandingPage from './pages/LandingPage';
-import ProfileSetupPage from './pages/ProfileSetupPage';
-import HomePage from './pages/HomePage';
-import TaskPickerPage from './pages/TaskPickerPage';
-import TaskDetailPage from './pages/TaskDetailPage';
-import UploadPage from './pages/UploadPage';
-import UploadQueuePage from './pages/UploadQueuePage';
-import GalleryPage from './pages/GalleryPage';
-import PhotoLightbox from './pages/PhotoLightbox';
-import AchievementsPage from './pages/AchievementsPage';
-import SlideshowPage from './pages/SlideshowPage';
-import SettingsPage from './pages/SettingsPage';
-import LegalPage from './pages/LegalPage';
-import PublicGalleryPage from './pages/PublicGalleryPage';
-import NotFoundPage from './pages/NotFoundPage';
import { LocaleProvider } from './i18n/LocaleContext';
import { DEFAULT_LOCALE, isLocaleCode } from './i18n/messages';
import { useTranslation, type TranslateFn } from './i18n/useTranslation';
import type { EventBranding } from './types/event-branding';
import type { EventBrandingPayload, FetchEventErrorCode } from './services/eventApi';
+const LandingPage = React.lazy(() => import('./pages/LandingPage'));
+const ProfileSetupPage = React.lazy(() => import('./pages/ProfileSetupPage'));
+const HomePage = React.lazy(() => import('./pages/HomePage'));
+const TaskPickerPage = React.lazy(() => import('./pages/TaskPickerPage'));
+const TaskDetailPage = React.lazy(() => import('./pages/TaskDetailPage'));
+const UploadPage = React.lazy(() => import('./pages/UploadPage'));
+const UploadQueuePage = React.lazy(() => import('./pages/UploadQueuePage'));
+const GalleryPage = React.lazy(() => import('./pages/GalleryPage'));
+const PhotoLightbox = React.lazy(() => import('./pages/PhotoLightbox'));
+const AchievementsPage = React.lazy(() => import('./pages/AchievementsPage'));
+const SlideshowPage = React.lazy(() => import('./pages/SlideshowPage'));
+const SettingsPage = React.lazy(() => import('./pages/SettingsPage'));
+const LegalPage = React.lazy(() => import('./pages/LegalPage'));
+const PublicGalleryPage = React.lazy(() => import('./pages/PublicGalleryPage'));
+const NotFoundPage = React.lazy(() => import('./pages/NotFoundPage'));
+
function HomeLayout() {
const { token } = useParams();