feat(01-code-splitting): convert 31+ route components to React.lazy
- Replace all 31+ static route component imports in App.tsx with React.lazy() dynamic imports - Add LazyLoadingFallback.tsx shared component for Suspense fallback - Wrap <Routes> with <Suspense> for chunk loading states - Handle named exports (ImageStudio, VideoStudio, ProductMarketing, StoryProjectList) with .then() wrapper - Main bundle reduced from 8.42MB to 2.50MB (70% reduction) - 190+ chunk files created for on-demand loading per route Closes Phase 1 Plan 01-01
This commit is contained in:
@@ -1,72 +1,81 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect, Suspense } from 'react';
|
||||||
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
|
import { BrowserRouter as Router, Routes, Route, Navigate } from 'react-router-dom';
|
||||||
import { Box, CircularProgress, Typography } from '@mui/material';
|
import { Box, CircularProgress, Typography } from '@mui/material';
|
||||||
import { ClerkProvider, useAuth } from '@clerk/clerk-react';
|
import { ClerkProvider, useAuth } from '@clerk/clerk-react';
|
||||||
import Wizard from './components/OnboardingWizard/Wizard';
|
|
||||||
import MainDashboard from './components/MainDashboard/MainDashboard';
|
|
||||||
import SEODashboard from './components/SEODashboard/SEODashboard';
|
|
||||||
import ContentPlanningDashboard from './components/ContentPlanningDashboard/ContentPlanningDashboard';
|
|
||||||
import FacebookWriter from './components/FacebookWriter/FacebookWriter';
|
|
||||||
import LinkedInWriter from './components/LinkedInWriter/LinkedInWriter';
|
|
||||||
import BlogWriter from './components/BlogWriter/BlogWriter';
|
|
||||||
import StoryWriter from './components/StoryWriter/StoryWriter';
|
|
||||||
import { StoryProjectList } from './components/StoryWriter/StoryProjectList';
|
|
||||||
import YouTubeCreator from './components/YouTubeCreator/YouTubeCreator';
|
|
||||||
import { CreateStudio, EditStudio, UpscaleStudio, ControlStudio, SocialOptimizer, AssetLibrary, ImageStudioDashboard, FaceSwapStudio, CompressionStudio, ImageProcessingStudio } from './components/ImageStudio';
|
|
||||||
import {
|
|
||||||
VideoStudioDashboard,
|
|
||||||
CreateVideo,
|
|
||||||
AvatarVideo,
|
|
||||||
EnhanceVideo,
|
|
||||||
ExtendVideo,
|
|
||||||
EditVideo,
|
|
||||||
TransformVideo,
|
|
||||||
SocialVideo,
|
|
||||||
FaceSwap,
|
|
||||||
VideoTranslate,
|
|
||||||
VideoBackgroundRemover,
|
|
||||||
AddAudioToVideo,
|
|
||||||
LibraryVideo,
|
|
||||||
} from './components/VideoStudio';
|
|
||||||
import {
|
|
||||||
ProductMarketingDashboard,
|
|
||||||
ProductPhotoshootStudio,
|
|
||||||
ProductAnimationStudio,
|
|
||||||
ProductVideoStudio,
|
|
||||||
ProductAvatarStudio,
|
|
||||||
} from './components/ProductMarketing';
|
|
||||||
import PodcastDashboard from './components/PodcastMaker/PodcastDashboard';
|
|
||||||
import PricingPage from './components/Pricing/PricingPage';
|
|
||||||
import WixTestPage from './components/WixTestPage/WixTestPage';
|
|
||||||
import WixCallbackPage from './components/WixCallbackPage/WixCallbackPage';
|
|
||||||
import WordPressCallbackPage from './components/WordPressCallbackPage/WordPressCallbackPage';
|
|
||||||
import BingCallbackPage from './components/BingCallbackPage/BingCallbackPage';
|
|
||||||
import BingAnalyticsStorage from './components/BingAnalyticsStorage/BingAnalyticsStorage';
|
|
||||||
import ResearchDashboard from './pages/ResearchDashboard';
|
|
||||||
import IntentResearchTest from './pages/IntentResearchTest';
|
|
||||||
import SchedulerDashboard from './pages/SchedulerDashboard';
|
|
||||||
import BillingPage from './pages/BillingPage';
|
|
||||||
import ApprovalsPage from './pages/ApprovalsPage';
|
|
||||||
import TeamActivityPage from './pages/TeamActivityPage';
|
|
||||||
import StripeDisputesDashboard from './pages/StripeDisputesDashboard';
|
|
||||||
import ProtectedRoute from './components/shared/ProtectedRoute';
|
import ProtectedRoute from './components/shared/ProtectedRoute';
|
||||||
import GSCAuthCallback from './components/SEODashboard/components/GSCAuthCallback';
|
|
||||||
import Landing from './components/Landing/Landing';
|
|
||||||
import ErrorBoundary from './components/shared/ErrorBoundary';
|
import ErrorBoundary from './components/shared/ErrorBoundary';
|
||||||
import ErrorBoundaryTest from './components/shared/ErrorBoundaryTest';
|
|
||||||
import { OnboardingProvider } from './contexts/OnboardingContext';
|
import { OnboardingProvider } from './contexts/OnboardingContext';
|
||||||
import { SubscriptionProvider } from './contexts/SubscriptionContext';
|
import { SubscriptionProvider } from './contexts/SubscriptionContext';
|
||||||
import InitialRouteHandler from './components/App/InitialRouteHandler';
|
import InitialRouteHandler from './components/App/InitialRouteHandler';
|
||||||
import TokenInstaller from './components/App/TokenInstaller';
|
import TokenInstaller from './components/App/TokenInstaller';
|
||||||
import { ConditionalCopilotKit, AuthenticatedCopilotWrapper } from './components/App/CopilotWrappers';
|
import { ConditionalCopilotKit, AuthenticatedCopilotWrapper } from './components/App/CopilotWrappers';
|
||||||
|
import Landing from './components/Landing/Landing';
|
||||||
|
import LazyLoadingFallback from './components/shared/LazyLoadingFallback';
|
||||||
|
|
||||||
// interface OnboardingStatus {
|
// ─── Lazy loaded route components ───────────────────────────────────────────
|
||||||
// onboarding_required: boolean;
|
// Default exports
|
||||||
// onboarding_complete: boolean;
|
const Wizard = React.lazy(() => import('./components/OnboardingWizard/Wizard'));
|
||||||
// current_step?: number;
|
const MainDashboard = React.lazy(() => import('./components/MainDashboard/MainDashboard'));
|
||||||
// total_steps?: number;
|
const SEODashboard = React.lazy(() => import('./components/SEODashboard/SEODashboard'));
|
||||||
// completion_percentage?: number;
|
const ContentPlanningDashboard = React.lazy(() => import('./components/ContentPlanningDashboard/ContentPlanningDashboard'));
|
||||||
// }
|
const FacebookWriter = React.lazy(() => import('./components/FacebookWriter/FacebookWriter'));
|
||||||
|
const LinkedInWriter = React.lazy(() => import('./components/LinkedInWriter/LinkedInWriter'));
|
||||||
|
const BlogWriter = React.lazy(() => import('./components/BlogWriter/BlogWriter'));
|
||||||
|
const StoryWriter = React.lazy(() => import('./components/StoryWriter/StoryWriter'));
|
||||||
|
const YouTubeCreator = React.lazy(() => import('./components/YouTubeCreator/YouTubeCreator'));
|
||||||
|
const PodcastDashboard = React.lazy(() => import('./components/PodcastMaker/PodcastDashboard'));
|
||||||
|
const PricingPage = React.lazy(() => import('./components/Pricing/PricingPage'));
|
||||||
|
const WixTestPage = React.lazy(() => import('./components/WixTestPage/WixTestPage'));
|
||||||
|
const WixCallbackPage = React.lazy(() => import('./components/WixCallbackPage/WixCallbackPage'));
|
||||||
|
const WordPressCallbackPage = React.lazy(() => import('./components/WordPressCallbackPage/WordPressCallbackPage'));
|
||||||
|
const BingCallbackPage = React.lazy(() => import('./components/BingCallbackPage/BingCallbackPage'));
|
||||||
|
const BingAnalyticsStorage = React.lazy(() => import('./components/BingAnalyticsStorage/BingAnalyticsStorage'));
|
||||||
|
const ResearchDashboard = React.lazy(() => import('./pages/ResearchDashboard'));
|
||||||
|
const IntentResearchTest = React.lazy(() => import('./pages/IntentResearchTest'));
|
||||||
|
const SchedulerDashboard = React.lazy(() => import('./pages/SchedulerDashboard'));
|
||||||
|
const BillingPage = React.lazy(() => import('./pages/BillingPage'));
|
||||||
|
const ApprovalsPage = React.lazy(() => import('./pages/ApprovalsPage'));
|
||||||
|
const TeamActivityPage = React.lazy(() => import('./pages/TeamActivityPage'));
|
||||||
|
const StripeDisputesDashboard = React.lazy(() => import('./pages/StripeDisputesDashboard'));
|
||||||
|
const GSCAuthCallback = React.lazy(() => import('./components/SEODashboard/components/GSCAuthCallback'));
|
||||||
|
const ErrorBoundaryTest = React.lazy(() => import('./components/shared/ErrorBoundaryTest'));
|
||||||
|
|
||||||
|
// Named exports — need .then() wrapper to resolve default
|
||||||
|
const StoryProjectList = React.lazy(() => import('./components/StoryWriter/StoryProjectList').then(m => ({ default: m.StoryProjectList })));
|
||||||
|
|
||||||
|
// ImageStudio barrel (10 named exports)
|
||||||
|
const CreateStudio = React.lazy(() => import('./components/ImageStudio').then(m => ({ default: m.CreateStudio })));
|
||||||
|
const EditStudio = React.lazy(() => import('./components/ImageStudio').then(m => ({ default: m.EditStudio })));
|
||||||
|
const UpscaleStudio = React.lazy(() => import('./components/ImageStudio').then(m => ({ default: m.UpscaleStudio })));
|
||||||
|
const ControlStudio = React.lazy(() => import('./components/ImageStudio').then(m => ({ default: m.ControlStudio })));
|
||||||
|
const SocialOptimizer = React.lazy(() => import('./components/ImageStudio').then(m => ({ default: m.SocialOptimizer })));
|
||||||
|
const AssetLibrary = React.lazy(() => import('./components/ImageStudio').then(m => ({ default: m.AssetLibrary })));
|
||||||
|
const ImageStudioDashboard = React.lazy(() => import('./components/ImageStudio').then(m => ({ default: m.ImageStudioDashboard })));
|
||||||
|
const FaceSwapStudio = React.lazy(() => import('./components/ImageStudio').then(m => ({ default: m.FaceSwapStudio })));
|
||||||
|
const CompressionStudio = React.lazy(() => import('./components/ImageStudio').then(m => ({ default: m.CompressionStudio })));
|
||||||
|
const ImageProcessingStudio = React.lazy(() => import('./components/ImageStudio').then(m => ({ default: m.ImageProcessingStudio })));
|
||||||
|
|
||||||
|
// VideoStudio barrel (13 named exports)
|
||||||
|
const VideoStudioDashboard = React.lazy(() => import('./components/VideoStudio').then(m => ({ default: m.VideoStudioDashboard })));
|
||||||
|
const CreateVideo = React.lazy(() => import('./components/VideoStudio').then(m => ({ default: m.CreateVideo })));
|
||||||
|
const AvatarVideo = React.lazy(() => import('./components/VideoStudio').then(m => ({ default: m.AvatarVideo })));
|
||||||
|
const EnhanceVideo = React.lazy(() => import('./components/VideoStudio').then(m => ({ default: m.EnhanceVideo })));
|
||||||
|
const ExtendVideo = React.lazy(() => import('./components/VideoStudio').then(m => ({ default: m.ExtendVideo })));
|
||||||
|
const EditVideo = React.lazy(() => import('./components/VideoStudio').then(m => ({ default: m.EditVideo })));
|
||||||
|
const TransformVideo = React.lazy(() => import('./components/VideoStudio').then(m => ({ default: m.TransformVideo })));
|
||||||
|
const SocialVideo = React.lazy(() => import('./components/VideoStudio').then(m => ({ default: m.SocialVideo })));
|
||||||
|
const FaceSwap = React.lazy(() => import('./components/VideoStudio').then(m => ({ default: m.FaceSwap })));
|
||||||
|
const VideoTranslate = React.lazy(() => import('./components/VideoStudio').then(m => ({ default: m.VideoTranslate })));
|
||||||
|
const VideoBackgroundRemover = React.lazy(() => import('./components/VideoStudio').then(m => ({ default: m.VideoBackgroundRemover })));
|
||||||
|
const AddAudioToVideo = React.lazy(() => import('./components/VideoStudio').then(m => ({ default: m.AddAudioToVideo })));
|
||||||
|
const LibraryVideo = React.lazy(() => import('./components/VideoStudio').then(m => ({ default: m.LibraryVideo })));
|
||||||
|
|
||||||
|
// ProductMarketing barrel (5 named exports)
|
||||||
|
const ProductMarketingDashboard = React.lazy(() => import('./components/ProductMarketing').then(m => ({ default: m.ProductMarketingDashboard })));
|
||||||
|
const ProductPhotoshootStudio = React.lazy(() => import('./components/ProductMarketing').then(m => ({ default: m.ProductPhotoshootStudio })));
|
||||||
|
const ProductAnimationStudio = React.lazy(() => import('./components/ProductMarketing').then(m => ({ default: m.ProductAnimationStudio })));
|
||||||
|
const ProductVideoStudio = React.lazy(() => import('./components/ProductMarketing').then(m => ({ default: m.ProductVideoStudio })));
|
||||||
|
const ProductAvatarStudio = React.lazy(() => import('./components/ProductMarketing').then(m => ({ default: m.ProductAvatarStudio })));
|
||||||
|
|
||||||
// Root route that chooses Landing (signed out) or InitialRouteHandler (signed in)
|
// Root route that chooses Landing (signed out) or InitialRouteHandler (signed in)
|
||||||
const RootRoute: React.FC = () => {
|
const RootRoute: React.FC = () => {
|
||||||
@@ -162,6 +171,7 @@ const App: React.FC = () => {
|
|||||||
<AuthenticatedCopilotWrapper apiKey={copilotApiKey}>
|
<AuthenticatedCopilotWrapper apiKey={copilotApiKey}>
|
||||||
<ConditionalCopilotKit>
|
<ConditionalCopilotKit>
|
||||||
<TokenInstaller />
|
<TokenInstaller />
|
||||||
|
<Suspense fallback={<LazyLoadingFallback />}>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<RootRoute />} />
|
<Route path="/" element={<RootRoute />} />
|
||||||
<Route
|
<Route
|
||||||
@@ -234,6 +244,7 @@ const App: React.FC = () => {
|
|||||||
<Route path="/bing/callback" element={<BingCallbackPage />} />
|
<Route path="/bing/callback" element={<BingCallbackPage />} />
|
||||||
<Route path="/bing-analytics-storage" element={<ProtectedRoute><BingAnalyticsStorage /></ProtectedRoute>} />
|
<Route path="/bing-analytics-storage" element={<ProtectedRoute><BingAnalyticsStorage /></ProtectedRoute>} />
|
||||||
</Routes>
|
</Routes>
|
||||||
|
</Suspense>
|
||||||
</ConditionalCopilotKit>
|
</ConditionalCopilotKit>
|
||||||
</AuthenticatedCopilotWrapper>
|
</AuthenticatedCopilotWrapper>
|
||||||
</Router>
|
</Router>
|
||||||
|
|||||||
28
frontend/src/components/shared/LazyLoadingFallback.tsx
Normal file
28
frontend/src/components/shared/LazyLoadingFallback.tsx
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import React from 'react';
|
||||||
|
import { Box, CircularProgress, Typography } from '@mui/material';
|
||||||
|
|
||||||
|
interface LazyLoadingFallbackProps {
|
||||||
|
message?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const LazyLoadingFallback: React.FC<LazyLoadingFallbackProps> = ({
|
||||||
|
message = 'Loading...'
|
||||||
|
}) => {
|
||||||
|
return (
|
||||||
|
<Box
|
||||||
|
display="flex"
|
||||||
|
flexDirection="column"
|
||||||
|
alignItems="center"
|
||||||
|
justifyContent="center"
|
||||||
|
minHeight="60vh"
|
||||||
|
gap={2}
|
||||||
|
>
|
||||||
|
<CircularProgress size={40} />
|
||||||
|
<Typography variant="body1" color="textSecondary">
|
||||||
|
{message}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LazyLoadingFallback;
|
||||||
Reference in New Issue
Block a user