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();