feat(App): implement code-splitting for improved performance and user experience
All checks were successful
Deploy Development / deploy (push) Successful in 39s
Test Suite / pytest-backend (push) Successful in 36s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 12s
Test Suite / playwright-tests (push) Successful in 56s
All checks were successful
Deploy Development / deploy (push) Successful in 39s
Test Suite / pytest-backend (push) Successful in 36s
Test Suite / lint-backend (push) Successful in 0s
Test Suite / build-frontend (push) Successful in 12s
Test Suite / playwright-tests (push) Successful in 56s
- Refactored the App component to utilize React's lazy loading for page components, enhancing load times and performance. - Introduced a fallback UI with a spinner during component loading, improving user feedback during navigation. - Updated the AuthContext to use useCallback and useMemo for optimized performance in login and logout functions, reducing unnecessary re-renders. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
85163ad440
commit
1c268555f6
|
|
@ -1,4 +1,4 @@
|
|||
import React from 'react'
|
||||
import React, { Suspense, lazy } from 'react'
|
||||
import {
|
||||
RouterProvider,
|
||||
createBrowserRouter,
|
||||
|
|
@ -12,45 +12,66 @@ import { ToastProvider } from './context/ToastContext'
|
|||
import { OrgInboxProvider, useOrgInbox } from './context/OrgInboxContext'
|
||||
import DesktopSidebar from './components/DesktopSidebar'
|
||||
import { getMainNavItems } from './config/appNav'
|
||||
import LoginPage from './pages/LoginPage'
|
||||
import VerifyPage from './pages/VerifyPage'
|
||||
import Dashboard from './pages/Dashboard'
|
||||
import AccountSettingsPage from './pages/AccountSettingsPage'
|
||||
import SettingsSystemInfoPage from './pages/SettingsSystemInfoPage'
|
||||
import ExercisesListPage from './pages/ExercisesListPage'
|
||||
import ExerciseDetailPage from './pages/ExerciseDetailPage'
|
||||
import ExerciseFormPage from './pages/ExerciseFormPage'
|
||||
import ClubsPage from './pages/ClubsPage'
|
||||
import InboxPage from './pages/InboxPage'
|
||||
import SkillsPage from './pages/SkillsPage'
|
||||
import TrainingPlanningPage from './pages/TrainingPlanningPage'
|
||||
import TrainingFrameworkProgramsListPage from './pages/TrainingFrameworkProgramsListPage'
|
||||
import TrainingFrameworkProgramEditPage from './pages/TrainingFrameworkProgramEditPage'
|
||||
import TrainingModulesListPage from './pages/TrainingModulesListPage'
|
||||
import TrainingModuleEditPage from './pages/TrainingModuleEditPage'
|
||||
import TrainingUnitRunPage from './pages/TrainingUnitRunPage'
|
||||
import TrainingCoachPage from './pages/TrainingCoachPage'
|
||||
import AdminCatalogsPage from './pages/AdminCatalogsPage'
|
||||
import AdminHierarchyPage from './pages/AdminHierarchyPage'
|
||||
import AdminMaturityModelsPage from './pages/AdminMaturityModelsPage'
|
||||
import TrainerContextsPage from './pages/TrainerContextsPage'
|
||||
import MediaWikiImportPage from './pages/MediaWikiImportPage'
|
||||
import AdminUsersPage from './pages/AdminUsersPage'
|
||||
import AdminHomeRedirect from './components/AdminHomeRedirect'
|
||||
import PlatformAdminRoute from './components/PlatformAdminRoute'
|
||||
import MediaLibraryPage from './pages/MediaLibraryPage'
|
||||
import LegalPage from './pages/LegalPage'
|
||||
import AdminLegalDocumentsPage from './pages/AdminLegalDocumentsPage'
|
||||
import SettingsLegalPage from './pages/SettingsLegalPage'
|
||||
import ActiveClubSwitcher from './components/ActiveClubSwitcher'
|
||||
import InactiveMembershipBanner from './components/InactiveMembershipBanner'
|
||||
import './app.css'
|
||||
|
||||
const LoginPage = lazy(() => import('./pages/LoginPage'))
|
||||
const VerifyPage = lazy(() => import('./pages/VerifyPage'))
|
||||
const Dashboard = lazy(() => import('./pages/Dashboard'))
|
||||
const AccountSettingsPage = lazy(() => import('./pages/AccountSettingsPage'))
|
||||
const SettingsSystemInfoPage = lazy(() => import('./pages/SettingsSystemInfoPage'))
|
||||
const ExercisesListPage = lazy(() => import('./pages/ExercisesListPage'))
|
||||
const ExerciseDetailPage = lazy(() => import('./pages/ExerciseDetailPage'))
|
||||
const ExerciseFormPage = lazy(() => import('./pages/ExerciseFormPage'))
|
||||
const ClubsPage = lazy(() => import('./pages/ClubsPage'))
|
||||
const InboxPage = lazy(() => import('./pages/InboxPage'))
|
||||
const SkillsPage = lazy(() => import('./pages/SkillsPage'))
|
||||
const TrainingPlanningPage = lazy(() => import('./pages/TrainingPlanningPage'))
|
||||
const TrainingFrameworkProgramsListPage = lazy(() =>
|
||||
import('./pages/TrainingFrameworkProgramsListPage'),
|
||||
)
|
||||
const TrainingFrameworkProgramEditPage = lazy(() =>
|
||||
import('./pages/TrainingFrameworkProgramEditPage'),
|
||||
)
|
||||
const TrainingModulesListPage = lazy(() => import('./pages/TrainingModulesListPage'))
|
||||
const TrainingModuleEditPage = lazy(() => import('./pages/TrainingModuleEditPage'))
|
||||
const TrainingUnitRunPage = lazy(() => import('./pages/TrainingUnitRunPage'))
|
||||
const TrainingCoachPage = lazy(() => import('./pages/TrainingCoachPage'))
|
||||
const AdminCatalogsPage = lazy(() => import('./pages/AdminCatalogsPage'))
|
||||
const AdminHierarchyPage = lazy(() => import('./pages/AdminHierarchyPage'))
|
||||
const AdminMaturityModelsPage = lazy(() => import('./pages/AdminMaturityModelsPage'))
|
||||
const TrainerContextsPage = lazy(() => import('./pages/TrainerContextsPage'))
|
||||
const MediaWikiImportPage = lazy(() => import('./pages/MediaWikiImportPage'))
|
||||
const AdminUsersPage = lazy(() => import('./pages/AdminUsersPage'))
|
||||
const MediaLibraryPage = lazy(() => import('./pages/MediaLibraryPage'))
|
||||
const LegalPage = lazy(() => import('./pages/LegalPage'))
|
||||
const AdminLegalDocumentsPage = lazy(() => import('./pages/AdminLegalDocumentsPage'))
|
||||
const SettingsLegalPage = lazy(() => import('./pages/SettingsLegalPage'))
|
||||
|
||||
/** Shield „Admin“: nur Super-Admin (global). Vereinsorga: Vereine → Mitglieder. */
|
||||
function computeShowAdminNav(currentUser) {
|
||||
return currentUser?.role === 'superadmin'
|
||||
}
|
||||
|
||||
function AppRouteFallback() {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
minHeight: '100vh',
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
background: 'var(--bg)',
|
||||
}}
|
||||
>
|
||||
<div className="spinner"></div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Bottom Navigation (Mobile)
|
||||
function Nav({ showAdminNav }) {
|
||||
const { canAccessOrgInbox, inboxCount } = useOrgInbox()
|
||||
|
|
@ -270,7 +291,9 @@ function App() {
|
|||
return (
|
||||
<AuthProvider>
|
||||
<ToastProvider>
|
||||
<Suspense fallback={<AppRouteFallback />}>
|
||||
<RouterProvider router={appRouter} />
|
||||
</Suspense>
|
||||
</ToastProvider>
|
||||
</AuthProvider>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,12 @@
|
|||
import { createContext, useContext, useState, useEffect, useCallback, useRef } from 'react'
|
||||
import {
|
||||
createContext,
|
||||
useContext,
|
||||
useState,
|
||||
useEffect,
|
||||
useCallback,
|
||||
useMemo,
|
||||
useRef,
|
||||
} from 'react'
|
||||
import api, { ACTIVE_CLUB_STORAGE_KEY } from '../utils/api'
|
||||
import { activeClubMemberships } from '../utils/activeClub'
|
||||
|
||||
|
|
@ -94,7 +102,7 @@ export function AuthProvider({ children }) {
|
|||
}, [])
|
||||
|
||||
/** Fallback, falls ohne checkAuth gesetzt wird (Legacy / Token-Injektion) */
|
||||
const login = (payload) => {
|
||||
const login = useCallback((payload) => {
|
||||
if (payload?.profile != null) {
|
||||
syncStoredActiveClub(payload.profile)
|
||||
setUser(payload.profile)
|
||||
|
|
@ -112,9 +120,9 @@ export function AuthProvider({ children }) {
|
|||
return
|
||||
}
|
||||
setUser(payload)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const logout = () => {
|
||||
const logout = useCallback(() => {
|
||||
setUser(null)
|
||||
localStorage.removeItem('authToken')
|
||||
localStorage.removeItem(ACTIVE_CLUB_STORAGE_KEY)
|
||||
|
|
@ -123,9 +131,10 @@ export function AuthProvider({ children }) {
|
|||
sessionStorage.removeItem(key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}, [])
|
||||
|
||||
const value = {
|
||||
const value = useMemo(
|
||||
() => ({
|
||||
user,
|
||||
isAuthenticated: !!user,
|
||||
loading,
|
||||
|
|
@ -133,7 +142,9 @@ export function AuthProvider({ children }) {
|
|||
logout,
|
||||
checkAuth,
|
||||
setActiveClub,
|
||||
}
|
||||
}),
|
||||
[user, loading, login, logout, checkAuth, setActiveClub],
|
||||
)
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={value}>
|
||||
|
|
|
|||
|
|
@ -9,6 +9,33 @@ export default defineConfig({
|
|||
},
|
||||
build: {
|
||||
outDir: 'dist',
|
||||
sourcemap: false
|
||||
sourcemap: false,
|
||||
rollupOptions: {
|
||||
output: {
|
||||
manualChunks(id) {
|
||||
if (!id.includes('node_modules')) return
|
||||
if (id.includes('jspdf')) return 'vendor-pdf'
|
||||
if (id.includes('lucide-react')) return 'vendor-icons'
|
||||
if (
|
||||
id.includes('react-markdown') ||
|
||||
id.includes('/marked/') ||
|
||||
id.includes('remark-') ||
|
||||
id.includes('mdast') ||
|
||||
id.includes('micromark') ||
|
||||
id.includes('unist')
|
||||
) {
|
||||
return 'vendor-markdown'
|
||||
}
|
||||
if (id.includes('react-router')) return 'vendor-router'
|
||||
if (
|
||||
/[/\\]node_modules[/\\]react-dom[/\\]/.test(id) ||
|
||||
/[/\\]node_modules[/\\]react[/\\]/.test(id) ||
|
||||
/[/\\]node_modules[/\\]scheduler[/\\]/.test(id)
|
||||
) {
|
||||
return 'vendor-react'
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user