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 { Box, CircularProgress, Typography } from '@mui/material';
|
||||
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 GSCAuthCallback from './components/SEODashboard/components/GSCAuthCallback';
|
||||
import Landing from './components/Landing/Landing';
|
||||
import ErrorBoundary from './components/shared/ErrorBoundary';
|
||||
import ErrorBoundaryTest from './components/shared/ErrorBoundaryTest';
|
||||
import { OnboardingProvider } from './contexts/OnboardingContext';
|
||||
import { SubscriptionProvider } from './contexts/SubscriptionContext';
|
||||
import InitialRouteHandler from './components/App/InitialRouteHandler';
|
||||
import TokenInstaller from './components/App/TokenInstaller';
|
||||
import { ConditionalCopilotKit, AuthenticatedCopilotWrapper } from './components/App/CopilotWrappers';
|
||||
import Landing from './components/Landing/Landing';
|
||||
import LazyLoadingFallback from './components/shared/LazyLoadingFallback';
|
||||
|
||||
// interface OnboardingStatus {
|
||||
// onboarding_required: boolean;
|
||||
// onboarding_complete: boolean;
|
||||
// current_step?: number;
|
||||
// total_steps?: number;
|
||||
// completion_percentage?: number;
|
||||
// }
|
||||
// ─── Lazy loaded route components ───────────────────────────────────────────
|
||||
// Default exports
|
||||
const Wizard = React.lazy(() => import('./components/OnboardingWizard/Wizard'));
|
||||
const MainDashboard = React.lazy(() => import('./components/MainDashboard/MainDashboard'));
|
||||
const SEODashboard = React.lazy(() => import('./components/SEODashboard/SEODashboard'));
|
||||
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)
|
||||
const RootRoute: React.FC = () => {
|
||||
@@ -162,78 +171,80 @@ const App: React.FC = () => {
|
||||
<AuthenticatedCopilotWrapper apiKey={copilotApiKey}>
|
||||
<ConditionalCopilotKit>
|
||||
<TokenInstaller />
|
||||
<Routes>
|
||||
<Route path="/" element={<RootRoute />} />
|
||||
<Route
|
||||
path="/onboarding"
|
||||
element={
|
||||
<ErrorBoundary context="Onboarding Wizard" showDetails>
|
||||
<Wizard />
|
||||
</ErrorBoundary>
|
||||
}
|
||||
/>
|
||||
{/* Error Boundary Testing - Development Only */}
|
||||
{process.env.NODE_ENV === 'development' && (
|
||||
<Route path="/error-test" element={<ErrorBoundaryTest />} />
|
||||
)}
|
||||
<Route path="/dashboard" element={<ProtectedRoute><MainDashboard /></ProtectedRoute>} />
|
||||
<Route path="/seo" element={<ProtectedRoute><SEODashboard /></ProtectedRoute>} />
|
||||
<Route path="/seo-dashboard" element={<ProtectedRoute><SEODashboard /></ProtectedRoute>} />
|
||||
<Route path="/content-planning" element={<ProtectedRoute><ContentPlanningDashboard /></ProtectedRoute>} />
|
||||
<Route path="/facebook-writer" element={<ProtectedRoute><FacebookWriter /></ProtectedRoute>} />
|
||||
<Route path="/linkedin-writer" element={<ProtectedRoute><LinkedInWriter /></ProtectedRoute>} />
|
||||
<Route path="/blog-writer" element={<ProtectedRoute><BlogWriter /></ProtectedRoute>} />
|
||||
<Route path="/story-writer" element={<ProtectedRoute><StoryWriter /></ProtectedRoute>} />
|
||||
<Route path="/story-projects" element={<ProtectedRoute><StoryProjectList /></ProtectedRoute>} />
|
||||
<Route path="/youtube-creator" element={<ProtectedRoute><YouTubeCreator /></ProtectedRoute>} />
|
||||
<Route path="/podcast-maker" element={<ProtectedRoute><PodcastDashboard /></ProtectedRoute>} />
|
||||
<Route path="/image-studio" element={<ProtectedRoute><ImageStudioDashboard /></ProtectedRoute>} />
|
||||
<Route path="/video-studio" element={<ProtectedRoute><VideoStudioDashboard /></ProtectedRoute>} />
|
||||
<Route path="/video-studio/create" element={<ProtectedRoute><CreateVideo /></ProtectedRoute>} />
|
||||
<Route path="/video-studio/avatar" element={<ProtectedRoute><AvatarVideo /></ProtectedRoute>} />
|
||||
<Route path="/video-studio/enhance" element={<ProtectedRoute><EnhanceVideo /></ProtectedRoute>} />
|
||||
<Route path="/video-studio/extend" element={<ProtectedRoute><ExtendVideo /></ProtectedRoute>} />
|
||||
<Route path="/video-studio/edit" element={<ProtectedRoute><EditVideo /></ProtectedRoute>} />
|
||||
<Route path="/video-studio/transform" element={<ProtectedRoute><TransformVideo /></ProtectedRoute>} />
|
||||
<Route path="/video-studio/social" element={<ProtectedRoute><SocialVideo /></ProtectedRoute>} />
|
||||
<Route path="/video-studio/face-swap" element={<ProtectedRoute><FaceSwap /></ProtectedRoute>} />
|
||||
<Route path="/video-studio/video-translate" element={<ProtectedRoute><VideoTranslate /></ProtectedRoute>} />
|
||||
<Route path="/video-studio/video-background-remover" element={<ProtectedRoute><VideoBackgroundRemover /></ProtectedRoute>} />
|
||||
<Route path="/video-studio/add-audio-to-video" element={<ProtectedRoute><AddAudioToVideo /></ProtectedRoute>} />
|
||||
<Route path="/video-studio/library" element={<ProtectedRoute><LibraryVideo /></ProtectedRoute>} />
|
||||
<Route path="/image-generator" element={<ProtectedRoute><CreateStudio /></ProtectedRoute>} />
|
||||
<Route path="/image-editor" element={<ProtectedRoute><EditStudio /></ProtectedRoute>} />
|
||||
<Route path="/image-upscale" element={<ProtectedRoute><UpscaleStudio /></ProtectedRoute>} />
|
||||
<Route path="/image-control" element={<ProtectedRoute><ControlStudio /></ProtectedRoute>} />
|
||||
<Route path="/image-studio/face-swap" element={<ProtectedRoute><FaceSwapStudio /></ProtectedRoute>} />
|
||||
<Route path="/image-studio/compress" element={<ProtectedRoute><CompressionStudio /></ProtectedRoute>} />
|
||||
<Route path="/image-studio/processing" element={<ProtectedRoute><ImageProcessingStudio /></ProtectedRoute>} />
|
||||
<Route path="/image-studio/social-optimizer" element={<ProtectedRoute><SocialOptimizer /></ProtectedRoute>} />
|
||||
<Route path="/asset-library" element={<ProtectedRoute><AssetLibrary /></ProtectedRoute>} />
|
||||
<Route path="/campaign-creator" element={<ProtectedRoute><ProductMarketingDashboard /></ProtectedRoute>} />
|
||||
<Route path="/campaign-creator/photoshoot" element={<ProtectedRoute><ProductPhotoshootStudio /></ProtectedRoute>} />
|
||||
<Route path="/campaign-creator/animation" element={<ProtectedRoute><ProductAnimationStudio /></ProtectedRoute>} />
|
||||
<Route path="/campaign-creator/video" element={<ProtectedRoute><ProductVideoStudio /></ProtectedRoute>} />
|
||||
<Route path="/campaign-creator/avatar" element={<ProtectedRoute><ProductAvatarStudio /></ProtectedRoute>} />
|
||||
<Route path="/product-marketing" element={<Navigate to="/campaign-creator" replace />} />
|
||||
<Route path="/scheduler-dashboard" element={<ProtectedRoute><SchedulerDashboard /></ProtectedRoute>} />
|
||||
<Route path="/billing" element={<ProtectedRoute><BillingPage /></ProtectedRoute>} />
|
||||
<Route path="/approvals" element={<ProtectedRoute><ApprovalsPage /></ProtectedRoute>} />
|
||||
<Route path="/team-activity" element={<ProtectedRoute><TeamActivityPage /></ProtectedRoute>} />
|
||||
<Route path="/stripe-disputes" element={<ProtectedRoute><StripeDisputesDashboard /></ProtectedRoute>} />
|
||||
<Route path="/pricing" element={<PricingPage />} />
|
||||
<Route path="/research-test" element={<ResearchDashboard />} />
|
||||
<Route path="/research-dashboard" element={<ResearchDashboard />} />
|
||||
<Route path="/alwrity-researcher" element={<ResearchDashboard />} />
|
||||
<Route path="/intent-research" element={<IntentResearchTest />} />
|
||||
<Route path="/wix-test" element={<WixTestPage />} />
|
||||
<Route path="/wix-test-direct" element={<WixTestPage />} />
|
||||
<Route path="/wix/callback" element={<WixCallbackPage />} />
|
||||
<Route path="/wp/callback" element={<WordPressCallbackPage />} />
|
||||
<Route path="/gsc/callback" element={<GSCAuthCallback />} />
|
||||
<Route path="/bing/callback" element={<BingCallbackPage />} />
|
||||
<Route path="/bing-analytics-storage" element={<ProtectedRoute><BingAnalyticsStorage /></ProtectedRoute>} />
|
||||
</Routes>
|
||||
<Suspense fallback={<LazyLoadingFallback />}>
|
||||
<Routes>
|
||||
<Route path="/" element={<RootRoute />} />
|
||||
<Route
|
||||
path="/onboarding"
|
||||
element={
|
||||
<ErrorBoundary context="Onboarding Wizard" showDetails>
|
||||
<Wizard />
|
||||
</ErrorBoundary>
|
||||
}
|
||||
/>
|
||||
{/* Error Boundary Testing - Development Only */}
|
||||
{process.env.NODE_ENV === 'development' && (
|
||||
<Route path="/error-test" element={<ErrorBoundaryTest />} />
|
||||
)}
|
||||
<Route path="/dashboard" element={<ProtectedRoute><MainDashboard /></ProtectedRoute>} />
|
||||
<Route path="/seo" element={<ProtectedRoute><SEODashboard /></ProtectedRoute>} />
|
||||
<Route path="/seo-dashboard" element={<ProtectedRoute><SEODashboard /></ProtectedRoute>} />
|
||||
<Route path="/content-planning" element={<ProtectedRoute><ContentPlanningDashboard /></ProtectedRoute>} />
|
||||
<Route path="/facebook-writer" element={<ProtectedRoute><FacebookWriter /></ProtectedRoute>} />
|
||||
<Route path="/linkedin-writer" element={<ProtectedRoute><LinkedInWriter /></ProtectedRoute>} />
|
||||
<Route path="/blog-writer" element={<ProtectedRoute><BlogWriter /></ProtectedRoute>} />
|
||||
<Route path="/story-writer" element={<ProtectedRoute><StoryWriter /></ProtectedRoute>} />
|
||||
<Route path="/story-projects" element={<ProtectedRoute><StoryProjectList /></ProtectedRoute>} />
|
||||
<Route path="/youtube-creator" element={<ProtectedRoute><YouTubeCreator /></ProtectedRoute>} />
|
||||
<Route path="/podcast-maker" element={<ProtectedRoute><PodcastDashboard /></ProtectedRoute>} />
|
||||
<Route path="/image-studio" element={<ProtectedRoute><ImageStudioDashboard /></ProtectedRoute>} />
|
||||
<Route path="/video-studio" element={<ProtectedRoute><VideoStudioDashboard /></ProtectedRoute>} />
|
||||
<Route path="/video-studio/create" element={<ProtectedRoute><CreateVideo /></ProtectedRoute>} />
|
||||
<Route path="/video-studio/avatar" element={<ProtectedRoute><AvatarVideo /></ProtectedRoute>} />
|
||||
<Route path="/video-studio/enhance" element={<ProtectedRoute><EnhanceVideo /></ProtectedRoute>} />
|
||||
<Route path="/video-studio/extend" element={<ProtectedRoute><ExtendVideo /></ProtectedRoute>} />
|
||||
<Route path="/video-studio/edit" element={<ProtectedRoute><EditVideo /></ProtectedRoute>} />
|
||||
<Route path="/video-studio/transform" element={<ProtectedRoute><TransformVideo /></ProtectedRoute>} />
|
||||
<Route path="/video-studio/social" element={<ProtectedRoute><SocialVideo /></ProtectedRoute>} />
|
||||
<Route path="/video-studio/face-swap" element={<ProtectedRoute><FaceSwap /></ProtectedRoute>} />
|
||||
<Route path="/video-studio/video-translate" element={<ProtectedRoute><VideoTranslate /></ProtectedRoute>} />
|
||||
<Route path="/video-studio/video-background-remover" element={<ProtectedRoute><VideoBackgroundRemover /></ProtectedRoute>} />
|
||||
<Route path="/video-studio/add-audio-to-video" element={<ProtectedRoute><AddAudioToVideo /></ProtectedRoute>} />
|
||||
<Route path="/video-studio/library" element={<ProtectedRoute><LibraryVideo /></ProtectedRoute>} />
|
||||
<Route path="/image-generator" element={<ProtectedRoute><CreateStudio /></ProtectedRoute>} />
|
||||
<Route path="/image-editor" element={<ProtectedRoute><EditStudio /></ProtectedRoute>} />
|
||||
<Route path="/image-upscale" element={<ProtectedRoute><UpscaleStudio /></ProtectedRoute>} />
|
||||
<Route path="/image-control" element={<ProtectedRoute><ControlStudio /></ProtectedRoute>} />
|
||||
<Route path="/image-studio/face-swap" element={<ProtectedRoute><FaceSwapStudio /></ProtectedRoute>} />
|
||||
<Route path="/image-studio/compress" element={<ProtectedRoute><CompressionStudio /></ProtectedRoute>} />
|
||||
<Route path="/image-studio/processing" element={<ProtectedRoute><ImageProcessingStudio /></ProtectedRoute>} />
|
||||
<Route path="/image-studio/social-optimizer" element={<ProtectedRoute><SocialOptimizer /></ProtectedRoute>} />
|
||||
<Route path="/asset-library" element={<ProtectedRoute><AssetLibrary /></ProtectedRoute>} />
|
||||
<Route path="/campaign-creator" element={<ProtectedRoute><ProductMarketingDashboard /></ProtectedRoute>} />
|
||||
<Route path="/campaign-creator/photoshoot" element={<ProtectedRoute><ProductPhotoshootStudio /></ProtectedRoute>} />
|
||||
<Route path="/campaign-creator/animation" element={<ProtectedRoute><ProductAnimationStudio /></ProtectedRoute>} />
|
||||
<Route path="/campaign-creator/video" element={<ProtectedRoute><ProductVideoStudio /></ProtectedRoute>} />
|
||||
<Route path="/campaign-creator/avatar" element={<ProtectedRoute><ProductAvatarStudio /></ProtectedRoute>} />
|
||||
<Route path="/product-marketing" element={<Navigate to="/campaign-creator" replace />} />
|
||||
<Route path="/scheduler-dashboard" element={<ProtectedRoute><SchedulerDashboard /></ProtectedRoute>} />
|
||||
<Route path="/billing" element={<ProtectedRoute><BillingPage /></ProtectedRoute>} />
|
||||
<Route path="/approvals" element={<ProtectedRoute><ApprovalsPage /></ProtectedRoute>} />
|
||||
<Route path="/team-activity" element={<ProtectedRoute><TeamActivityPage /></ProtectedRoute>} />
|
||||
<Route path="/stripe-disputes" element={<ProtectedRoute><StripeDisputesDashboard /></ProtectedRoute>} />
|
||||
<Route path="/pricing" element={<PricingPage />} />
|
||||
<Route path="/research-test" element={<ResearchDashboard />} />
|
||||
<Route path="/research-dashboard" element={<ResearchDashboard />} />
|
||||
<Route path="/alwrity-researcher" element={<ResearchDashboard />} />
|
||||
<Route path="/intent-research" element={<IntentResearchTest />} />
|
||||
<Route path="/wix-test" element={<WixTestPage />} />
|
||||
<Route path="/wix-test-direct" element={<WixTestPage />} />
|
||||
<Route path="/wix/callback" element={<WixCallbackPage />} />
|
||||
<Route path="/wp/callback" element={<WordPressCallbackPage />} />
|
||||
<Route path="/gsc/callback" element={<GSCAuthCallback />} />
|
||||
<Route path="/bing/callback" element={<BingCallbackPage />} />
|
||||
<Route path="/bing-analytics-storage" element={<ProtectedRoute><BingAnalyticsStorage /></ProtectedRoute>} />
|
||||
</Routes>
|
||||
</Suspense>
|
||||
</ConditionalCopilotKit>
|
||||
</AuthenticatedCopilotWrapper>
|
||||
</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