Files
MyClub/frontend/src/App.lazy.tsx
T
Tomas Dvorak 087f30e82c dev day #80
2025-11-02 21:31:00 +01:00

294 lines
16 KiB
TypeScript

import React, { lazy, Suspense } from 'react';
import { ChakraProvider, extendTheme, Spinner, Center, Box } from '@chakra-ui/react';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { BrowserRouter as Router, Routes, Route, Navigate, Outlet } from 'react-router-dom';
import { AuthProvider, useAuth } from './contexts/AuthContext';
import { ClubThemeProvider } from './contexts/ClubThemeContext';
import { HelmetProvider } from 'react-helmet-async';
import { theme } from './App';
import { useUmami } from './hooks/useUmami';
import { useFontLoader } from './hooks/useFontLoader';
import DefaultSEO from './components/seo/DefaultSEO';
import CookieBanner from './components/CookieBanner';
import ProtectedRoute from './components/ProtectedRoute';
import { getSetupStatus } from './services/setup';
import { useState, useEffect } from 'react';
// Create a client
const queryClient = new QueryClient({
defaultOptions: {
queries: {
staleTime: 5 * 60 * 1000,
cacheTime: 10 * 60 * 1000,
refetchOnWindowFocus: false,
refetchOnMount: false,
retry: 1,
},
},
});
// Loading component
const PageLoader = () => (
<Center h="100vh">
<Box textAlign="center">
<Spinner size="xl" color="brand.primary" thickness="4px" />
<Box mt={4} fontSize="sm" color="gray.600">Načítání...</Box>
</Box>
</Center>
);
// Lazy load pages for code splitting
const HomePage = lazy(() => import('./pages/HomePage'));
const BlogPage = lazy(() => import('./pages/BlogPage'));
const ArticleDetailPage = lazy(() => import('./pages/ArticleDetailPage'));
const ActivityDetailPage = lazy(() => import('./pages/ActivityDetailPage'));
const MatchDetailPage = lazy(() => import('./pages/MatchDetailPage'));
const ClubPage = lazy(() => import('./pages/ClubPage'));
const CalendarPage = lazy(() => import('./pages/CalendarPage'));
const TablesPage = lazy(() => import('./pages/TablesPage'));
const MatchesPage = lazy(() => import('./pages/MatchesPage'));
const PlayersPage = lazy(() => import('./pages/PlayersPage'));
const PlayerDetailPage = lazy(() => import('./pages/PlayerDetailPage'));
const SponsorsPage = lazy(() => import('./pages/SponsorsPage'));
const ContactPage = lazy(() => import('./pages/ContactPage'));
const GalleryPage = lazy(() => import('./pages/GalleryPage'));
const AlbumDetailPage = lazy(() => import('./pages/AlbumDetailPage'));
const AuthPage = lazy(() => import('./pages/AuthPage'));
const RegisterPage = lazy(() => import('./pages/RegisterPage'));
const ForgotPasswordPage = lazy(() => import('./pages/ForgotPasswordPage'));
const ResetPasswordPage = lazy(() => import('./pages/ResetPasswordPage'));
const ActivitiesCalendarPage = lazy(() => import('./pages/ActivitiesCalendarPage'));
const AboutPage = lazy(() => import('./pages/AboutPage'));
const SetupPage = lazy(() => import('./pages/SetupPage'));
const StylePreviewPage = lazy(() => import('./pages/StylePreviewPage'));
const NewsletterUnsubscribePage = lazy(() => import('./pages/NewsletterUnsubscribePage'));
const NewsletterPreferencesPage = lazy(() => import('./pages/NewsletterPreferencesPage'));
const VideosPage = lazy(() => import('./pages/VideosPage'));
const SearchPage = lazy(() => import('./pages/SearchPage'));
const ClothingPage = lazy(() => import('./pages/ClothingPage'));
const PollsPage = lazy(() => import('./pages/PollsPage'));
const OverlayScoreboardPage = lazy(() => import('./pages/OverlayScoreboardPage'));
const OverlaySponsorsPage = lazy(() => import('./pages/OverlaySponsorsPage'));
const NotFoundPage = lazy(() => import('./pages/NotFoundPage'));
const ForbiddenPage = lazy(() => import('./pages/ForbiddenPage'));
// Legal pages
const CookiePolicyPage = lazy(() => import('./pages/legal/CookiePolicyPage'));
const TermsPage = lazy(() => import('./pages/legal/TermsPage'));
const PrivacyPolicyPage = lazy(() => import('./pages/legal/PrivacyPolicyPage'));
// Admin pages
const AdminDashboardPage = lazy(() => import('./pages/admin/AdminDashboardPage'));
const ArticlesAdminPage = lazy(() => import('./pages/admin/ArticlesAdminPage'));
const SponsorsAdminPage = lazy(() => import('./pages/admin/SponsorsAdminPage'));
const CategoriesAdminPage = lazy(() => import('./pages/admin/CategoriesAdminPage'));
const MediaAdminPage = lazy(() => import('./pages/admin/MediaAdminPage'));
const MatchesAdminPage = lazy(() => import('./pages/admin/MatchesAdminPage'));
const PlayersAdminPage = lazy(() => import('./pages/admin/PlayersAdminPage'));
const TeamsAdminPage = lazy(() => import('./pages/admin/TeamsAdminPage'));
const BannersAdminPage = lazy(() => import('./pages/admin/BannersAdminPage'));
const MessagesAdminPage = lazy(() => import('./pages/admin/MessagesAdminPage'));
const SettingsAdminPage = lazy(() => import('./pages/admin/SettingsAdminPage'));
const UsersAdminPage = lazy(() => import('./pages/admin/UsersAdminPage'));
const NewsletterAdminPage = lazy(() => import('./pages/admin/NewsletterAdminPage'));
const CompetitionAliasesAdminPage = lazy(() => import('./pages/admin/CompetitionAliasesAdminPage'));
const PrefetchAdminPage = lazy(() => import('./pages/admin/PrefetchAdminPage'));
const AdminVideosPage = lazy(() => import('./pages/admin/AdminVideosPage'));
const GalleryAdminPage = lazy(() => import('./pages/admin/GalleryAdminPage'));
const AdminActivitiesPage = lazy(() => import('./pages/admin/AdminActivitiesPage'));
const AdminMerchPage = lazy(() => import('./pages/admin/AdminMerchPage'));
const AdminResetPasswordPage = lazy(() => import('./pages/admin/AdminResetPasswordPage'));
const AboutAdminPage = lazy(() => import('./pages/admin/AboutAdminPage'));
const AnalyticsAdminPage = lazy(() => import('./pages/admin/AnalyticsAdminPage'));
const FilesAdminPage = lazy(() => import('./pages/admin/FilesAdminPage'));
const ContactsAdminPage = lazy(() => import('./pages/admin/ContactsAdminPage'));
const NavigationAdminPage = lazy(() => import('./pages/admin/NavigationAdminPage'));
const PollsAdminPage = lazy(() => import('./pages/admin/PollsAdminPage'));
const CommentsAdminPage = lazy(() => import('./pages/admin/CommentsAdminPage'));
const AdminDocsPage = lazy(() => import('./pages/admin/AdminDocsPage'));
const ScoreboardAdminPage = lazy(() => import('./pages/admin/ScoreboardAdminPage'));
const MobileScoreboardControlPage = lazy(() => import('./pages/admin/MobileScoreboardControlPage'));
const ShortlinksAdminPage = lazy(() => import('./pages/admin/ShortlinksAdminPage'));
const EngagementAdminPage = lazy(() => import('./pages/admin/EngagementAdminPage'));
const SemiAdminPage = lazy(() => import('./pages/SemiAdminPage'));
// Analytics and font loader
const AnalyticsInitializer: React.FC = () => {
useUmami();
return null;
};
const FontLoader: React.FC = () => {
useFontLoader();
return null;
};
// Public route wrapper
const PublicRoute = ({ children }: { children: React.ReactNode }) => {
const { isAuthenticated, isLoading } = useAuth();
const [checkingSetup, setCheckingSetup] = useState(true);
const [requiresSetup, setRequiresSetup] = useState<boolean>(false);
useEffect(() => {
let mounted = true;
(async () => {
try {
const s = await getSetupStatus();
if (mounted) setRequiresSetup(!!s.requires_setup);
} catch (_) {
if (mounted) setRequiresSetup(false);
} finally {
if (mounted) setCheckingSetup(false);
}
})();
return () => { mounted = false; };
}, []);
if (isLoading || checkingSetup) {
return <PageLoader />;
}
if (isAuthenticated) {
return <Navigate to="/admin" replace />;
}
const currentPath = window.location.pathname;
if (requiresSetup && currentPath !== '/setup') {
return <Navigate to="/setup" replace />;
}
return <>{children}</>;
};
const AdminRoutesWrapper = () => {
return <Outlet />;
};
const AppLazy: React.FC = () => {
return (
<ChakraProvider theme={theme}>
<QueryClientProvider client={queryClient}>
<Router>
<AuthProvider>
<ClubThemeProvider>
<HelmetProvider>
<AnalyticsInitializer />
<FontLoader />
<DefaultSEO />
<Suspense fallback={<PageLoader />}>
<Routes>
{/* Public routes */}
<Route path="/" element={<HomePage />} />
<Route path="/hledat" element={<SearchPage />} />
<Route path="/search" element={<SearchPage />} />
<Route path="/overlay/scoreboard" element={<OverlayScoreboardPage />} />
<Route path="/overlay/sponsors" element={<OverlaySponsorsPage />} />
<Route path="/blog" element={<BlogPage />} />
<Route path="/klub" element={<ClubPage />} />
<Route path="/o-klubu" element={<AboutPage />} />
<Route path="/kalendar" element={<CalendarPage />} />
<Route path="/aktivity" element={<ActivitiesCalendarPage />} />
<Route path="/tabulky" element={<TablesPage />} />
<Route path="/zapasy" element={<MatchesPage />} />
<Route path="/players" element={<PlayersPage />} />
<Route path="/hraci" element={<PlayersPage />} />
<Route path="/players/:id" element={<PlayerDetailPage />} />
<Route path="/hraci/:id" element={<PlayerDetailPage />} />
<Route path="/sponzori" element={<SponsorsPage />} />
<Route path="/kontakt" element={<ContactPage />} />
<Route path="/ankety" element={<PollsPage />} />
<Route path="/galerie" element={<GalleryPage />} />
<Route path="/galerie/album/:id" element={<AlbumDetailPage />} />
<Route path="/videa" element={<VideosPage />} />
<Route path="/obleceni" element={<ClothingPage />} />
{/* Legal pages */}
<Route path="/pravidla-cookies" element={<CookiePolicyPage />} />
<Route path="/obchodni-podminky" element={<TermsPage />} />
<Route path="/zasady-ochrany-osobnich-udaju" element={<PrivacyPolicyPage />} />
{/* Article routes */}
<Route path="/news" element={<Navigate to="/blog" replace />} />
<Route path="/news/:slug" element={<ArticleDetailPage />} />
<Route path="/articles/slug/:slug" element={<ArticleDetailPage />} />
<Route path="/articles/:id" element={<ArticleDetailPage />} />
<Route path="/zapas/:id" element={<MatchDetailPage />} />
<Route path="/aktivita/:id" element={<ActivityDetailPage />} />
{/* Redirects */}
<Route path="/clanky" element={<Navigate to="/blog" replace />} />
<Route path="/aktuality" element={<Navigate to="/blog" replace />} />
{/* Setup */}
<Route path="/setup" element={<PublicRoute><SetupPage /></PublicRoute>} />
<Route path="/setup/styl" element={<PublicRoute><StylePreviewPage /></PublicRoute>} />
{/* Auth */}
<Route path="/login" element={<PublicRoute><AuthPage /></PublicRoute>} />
<Route path="/register" element={<PublicRoute><RegisterPage /></PublicRoute>} />
<Route path="/forgot-password" element={<ForgotPasswordPage />} />
<Route path="/reset-password" element={<ResetPasswordPage />} />
<Route path="/newsletter/unsubscribe/:email" element={<NewsletterUnsubscribePage />} />
<Route path="/newsletter/preferences" element={<NewsletterPreferencesPage />} />
<Route path="/403" element={<ForbiddenPage />} />
<Route path="/semiadmin" element={<ProtectedRoute><SemiAdminPage /></ProtectedRoute>} />
{/* Admin routes */}
<Route element={<ProtectedRoute requiredRole="admin"><AdminRoutesWrapper /></ProtectedRoute>}>
<Route path="/admin" element={<AdminDashboardPage />} />
<Route path="/admin/docs" element={<AdminDocsPage />} />
<Route path="/admin/clanky" element={<ArticlesAdminPage />} />
<Route path="/admin/o-klubu" element={<AboutAdminPage />} />
<Route path="/admin/videa" element={<AdminVideosPage />} />
<Route path="/admin/galerie" element={<GalleryAdminPage />} />
<Route path="/admin/obleceni" element={<AdminMerchPage />} />
<Route path="/admin/aktivity" element={<AdminActivitiesPage />} />
<Route path="/admin/sponzori" element={<SponsorsAdminPage />} />
<Route path="/admin/kategorie" element={<CategoriesAdminPage />} />
<Route path="/admin/media" element={<MediaAdminPage />} />
<Route path="/admin/zapasy" element={<MatchesAdminPage />} />
<Route path="/admin/hraci" element={<PlayersAdminPage />} />
<Route path="/admin/tymy" element={<TeamsAdminPage />} />
<Route path="/admin/uzivatele" element={<UsersAdminPage />} />
<Route path="/admin/bannery" element={<BannersAdminPage />} />
<Route path="/admin/zpravy" element={<MessagesAdminPage />} />
<Route path="/admin/nastaveni" element={<SettingsAdminPage />} />
<Route path="/admin/newsletter" element={<NewsletterAdminPage />} />
<Route path="/admin/ankety" element={<PollsAdminPage />} />
<Route path="/admin/aliasy-soutezi" element={<CompetitionAliasesAdminPage />} />
<Route path="/admin/prefetch" element={<PrefetchAdminPage />} />
<Route path="/admin/users/send-reset" element={<AdminResetPasswordPage />} />
<Route path="/admin/scoreboard" element={<ScoreboardAdminPage />} />
<Route path="/admin/scoreboard/remote" element={<MobileScoreboardControlPage />} />
<Route path="/admin/analytika" element={<AnalyticsAdminPage />} />
<Route path="/admin/soubory" element={<FilesAdminPage />} />
<Route path="/admin/kontakty" element={<ContactsAdminPage />} />
<Route path="/admin/navigace" element={<NavigationAdminPage />} />
<Route path="/admin/komentare" element={<CommentsAdminPage />} />
<Route path="/admin/shortlinks" element={<ShortlinksAdminPage />} />
<Route path="/admin/engagement" element={<EngagementAdminPage />} />
</Route>
{/* Legacy admin routes */}
<Route path="/dashboard" element={<Navigate to="/admin" replace />} />
<Route path="/admin/sponsors" element={<ProtectedRoute requiredRole="admin"><SponsorsAdminPage /></ProtectedRoute>} />
<Route path="/admin/banners" element={<ProtectedRoute requiredRole="admin"><BannersAdminPage /></ProtectedRoute>} />
<Route path="/admin/messages" element={<ProtectedRoute requiredRole="admin"><MessagesAdminPage /></ProtectedRoute>} />
<Route path="/admin/settings" element={<ProtectedRoute requiredRole="admin"><SettingsAdminPage /></ProtectedRoute>} />
{/* 404 */}
<Route path="*" element={<NotFoundPage />} />
</Routes>
</Suspense>
<CookieBanner />
</HelmetProvider>
</ClubThemeProvider>
</AuthProvider>
</Router>
</QueryClientProvider>
</ChakraProvider>
);
};
export default AppLazy;