page transition
This commit is contained in:
@@ -1,98 +1,117 @@
|
|||||||
|
import { Suspense, lazy } from "react";
|
||||||
import { BrowserRouter as Router, Routes, Route } from "react-router";
|
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 AppLayout from "./layout/AppLayout";
|
||||||
import { ScrollToTop } from "./components/common/ScrollToTop";
|
import { ScrollToTop } from "./components/common/ScrollToTop";
|
||||||
import ProtectedRoute from "./components/auth/ProtectedRoute";
|
import ProtectedRoute from "./components/auth/ProtectedRoute";
|
||||||
import GlobalErrorDisplay from "./components/common/GlobalErrorDisplay";
|
import GlobalErrorDisplay from "./components/common/GlobalErrorDisplay";
|
||||||
import LoadingStateMonitor from "./components/common/LoadingStateMonitor";
|
import LoadingStateMonitor from "./components/common/LoadingStateMonitor";
|
||||||
import Home from "./pages/Dashboard/Home";
|
import PageTransition from "./components/common/PageTransition";
|
||||||
|
|
||||||
// Planner Module
|
// Modern page loader component
|
||||||
import PlannerDashboard from "./pages/Planner/Dashboard";
|
const PageLoader = () => (
|
||||||
import Keywords from "./pages/Planner/Keywords";
|
<div className="flex items-center justify-center min-h-screen bg-white dark:bg-gray-900">
|
||||||
import Clusters from "./pages/Planner/Clusters";
|
<div className="flex flex-col items-center gap-3">
|
||||||
import Ideas from "./pages/Planner/Ideas";
|
<div className="relative w-12 h-12">
|
||||||
|
<div className="absolute inset-0 border-4 border-gray-200 dark:border-gray-700 rounded-full"></div>
|
||||||
|
<div className="absolute inset-0 border-4 border-transparent border-t-brand-500 rounded-full animate-spin"></div>
|
||||||
|
</div>
|
||||||
|
<div className="w-2 h-2 bg-brand-500 rounded-full animate-pulse"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
// Writer Module
|
// Auth pages - loaded immediately (needed for login)
|
||||||
import WriterDashboard from "./pages/Writer/Dashboard";
|
import SignIn from "./pages/AuthPages/SignIn";
|
||||||
import Tasks from "./pages/Writer/Tasks";
|
import SignUp from "./pages/AuthPages/SignUp";
|
||||||
import Content from "./pages/Writer/Content";
|
import NotFound from "./pages/OtherPage/NotFound";
|
||||||
import Drafts from "./pages/Writer/Drafts";
|
|
||||||
import Images from "./pages/Writer/Images";
|
|
||||||
import Published from "./pages/Writer/Published";
|
|
||||||
|
|
||||||
// Thinker Module
|
// Lazy load all other pages - only loads when navigated to
|
||||||
import ThinkerDashboard from "./pages/Thinker/Dashboard";
|
const Home = lazy(() => import("./pages/Dashboard/Home"));
|
||||||
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";
|
|
||||||
|
|
||||||
// Billing Module
|
// Planner Module - Lazy loaded
|
||||||
import Credits from "./pages/Billing/Credits";
|
const PlannerDashboard = lazy(() => import("./pages/Planner/Dashboard"));
|
||||||
import Transactions from "./pages/Billing/Transactions";
|
const Keywords = lazy(() => import("./pages/Planner/Keywords"));
|
||||||
import Usage from "./pages/Billing/Usage";
|
const Clusters = lazy(() => import("./pages/Planner/Clusters"));
|
||||||
|
const Ideas = lazy(() => import("./pages/Planner/Ideas"));
|
||||||
|
const KeywordOpportunities = lazy(() => import("./pages/Planner/KeywordOpportunities"));
|
||||||
|
|
||||||
// Reference Data
|
// Writer Module - Lazy loaded
|
||||||
import SeedKeywords from "./pages/Reference/SeedKeywords";
|
const WriterDashboard = lazy(() => import("./pages/Writer/Dashboard"));
|
||||||
import KeywordOpportunities from "./pages/Planner/KeywordOpportunities";
|
const Tasks = lazy(() => import("./pages/Writer/Tasks"));
|
||||||
import ReferenceIndustries from "./pages/Reference/Industries";
|
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
|
// Thinker Module - Lazy loaded
|
||||||
import Analytics from "./pages/Analytics";
|
const ThinkerDashboard = lazy(() => import("./pages/Thinker/Dashboard"));
|
||||||
import Schedules from "./pages/Schedules";
|
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
|
// Billing Module - Lazy loaded
|
||||||
import GeneralSettings from "./pages/Settings/General";
|
const Credits = lazy(() => import("./pages/Billing/Credits"));
|
||||||
import Users from "./pages/Settings/Users";
|
const Transactions = lazy(() => import("./pages/Billing/Transactions"));
|
||||||
import Subscriptions from "./pages/Settings/Subscriptions";
|
const Usage = lazy(() => import("./pages/Billing/Usage"));
|
||||||
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";
|
|
||||||
|
|
||||||
// Help
|
// Reference Data - Lazy loaded
|
||||||
import Help from "./pages/Help/Help";
|
const SeedKeywords = lazy(() => import("./pages/Reference/SeedKeywords"));
|
||||||
import Docs from "./pages/Help/Docs";
|
const ReferenceIndustries = lazy(() => import("./pages/Reference/Industries"));
|
||||||
import SystemTesting from "./pages/Help/SystemTesting";
|
|
||||||
import FunctionTesting from "./pages/Help/FunctionTesting";
|
|
||||||
|
|
||||||
// Components
|
// Other Pages - Lazy loaded
|
||||||
import Components from "./pages/Components";
|
const Analytics = lazy(() => import("./pages/Analytics"));
|
||||||
|
const Schedules = lazy(() => import("./pages/Schedules"));
|
||||||
|
|
||||||
// UI Elements
|
// Settings - Lazy loaded
|
||||||
import Alerts from "./pages/Settings/UiElements/Alerts";
|
const GeneralSettings = lazy(() => import("./pages/Settings/General"));
|
||||||
import Avatars from "./pages/Settings/UiElements/Avatars";
|
const Users = lazy(() => import("./pages/Settings/Users"));
|
||||||
import Badges from "./pages/Settings/UiElements/Badges";
|
const Subscriptions = lazy(() => import("./pages/Settings/Subscriptions"));
|
||||||
import Breadcrumb from "./pages/Settings/UiElements/Breadcrumb";
|
const SystemSettings = lazy(() => import("./pages/Settings/System"));
|
||||||
import Buttons from "./pages/Settings/UiElements/Buttons";
|
const AccountSettings = lazy(() => import("./pages/Settings/Account"));
|
||||||
import ButtonsGroup from "./pages/Settings/UiElements/ButtonsGroup";
|
const ModuleSettings = lazy(() => import("./pages/Settings/Modules"));
|
||||||
import Cards from "./pages/Settings/UiElements/Cards";
|
const AISettings = lazy(() => import("./pages/Settings/AI"));
|
||||||
import Carousel from "./pages/Settings/UiElements/Carousel";
|
const Plans = lazy(() => import("./pages/Settings/Plans"));
|
||||||
import Dropdowns from "./pages/Settings/UiElements/Dropdowns";
|
const Industries = lazy(() => import("./pages/Settings/Industries"));
|
||||||
import ImagesUI from "./pages/Settings/UiElements/Images";
|
const Status = lazy(() => import("./pages/Settings/Status"));
|
||||||
import Links from "./pages/Settings/UiElements/Links";
|
const Integration = lazy(() => import("./pages/Settings/Integration"));
|
||||||
import List from "./pages/Settings/UiElements/List";
|
const Sites = lazy(() => import("./pages/Settings/Sites"));
|
||||||
import Modals from "./pages/Settings/UiElements/Modals";
|
const ImportExport = lazy(() => import("./pages/Settings/ImportExport"));
|
||||||
import Notifications from "./pages/Settings/UiElements/Notifications";
|
|
||||||
import Pagination from "./pages/Settings/UiElements/Pagination";
|
// Help - Lazy loaded
|
||||||
import Popovers from "./pages/Settings/UiElements/Popovers";
|
const Help = lazy(() => import("./pages/Help/Help"));
|
||||||
import PricingTable from "./pages/Settings/UiElements/PricingTable";
|
const Docs = lazy(() => import("./pages/Help/Docs"));
|
||||||
import Progressbar from "./pages/Settings/UiElements/Progressbar";
|
const SystemTesting = lazy(() => import("./pages/Help/SystemTesting"));
|
||||||
import Ribbons from "./pages/Settings/UiElements/Ribbons";
|
const FunctionTesting = lazy(() => import("./pages/Help/FunctionTesting"));
|
||||||
import Spinners from "./pages/Settings/UiElements/Spinners";
|
|
||||||
import Tabs from "./pages/Settings/UiElements/Tabs";
|
// Components - Lazy loaded
|
||||||
import Tooltips from "./pages/Settings/UiElements/Tooltips";
|
const Components = lazy(() => import("./pages/Components"));
|
||||||
import Videos from "./pages/Settings/UiElements/Videos";
|
|
||||||
|
// 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() {
|
export default function App() {
|
||||||
return (
|
return (
|
||||||
@@ -115,95 +134,497 @@ export default function App() {
|
|||||||
}
|
}
|
||||||
>
|
>
|
||||||
{/* Dashboard */}
|
{/* Dashboard */}
|
||||||
<Route index path="/" element={<Home />} />
|
<Route index path="/" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Home />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
|
||||||
{/* Planner Module */}
|
{/* Planner Module */}
|
||||||
<Route path="/planner" element={<PlannerDashboard />} />
|
<Route path="/planner" element={
|
||||||
<Route path="/planner/keywords" element={<Keywords />} />
|
<PageTransition>
|
||||||
<Route path="/planner/clusters" element={<Clusters />} />
|
<Suspense fallback={<PageLoader />}>
|
||||||
<Route path="/planner/ideas" element={<Ideas />} />
|
<PlannerDashboard />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/planner/keywords" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Keywords />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/planner/clusters" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Clusters />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/planner/ideas" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Ideas />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
|
||||||
{/* Writer Module */}
|
{/* Writer Module */}
|
||||||
<Route path="/writer" element={<WriterDashboard />} />
|
<Route path="/writer" element={
|
||||||
<Route path="/writer/tasks" element={<Tasks />} />
|
<PageTransition>
|
||||||
<Route path="/writer/content" element={<Content />} />
|
<Suspense fallback={<PageLoader />}>
|
||||||
<Route path="/writer/drafts" element={<Drafts />} />
|
<WriterDashboard />
|
||||||
<Route path="/writer/images" element={<Images />} />
|
</Suspense>
|
||||||
<Route path="/writer/published" element={<Published />} />
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/writer/tasks" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Tasks />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/writer/content" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Content />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/writer/drafts" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Drafts />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/writer/images" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Images />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/writer/published" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Published />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
|
||||||
{/* Thinker Module */}
|
{/* Thinker Module */}
|
||||||
<Route path="/thinker" element={<ThinkerDashboard />} />
|
<Route path="/thinker" element={
|
||||||
<Route path="/thinker/prompts" element={<Prompts />} />
|
<PageTransition>
|
||||||
<Route path="/thinker/author-profiles" element={<AuthorProfiles />} />
|
<Suspense fallback={<PageLoader />}>
|
||||||
<Route path="/thinker/profile" element={<ThinkerProfile />} />
|
<ThinkerDashboard />
|
||||||
<Route path="/thinker/strategies" element={<Strategies />} />
|
</Suspense>
|
||||||
<Route path="/thinker/image-testing" element={<ImageTesting />} />
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/thinker/prompts" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Prompts />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/thinker/author-profiles" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<AuthorProfiles />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/thinker/profile" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<ThinkerProfile />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/thinker/strategies" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Strategies />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/thinker/image-testing" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<ImageTesting />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
|
||||||
{/* Billing Module */}
|
{/* Billing Module */}
|
||||||
<Route path="/billing/credits" element={<Credits />} />
|
<Route path="/billing/credits" element={
|
||||||
<Route path="/billing/transactions" element={<Transactions />} />
|
<PageTransition>
|
||||||
<Route path="/billing/usage" element={<Usage />} />
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Credits />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/billing/transactions" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Transactions />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/billing/usage" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Usage />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
|
||||||
{/* Reference Data */}
|
{/* Reference Data */}
|
||||||
<Route path="/reference/seed-keywords" element={<SeedKeywords />} />
|
<Route path="/reference/seed-keywords" element={
|
||||||
<Route path="/planner/keyword-opportunities" element={<KeywordOpportunities />} />
|
<PageTransition>
|
||||||
<Route path="/reference/industries" element={<ReferenceIndustries />} />
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<SeedKeywords />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/planner/keyword-opportunities" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<KeywordOpportunities />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/reference/industries" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<ReferenceIndustries />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
|
||||||
{/* Other Pages */}
|
{/* Other Pages */}
|
||||||
<Route path="/analytics" element={<Analytics />} />
|
<Route path="/analytics" element={
|
||||||
<Route path="/schedules" element={<Schedules />} />
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Analytics />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/schedules" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Schedules />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
|
||||||
{/* Settings */}
|
{/* Settings */}
|
||||||
<Route path="/settings" element={<GeneralSettings />} />
|
<Route path="/settings" element={
|
||||||
<Route path="/settings/users" element={<Users />} />
|
<PageTransition>
|
||||||
<Route path="/settings/subscriptions" element={<Subscriptions />} />
|
<Suspense fallback={<PageLoader />}>
|
||||||
<Route path="/settings/system" element={<SystemSettings />} />
|
<GeneralSettings />
|
||||||
<Route path="/settings/account" element={<AccountSettings />} />
|
</Suspense>
|
||||||
<Route path="/settings/modules" element={<ModuleSettings />} />
|
</PageTransition>
|
||||||
<Route path="/settings/ai" element={<AISettings />} />
|
} />
|
||||||
<Route path="/settings/plans" element={<Plans />} />
|
<Route path="/settings/users" element={
|
||||||
<Route path="/settings/industries" element={<Industries />} />
|
<PageTransition>
|
||||||
<Route path="/settings/status" element={<Status />} />
|
<Suspense fallback={<PageLoader />}>
|
||||||
<Route path="/settings/integration" element={<Integration />} />
|
<Users />
|
||||||
<Route path="/settings/sites" element={<Sites />} />
|
</Suspense>
|
||||||
<Route path="/settings/import-export" element={<ImportExport />} />
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/settings/subscriptions" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Subscriptions />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/settings/system" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<SystemSettings />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/settings/account" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<AccountSettings />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/settings/modules" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<ModuleSettings />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/settings/ai" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<AISettings />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/settings/plans" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Plans />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/settings/industries" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Industries />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/settings/status" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Status />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/settings/integration" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Integration />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/settings/sites" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Sites />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/settings/import-export" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<ImportExport />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
|
||||||
{/* Help */}
|
{/* Help */}
|
||||||
<Route path="/help" element={<Help />} />
|
<Route path="/help" element={
|
||||||
<Route path="/help/docs" element={<Docs />} />
|
<PageTransition>
|
||||||
<Route path="/help/system-testing" element={<SystemTesting />} />
|
<Suspense fallback={<PageLoader />}>
|
||||||
<Route path="/help/function-testing" element={<FunctionTesting />} />
|
<Help />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/help/docs" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Docs />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/help/system-testing" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<SystemTesting />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/help/function-testing" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<FunctionTesting />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
|
||||||
{/* UI Elements */}
|
{/* UI Elements */}
|
||||||
<Route path="/ui-elements/alerts" element={<Alerts />} />
|
<Route path="/ui-elements/alerts" element={
|
||||||
<Route path="/ui-elements/avatars" element={<Avatars />} />
|
<PageTransition>
|
||||||
<Route path="/ui-elements/badges" element={<Badges />} />
|
<Suspense fallback={<PageLoader />}>
|
||||||
<Route path="/ui-elements/breadcrumb" element={<Breadcrumb />} />
|
<Alerts />
|
||||||
<Route path="/ui-elements/buttons" element={<Buttons />} />
|
</Suspense>
|
||||||
<Route path="/ui-elements/buttons-group" element={<ButtonsGroup />} />
|
</PageTransition>
|
||||||
<Route path="/ui-elements/cards" element={<Cards />} />
|
} />
|
||||||
<Route path="/ui-elements/carousel" element={<Carousel />} />
|
<Route path="/ui-elements/avatars" element={
|
||||||
<Route path="/ui-elements/dropdowns" element={<Dropdowns />} />
|
<PageTransition>
|
||||||
<Route path="/ui-elements/images" element={<ImagesUI />} />
|
<Suspense fallback={<PageLoader />}>
|
||||||
<Route path="/ui-elements/links" element={<Links />} />
|
<Avatars />
|
||||||
<Route path="/ui-elements/list" element={<List />} />
|
</Suspense>
|
||||||
<Route path="/ui-elements/modals" element={<Modals />} />
|
</PageTransition>
|
||||||
<Route path="/ui-elements/notifications" element={<Notifications />} />
|
} />
|
||||||
<Route path="/ui-elements/pagination" element={<Pagination />} />
|
<Route path="/ui-elements/badges" element={
|
||||||
<Route path="/ui-elements/popovers" element={<Popovers />} />
|
<PageTransition>
|
||||||
<Route path="/ui-elements/pricing-table" element={<PricingTable />} />
|
<Suspense fallback={<PageLoader />}>
|
||||||
<Route path="/ui-elements/progressbar" element={<Progressbar />} />
|
<Badges />
|
||||||
<Route path="/ui-elements/ribbons" element={<Ribbons />} />
|
</Suspense>
|
||||||
<Route path="/ui-elements/spinners" element={<Spinners />} />
|
</PageTransition>
|
||||||
<Route path="/ui-elements/tabs" element={<Tabs />} />
|
} />
|
||||||
<Route path="/ui-elements/tooltips" element={<Tooltips />} />
|
<Route path="/ui-elements/breadcrumb" element={
|
||||||
<Route path="/ui-elements/videos" element={<Videos />} />
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Breadcrumb />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/ui-elements/buttons" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Buttons />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/ui-elements/buttons-group" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<ButtonsGroup />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/ui-elements/cards" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Cards />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/ui-elements/carousel" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Carousel />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/ui-elements/dropdowns" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Dropdowns />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/ui-elements/images" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<ImagesUI />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/ui-elements/links" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Links />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/ui-elements/list" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<List />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/ui-elements/modals" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Modals />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/ui-elements/notifications" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Notifications />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/ui-elements/pagination" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Pagination />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/ui-elements/popovers" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Popovers />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/ui-elements/pricing-table" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<PricingTable />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/ui-elements/progressbar" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Progressbar />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/ui-elements/ribbons" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Ribbons />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/ui-elements/spinners" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Spinners />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/ui-elements/tabs" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Tabs />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/ui-elements/tooltips" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Tooltips />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
<Route path="/ui-elements/videos" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Videos />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
|
||||||
{/* Components (Showcase Page) */}
|
{/* Components (Showcase Page) */}
|
||||||
<Route path="/components" element={<Components />} />
|
<Route path="/components" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Components />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
|
|
||||||
{/* Redirect old notification route */}
|
{/* Redirect old notification route */}
|
||||||
<Route path="/notifications" element={<Notifications />} />
|
<Route path="/notifications" element={
|
||||||
|
<PageTransition>
|
||||||
|
<Suspense fallback={<PageLoader />}>
|
||||||
|
<Notifications />
|
||||||
|
</Suspense>
|
||||||
|
</PageTransition>
|
||||||
|
} />
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
{/* Fallback Route */}
|
{/* Fallback Route */}
|
||||||
|
|||||||
76
frontend/src/components/common/PageTransition.tsx
Normal file
76
frontend/src/components/common/PageTransition.tsx
Normal file
@@ -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 (
|
||||||
|
<div className="relative min-h-screen">
|
||||||
|
{/* Subtle fade overlay - very light */}
|
||||||
|
<div
|
||||||
|
className={`absolute inset-0 bg-white/50 dark:bg-gray-900/50 backdrop-blur-sm transition-opacity duration-200 z-40 pointer-events-none ${
|
||||||
|
isTransitioning ? 'opacity-100' : 'opacity-0'
|
||||||
|
}`}
|
||||||
|
/>
|
||||||
|
|
||||||
|
{/* Minimal loading indicator - only shows briefly */}
|
||||||
|
{isTransitioning && (
|
||||||
|
<div className="fixed top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 z-50 pointer-events-none">
|
||||||
|
<div className="relative w-10 h-10">
|
||||||
|
<div className="absolute inset-0 border-2 border-gray-200/50 dark:border-gray-700/50 rounded-full"></div>
|
||||||
|
<div className="absolute inset-0 border-2 border-transparent border-t-brand-500 rounded-full animate-spin"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{/* Page content with smooth fade */}
|
||||||
|
<div
|
||||||
|
className={`transition-opacity duration-200 ease-in-out ${
|
||||||
|
isTransitioning ? 'opacity-0' : 'opacity-100'
|
||||||
|
}`}
|
||||||
|
>
|
||||||
|
{displayChildren}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
@@ -99,27 +99,42 @@ const LayoutContent: React.FC = () => {
|
|||||||
}
|
}
|
||||||
}, [activeSite?.id, activeSite?.is_active]); // Depend on both ID and is_active
|
}, [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
|
// This ensures changes are reflected immediately without requiring re-login
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!isAuthenticated) return;
|
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();
|
const now = Date.now();
|
||||||
// Throttle: only refresh if last refresh was more than 30 seconds ago
|
// Throttle: only refresh if last refresh was more than 30 seconds ago (unless forced)
|
||||||
if (now - lastUserRefresh.current < 30000) return;
|
if (!force && now - lastUserRefresh.current < 30000) return;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
lastUserRefresh.current = now;
|
lastUserRefresh.current = now;
|
||||||
await refreshUser();
|
await refreshUser();
|
||||||
|
|
||||||
|
// Store current version after successful refresh
|
||||||
|
if (force) {
|
||||||
|
localStorage.setItem(VERSION_STORAGE_KEY, APP_VERSION);
|
||||||
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// Silently fail - user might still be authenticated
|
// Silently fail - user might still be authenticated
|
||||||
console.debug('User data refresh failed (non-critical):', error);
|
console.debug('User data refresh failed (non-critical):', error);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Refresh on mount
|
// Check if app version changed (indicates code update)
|
||||||
refreshUserData();
|
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)
|
// Refresh when window becomes visible (user switches back to tab)
|
||||||
const handleVisibilityChange = () => {
|
const handleVisibilityChange = () => {
|
||||||
@@ -134,7 +149,7 @@ const LayoutContent: React.FC = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Periodic refresh every 2 minutes
|
// Periodic refresh every 2 minutes
|
||||||
const intervalId = setInterval(refreshUserData, 120000);
|
const intervalId = setInterval(() => refreshUserData(), 120000);
|
||||||
|
|
||||||
document.addEventListener('visibilitychange', handleVisibilityChange);
|
document.addEventListener('visibilitychange', handleVisibilityChange);
|
||||||
window.addEventListener('focus', handleFocus);
|
window.addEventListener('focus', handleFocus);
|
||||||
|
|||||||
Reference in New Issue
Block a user