import React from 'react'; import { createBrowserRouter, Outlet, Navigate, useLocation, useParams } from 'react-router-dom'; import { useQuery } from '@tanstack/react-query'; import RouteErrorElement from '@/components/RouteErrorElement'; import { useAuth } from './auth/context'; import { useEventContext } from './context/EventContext'; import { ADMIN_BASE_PATH, ADMIN_BILLING_PATH, ADMIN_DEFAULT_AFTER_LOGIN_PATH, ADMIN_EVENTS_PATH, ADMIN_LOGIN_PATH, ADMIN_LOGIN_START_PATH, ADMIN_PUBLIC_LANDING_PATH, ADMIN_WELCOME_BASE_PATH, } from './constants'; import { fetchOnboardingStatus, getTenantPackagesOverview } from './api'; import { resolveOnboardingRedirect } from './mobile/lib/onboardingGuard'; const AuthCallbackPage = React.lazy(() => import('./mobile/AuthCallbackPage')); const LoginStartPage = React.lazy(() => import('./mobile/LoginStartPage')); const LogoutPage = React.lazy(() => import('./mobile/LogoutPage')); const MobileEventsPage = React.lazy(() => import('./mobile/EventsPage')); const MobileEventPhotoboothPage = React.lazy(() => import('./mobile/EventPhotoboothPage')); const MobileBrandingPage = React.lazy(() => import('./mobile/BrandingPage')); const MobileEventFormPage = React.lazy(() => import('./mobile/EventFormPage')); const MobileQrPrintPage = React.lazy(() => import('./mobile/QrPrintPage')); const MobileQrLayoutCustomizePage = React.lazy(() => import('./mobile/QrLayoutCustomizePage')); const MobileEventGuestNotificationsPage = React.lazy(() => import('./mobile/EventGuestNotificationsPage')); const MobileEventControlRoomPage = React.lazy(() => import('./mobile/EventControlRoomPage')); const MobileEventLiveShowSettingsPage = React.lazy(() => import('./mobile/EventLiveShowSettingsPage')); const MobileEventMembersPage = React.lazy(() => import('./mobile/EventMembersPage')); const MobileEventTasksPage = React.lazy(() => import('./mobile/EventTasksPage')); const MobileEventRecapPage = React.lazy(() => import('./mobile/EventRecapPage')); const MobileEventAnalyticsPage = React.lazy(() => import('./mobile/EventAnalyticsPage')); const MobileNotificationsPage = React.lazy(() => import('./mobile/NotificationsPage')); const MobileProfilePage = React.lazy(() => import('./mobile/ProfilePage')); const MobileProfileAccountPage = React.lazy(() => import('./mobile/ProfileAccountPage')); const MobileBillingPage = React.lazy(() => import('./mobile/BillingPage')); const MobilePackageShopPage = React.lazy(() => import('./mobile/PackageShopPage')); const MobileSettingsPage = React.lazy(() => import('./mobile/SettingsPage')); const MobileDataExportsPage = React.lazy(() => import('./mobile/DataExportsPage')); const MobileLoginPage = React.lazy(() => import('./mobile/LoginPage')); const MobilePublicHelpPage = React.lazy(() => import('./mobile/PublicHelpPage')); const MobileForgotPasswordPage = React.lazy(() => import('./mobile/ForgotPasswordPage')); const MobileResetPasswordPage = React.lazy(() => import('./mobile/ResetPasswordPage')); const MobileDashboardPage = React.lazy(() => import('./mobile/DashboardPage')); const MobileTasksTabPage = React.lazy(() => import('./mobile/TasksTabPage')); const MobileUploadsTabPage = React.lazy(() => import('./mobile/UploadsTabPage')); const MobileAnimatedOutlet = React.lazy(() => import('./mobile/components/MobileAnimatedOutlet')); const MobileWelcomeLandingPage = React.lazy(() => import('./mobile/welcome/WelcomeLandingPage')); const MobileWelcomePackagesPage = React.lazy(() => import('./mobile/welcome/WelcomePackagesPage')); const MobileWelcomeSummaryPage = React.lazy(() => import('./mobile/welcome/WelcomeSummaryPage')); const MobileWelcomeEventPage = React.lazy(() => import('./mobile/welcome/WelcomeEventPage')); function RequireAuth() { const { status, user } = useAuth(); const location = useLocation(); const { hasEvents, isLoading: eventsLoading } = useEventContext(); const isWelcomePath = location.pathname.startsWith(ADMIN_WELCOME_BASE_PATH); const isBillingPath = location.pathname.startsWith(ADMIN_BILLING_PATH); const isTenantAdmin = Boolean(user && user.role !== 'member'); const shouldCheckPackages = status === 'authenticated' && isTenantAdmin && !eventsLoading && !hasEvents && !isWelcomePath && !isBillingPath; const { data: packagesData, isLoading: packagesLoading } = useQuery({ queryKey: ['mobile', 'onboarding', 'packages-overview'], queryFn: () => getTenantPackagesOverview({ force: true }), enabled: shouldCheckPackages, staleTime: 60_000, }); const { data: onboardingStatus, isLoading: onboardingLoading } = useQuery({ queryKey: ['mobile', 'onboarding', 'status'], queryFn: fetchOnboardingStatus, enabled: shouldCheckPackages, staleTime: 60_000, }); const hasActivePackage = Boolean(packagesData?.activePackage) || Boolean(packagesData?.packages?.some((pkg) => pkg.active)); const remainingEvents = packagesData?.activePackage?.remaining_events ?? null; const isOnboardingDismissed = Boolean(onboardingStatus?.steps?.dismissed_at); const isOnboardingCompleted = Boolean(onboardingStatus?.steps?.completed_at); const shouldBlockOnboarding = shouldCheckPackages && onboardingLoading; const redirectTarget = resolveOnboardingRedirect({ hasEvents, hasActivePackage, remainingEvents, pathname: location.pathname, isBillingPath, isOnboardingDismissed, isOnboardingCompleted, }); if (status === 'loading') { return (
Bitte warten ...
); } if (status === 'unauthenticated') { return ; } if (!isWelcomePath && !isBillingPath && (eventsLoading || packagesLoading || shouldBlockOnboarding)) { return (
Bitte warten ...
); } if (redirectTarget) { return ; } return ( }> ); } function LandingGate() { const { status } = useAuth(); if (status === 'loading') { return (
Bitte warten ...
); } if (status === 'authenticated') { return ; } return ; } function RequireAdminAccess({ children }: { children: React.ReactNode }) { const { user } = useAuth(); if (user?.role === 'member') { return ; } return <>{children}; } function RedirectToMobileEvent({ buildPath }: { buildPath: (slug: string) => string }) { const { slug } = useParams<{ slug?: string }>(); if (!slug) { return ; } return ; } export const router = createBrowserRouter([ { path: ADMIN_BASE_PATH, element: , errorElement: , children: [ { index: true, element: }, { path: 'login', element: }, { path: 'mobile/login', element: }, { path: 'help', element: }, { path: 'forgot-password', element: }, { path: 'reset-password/:token', element: }, { path: 'start', element: }, { path: 'logout', element: }, { path: 'auth/callback', element: }, { element: , children: [ { path: 'dashboard', element: }, { path: 'live', element: }, { path: 'events', element: }, { path: 'events/new', element: }, { path: 'events/:slug', element: `${ADMIN_EVENTS_PATH}/${slug}`} /> }, { path: 'events/:slug/recap', element: `${ADMIN_EVENTS_PATH}/${slug}`} /> }, { path: 'events/:slug/edit', element: `${ADMIN_EVENTS_PATH}/${slug}/edit`} /> }, { path: 'events/:slug/photos', element: `${ADMIN_EVENTS_PATH}/${slug}/control-room`} /> }, { path: 'events/:slug/members', element: `${ADMIN_EVENTS_PATH}/${slug}/members`} /> }, { path: 'events/:slug/tasks', element: `${ADMIN_EVENTS_PATH}/${slug}/tasks`} /> }, { path: 'events/:slug/invites', element: `${ADMIN_EVENTS_PATH}/${slug}/qr`} /> }, { path: 'events/:slug/branding', element: `${ADMIN_EVENTS_PATH}/${slug}/branding`} /> }, { path: 'events/:slug/photobooth', element: `${ADMIN_EVENTS_PATH}/${slug}/photobooth`} /> }, { path: 'events/:slug/guest-notifications', element: `${ADMIN_EVENTS_PATH}/${slug}/guest-notifications`} /> }, { path: 'events/:slug/toolkit', element: `${ADMIN_EVENTS_PATH}/${slug}`} /> }, { path: 'mobile/events', element: }, { path: 'mobile/events/:slug', element: }, { path: 'mobile/events/:slug/branding', element: }, { path: 'mobile/events/new', element: }, { path: 'mobile/events/:slug/edit', element: }, { path: 'mobile/events/:slug/qr', element: }, { path: 'mobile/events/:slug/qr/customize/:tokenId?', element: }, { path: 'mobile/events/:slug/control-room', element: }, { path: 'mobile/events/:slug/photos/:photoId?', element: `${ADMIN_EVENTS_PATH}/${slug}/control-room`} /> }, { path: 'mobile/events/:slug/live-show', element: `${ADMIN_EVENTS_PATH}/${slug}/control-room`} /> }, { path: 'mobile/events/:slug/live-show/settings', element: }, { path: 'mobile/events/:slug/recap', element: }, { path: 'mobile/events/:slug/analytics', element: }, { path: 'mobile/events/:slug/members', element: }, { path: 'mobile/events/:slug/tasks', element: }, { path: 'mobile/events/:slug/photobooth', element: }, { path: 'mobile/events/:slug/guest-notifications', element: }, { path: 'mobile/notifications', element: }, { path: 'mobile/notifications/:notificationId', element: }, { path: 'mobile/profile', element: }, { path: 'mobile/profile/account', element: }, { path: 'mobile/billing', element: }, { path: 'mobile/billing/shop', element: }, { path: 'mobile/settings', element: }, { path: 'mobile/exports', element: }, { path: 'mobile/dashboard', element: }, { path: 'mobile/tasks', element: }, { path: 'mobile/uploads', element: }, { path: 'mobile/welcome', element: }, { path: 'mobile/welcome/packages', element: }, { path: 'mobile/welcome/summary', element: }, { path: 'mobile/welcome/event', element: }, ], }, ], }, { path: '*', element: , errorElement: , }, ]);