diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 01426e2c..18e3cdc5 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -1,98 +1,117 @@
+import { Suspense, lazy } from "react";
import { BrowserRouter as Router, Routes, Route } from "react-router";
-import SignIn from "./pages/AuthPages/SignIn";
-import SignUp from "./pages/AuthPages/SignUp";
-import NotFound from "./pages/OtherPage/NotFound";
import AppLayout from "./layout/AppLayout";
import { ScrollToTop } from "./components/common/ScrollToTop";
import ProtectedRoute from "./components/auth/ProtectedRoute";
import GlobalErrorDisplay from "./components/common/GlobalErrorDisplay";
import LoadingStateMonitor from "./components/common/LoadingStateMonitor";
-import Home from "./pages/Dashboard/Home";
+import PageTransition from "./components/common/PageTransition";
-// Planner Module
-import PlannerDashboard from "./pages/Planner/Dashboard";
-import Keywords from "./pages/Planner/Keywords";
-import Clusters from "./pages/Planner/Clusters";
-import Ideas from "./pages/Planner/Ideas";
+// Modern page loader component
+const PageLoader = () => (
+
+);
-// Writer Module
-import WriterDashboard from "./pages/Writer/Dashboard";
-import Tasks from "./pages/Writer/Tasks";
-import Content from "./pages/Writer/Content";
-import Drafts from "./pages/Writer/Drafts";
-import Images from "./pages/Writer/Images";
-import Published from "./pages/Writer/Published";
+// Auth pages - loaded immediately (needed for login)
+import SignIn from "./pages/AuthPages/SignIn";
+import SignUp from "./pages/AuthPages/SignUp";
+import NotFound from "./pages/OtherPage/NotFound";
-// Thinker Module
-import ThinkerDashboard from "./pages/Thinker/Dashboard";
-import Prompts from "./pages/Thinker/Prompts";
-import AuthorProfiles from "./pages/Thinker/AuthorProfiles";
-import ThinkerProfile from "./pages/Thinker/Profile";
-import Strategies from "./pages/Thinker/Strategies";
-import ImageTesting from "./pages/Thinker/ImageTesting";
+// Lazy load all other pages - only loads when navigated to
+const Home = lazy(() => import("./pages/Dashboard/Home"));
-// Billing Module
-import Credits from "./pages/Billing/Credits";
-import Transactions from "./pages/Billing/Transactions";
-import Usage from "./pages/Billing/Usage";
+// Planner Module - Lazy loaded
+const PlannerDashboard = lazy(() => import("./pages/Planner/Dashboard"));
+const Keywords = lazy(() => import("./pages/Planner/Keywords"));
+const Clusters = lazy(() => import("./pages/Planner/Clusters"));
+const Ideas = lazy(() => import("./pages/Planner/Ideas"));
+const KeywordOpportunities = lazy(() => import("./pages/Planner/KeywordOpportunities"));
-// Reference Data
-import SeedKeywords from "./pages/Reference/SeedKeywords";
-import KeywordOpportunities from "./pages/Planner/KeywordOpportunities";
-import ReferenceIndustries from "./pages/Reference/Industries";
+// Writer Module - Lazy loaded
+const WriterDashboard = lazy(() => import("./pages/Writer/Dashboard"));
+const Tasks = lazy(() => import("./pages/Writer/Tasks"));
+const Content = lazy(() => import("./pages/Writer/Content"));
+const Drafts = lazy(() => import("./pages/Writer/Drafts"));
+const Images = lazy(() => import("./pages/Writer/Images"));
+const Published = lazy(() => import("./pages/Writer/Published"));
-// Other Pages
-import Analytics from "./pages/Analytics";
-import Schedules from "./pages/Schedules";
+// Thinker Module - Lazy loaded
+const ThinkerDashboard = lazy(() => import("./pages/Thinker/Dashboard"));
+const Prompts = lazy(() => import("./pages/Thinker/Prompts"));
+const AuthorProfiles = lazy(() => import("./pages/Thinker/AuthorProfiles"));
+const ThinkerProfile = lazy(() => import("./pages/Thinker/Profile"));
+const Strategies = lazy(() => import("./pages/Thinker/Strategies"));
+const ImageTesting = lazy(() => import("./pages/Thinker/ImageTesting"));
-// Settings
-import GeneralSettings from "./pages/Settings/General";
-import Users from "./pages/Settings/Users";
-import Subscriptions from "./pages/Settings/Subscriptions";
-import SystemSettings from "./pages/Settings/System";
-import AccountSettings from "./pages/Settings/Account";
-import ModuleSettings from "./pages/Settings/Modules";
-import AISettings from "./pages/Settings/AI";
-import Plans from "./pages/Settings/Plans";
-import Industries from "./pages/Settings/Industries";
-import Status from "./pages/Settings/Status";
-import Integration from "./pages/Settings/Integration";
-import Sites from "./pages/Settings/Sites";
-import ImportExport from "./pages/Settings/ImportExport";
+// Billing Module - Lazy loaded
+const Credits = lazy(() => import("./pages/Billing/Credits"));
+const Transactions = lazy(() => import("./pages/Billing/Transactions"));
+const Usage = lazy(() => import("./pages/Billing/Usage"));
-// Help
-import Help from "./pages/Help/Help";
-import Docs from "./pages/Help/Docs";
-import SystemTesting from "./pages/Help/SystemTesting";
-import FunctionTesting from "./pages/Help/FunctionTesting";
+// Reference Data - Lazy loaded
+const SeedKeywords = lazy(() => import("./pages/Reference/SeedKeywords"));
+const ReferenceIndustries = lazy(() => import("./pages/Reference/Industries"));
-// Components
-import Components from "./pages/Components";
+// Other Pages - Lazy loaded
+const Analytics = lazy(() => import("./pages/Analytics"));
+const Schedules = lazy(() => import("./pages/Schedules"));
-// UI Elements
-import Alerts from "./pages/Settings/UiElements/Alerts";
-import Avatars from "./pages/Settings/UiElements/Avatars";
-import Badges from "./pages/Settings/UiElements/Badges";
-import Breadcrumb from "./pages/Settings/UiElements/Breadcrumb";
-import Buttons from "./pages/Settings/UiElements/Buttons";
-import ButtonsGroup from "./pages/Settings/UiElements/ButtonsGroup";
-import Cards from "./pages/Settings/UiElements/Cards";
-import Carousel from "./pages/Settings/UiElements/Carousel";
-import Dropdowns from "./pages/Settings/UiElements/Dropdowns";
-import ImagesUI from "./pages/Settings/UiElements/Images";
-import Links from "./pages/Settings/UiElements/Links";
-import List from "./pages/Settings/UiElements/List";
-import Modals from "./pages/Settings/UiElements/Modals";
-import Notifications from "./pages/Settings/UiElements/Notifications";
-import Pagination from "./pages/Settings/UiElements/Pagination";
-import Popovers from "./pages/Settings/UiElements/Popovers";
-import PricingTable from "./pages/Settings/UiElements/PricingTable";
-import Progressbar from "./pages/Settings/UiElements/Progressbar";
-import Ribbons from "./pages/Settings/UiElements/Ribbons";
-import Spinners from "./pages/Settings/UiElements/Spinners";
-import Tabs from "./pages/Settings/UiElements/Tabs";
-import Tooltips from "./pages/Settings/UiElements/Tooltips";
-import Videos from "./pages/Settings/UiElements/Videos";
+// Settings - Lazy loaded
+const GeneralSettings = lazy(() => import("./pages/Settings/General"));
+const Users = lazy(() => import("./pages/Settings/Users"));
+const Subscriptions = lazy(() => import("./pages/Settings/Subscriptions"));
+const SystemSettings = lazy(() => import("./pages/Settings/System"));
+const AccountSettings = lazy(() => import("./pages/Settings/Account"));
+const ModuleSettings = lazy(() => import("./pages/Settings/Modules"));
+const AISettings = lazy(() => import("./pages/Settings/AI"));
+const Plans = lazy(() => import("./pages/Settings/Plans"));
+const Industries = lazy(() => import("./pages/Settings/Industries"));
+const Status = lazy(() => import("./pages/Settings/Status"));
+const Integration = lazy(() => import("./pages/Settings/Integration"));
+const Sites = lazy(() => import("./pages/Settings/Sites"));
+const ImportExport = lazy(() => import("./pages/Settings/ImportExport"));
+
+// Help - Lazy loaded
+const Help = lazy(() => import("./pages/Help/Help"));
+const Docs = lazy(() => import("./pages/Help/Docs"));
+const SystemTesting = lazy(() => import("./pages/Help/SystemTesting"));
+const FunctionTesting = lazy(() => import("./pages/Help/FunctionTesting"));
+
+// Components - Lazy loaded
+const Components = lazy(() => import("./pages/Components"));
+
+// UI Elements - Lazy loaded (rarely used)
+const Alerts = lazy(() => import("./pages/Settings/UiElements/Alerts"));
+const Avatars = lazy(() => import("./pages/Settings/UiElements/Avatars"));
+const Badges = lazy(() => import("./pages/Settings/UiElements/Badges"));
+const Breadcrumb = lazy(() => import("./pages/Settings/UiElements/Breadcrumb"));
+const Buttons = lazy(() => import("./pages/Settings/UiElements/Buttons"));
+const ButtonsGroup = lazy(() => import("./pages/Settings/UiElements/ButtonsGroup"));
+const Cards = lazy(() => import("./pages/Settings/UiElements/Cards"));
+const Carousel = lazy(() => import("./pages/Settings/UiElements/Carousel"));
+const Dropdowns = lazy(() => import("./pages/Settings/UiElements/Dropdowns"));
+const ImagesUI = lazy(() => import("./pages/Settings/UiElements/Images"));
+const Links = lazy(() => import("./pages/Settings/UiElements/Links"));
+const List = lazy(() => import("./pages/Settings/UiElements/List"));
+const Modals = lazy(() => import("./pages/Settings/UiElements/Modals"));
+const Notifications = lazy(() => import("./pages/Settings/UiElements/Notifications"));
+const Pagination = lazy(() => import("./pages/Settings/UiElements/Pagination"));
+const Popovers = lazy(() => import("./pages/Settings/UiElements/Popovers"));
+const PricingTable = lazy(() => import("./pages/Settings/UiElements/PricingTable"));
+const Progressbar = lazy(() => import("./pages/Settings/UiElements/Progressbar"));
+const Ribbons = lazy(() => import("./pages/Settings/UiElements/Ribbons"));
+const Spinners = lazy(() => import("./pages/Settings/UiElements/Spinners"));
+const Tabs = lazy(() => import("./pages/Settings/UiElements/Tabs"));
+const Tooltips = lazy(() => import("./pages/Settings/UiElements/Tooltips"));
+const Videos = lazy(() => import("./pages/Settings/UiElements/Videos"));
export default function App() {
return (
@@ -115,95 +134,497 @@ export default function App() {
}
>
{/* Dashboard */}
- } />
+
+ }>
+
+
+
+ } />
{/* Planner Module */}
- } />
- } />
- } />
- } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
{/* Writer Module */}
- } />
- } />
- } />
- } />
- } />
- } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
{/* Thinker Module */}
- } />
- } />
- } />
- } />
- } />
- } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
{/* Billing Module */}
- } />
- } />
- } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
{/* Reference Data */}
- } />
- } />
- } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
{/* Other Pages */}
- } />
- } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
{/* Settings */}
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
{/* Help */}
- } />
- } />
- } />
- } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
{/* UI Elements */}
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
+
+ }>
+
+
+
+ } />
{/* Components (Showcase Page) */}
- } />
+
+ }>
+
+
+
+ } />
{/* Redirect old notification route */}
- } />
+
+ }>
+
+
+
+ } />
{/* Fallback Route */}
diff --git a/frontend/src/components/common/PageTransition.tsx b/frontend/src/components/common/PageTransition.tsx
new file mode 100644
index 00000000..76f06336
--- /dev/null
+++ b/frontend/src/components/common/PageTransition.tsx
@@ -0,0 +1,76 @@
+import { ReactNode, useEffect, useState } from 'react';
+import { useLocation } from 'react-router';
+
+interface PageTransitionProps {
+ children: ReactNode;
+}
+
+/**
+ * Smooth page transition wrapper with modern loading indicator
+ * Provides seamless transitions between pages without feeling like a page load
+ * Uses subtle fade effects and minimal loading indicator
+ */
+export default function PageTransition({ children }: PageTransitionProps) {
+ const location = useLocation();
+ const [isTransitioning, setIsTransitioning] = useState(false);
+ const [displayChildren, setDisplayChildren] = useState(children);
+ const [currentPath, setCurrentPath] = useState(location.pathname);
+
+ useEffect(() => {
+ // Only show transition if pathname actually changed
+ if (location.pathname === currentPath) {
+ setDisplayChildren(children);
+ return;
+ }
+
+ // Start transition with minimal delay
+ setIsTransitioning(true);
+ setCurrentPath(location.pathname);
+
+ // Quick fade-out, then swap content
+ const fadeOutTimer = setTimeout(() => {
+ setDisplayChildren(children);
+ }, 100);
+
+ // Complete transition quickly for smooth feel
+ const fadeInTimer = setTimeout(() => {
+ setIsTransitioning(false);
+ }, 200);
+
+ return () => {
+ clearTimeout(fadeOutTimer);
+ clearTimeout(fadeInTimer);
+ };
+ }, [location.pathname, children, currentPath]);
+
+ return (
+
+ {/* Subtle fade overlay - very light */}
+
+
+ {/* Minimal loading indicator - only shows briefly */}
+ {isTransitioning && (
+
+ )}
+
+ {/* Page content with smooth fade */}
+
+ {displayChildren}
+
+
+ );
+}
+
diff --git a/frontend/src/layout/AppLayout.tsx b/frontend/src/layout/AppLayout.tsx
index 1004ccf9..855556af 100644
--- a/frontend/src/layout/AppLayout.tsx
+++ b/frontend/src/layout/AppLayout.tsx
@@ -99,27 +99,42 @@ const LayoutContent: React.FC = () => {
}
}, [activeSite?.id, activeSite?.is_active]); // Depend on both ID and is_active
- // Refresh user data on mount and periodically to get latest account/plan changes
+ // Refresh user data on mount and when app version changes (after code updates)
// This ensures changes are reflected immediately without requiring re-login
useEffect(() => {
if (!isAuthenticated) return;
- const refreshUserData = async () => {
+ const APP_VERSION = import.meta.env.VITE_APP_VERSION || '2.0.2';
+ const VERSION_STORAGE_KEY = 'igny8-app-version';
+
+ const refreshUserData = async (force = false) => {
const now = Date.now();
- // Throttle: only refresh if last refresh was more than 30 seconds ago
- if (now - lastUserRefresh.current < 30000) return;
+ // Throttle: only refresh if last refresh was more than 30 seconds ago (unless forced)
+ if (!force && now - lastUserRefresh.current < 30000) return;
try {
lastUserRefresh.current = now;
await refreshUser();
+
+ // Store current version after successful refresh
+ if (force) {
+ localStorage.setItem(VERSION_STORAGE_KEY, APP_VERSION);
+ }
} catch (error) {
// Silently fail - user might still be authenticated
console.debug('User data refresh failed (non-critical):', error);
}
};
- // Refresh on mount
- refreshUserData();
+ // Check if app version changed (indicates code update)
+ const storedVersion = localStorage.getItem(VERSION_STORAGE_KEY);
+ if (storedVersion !== APP_VERSION) {
+ // Force refresh on version change
+ refreshUserData(true);
+ } else {
+ // Normal refresh on mount
+ refreshUserData();
+ }
// Refresh when window becomes visible (user switches back to tab)
const handleVisibilityChange = () => {
@@ -134,7 +149,7 @@ const LayoutContent: React.FC = () => {
};
// Periodic refresh every 2 minutes
- const intervalId = setInterval(refreshUserData, 120000);
+ const intervalId = setInterval(() => refreshUserData(), 120000);
document.addEventListener('visibilitychange', handleVisibilityChange);
window.addEventListener('focus', handleFocus);