Phase 1 fixes
This commit is contained in:
@@ -10,6 +10,7 @@ import LoadingStateMonitor from "./components/common/LoadingStateMonitor";
|
|||||||
import { PageProvider } from "./context/PageContext";
|
import { PageProvider } from "./context/PageContext";
|
||||||
import { useAuthStore } from "./store/authStore";
|
import { useAuthStore } from "./store/authStore";
|
||||||
import { useModuleStore } from "./store/moduleStore";
|
import { useModuleStore } from "./store/moduleStore";
|
||||||
|
import SuspenseLoader from "./components/common/SuspenseLoader";
|
||||||
|
|
||||||
// Auth pages - loaded immediately (needed for login)
|
// Auth pages - loaded immediately (needed for login)
|
||||||
import SignIn from "./pages/AuthPages/SignIn";
|
import SignIn from "./pages/AuthPages/SignIn";
|
||||||
@@ -131,8 +132,7 @@ export default function App() {
|
|||||||
<LoadingStateMonitor />
|
<LoadingStateMonitor />
|
||||||
<HelmetProvider>
|
<HelmetProvider>
|
||||||
<ScrollToTop />
|
<ScrollToTop />
|
||||||
{/* CRITICAL FIX: Move Suspense OUTSIDE Routes to prevent Router context loss during HMR */}
|
<Suspense fallback={<SuspenseLoader />}>
|
||||||
<Suspense fallback={<div className="flex items-center justify-center min-h-screen"><div className="text-lg">Loading...</div></div>}>
|
|
||||||
<Routes>
|
<Routes>
|
||||||
{/* Auth Routes - Public */}
|
{/* Auth Routes - Public */}
|
||||||
<Route path="/signin" element={<SignIn />} />
|
<Route path="/signin" element={<SignIn />} />
|
||||||
|
|||||||
16
frontend/src/components/common/SuspenseLoader.tsx
Normal file
16
frontend/src/components/common/SuspenseLoader.tsx
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/**
|
||||||
|
* Suspense Loader - Standalone loading component for React Suspense fallback
|
||||||
|
* No context dependencies - can be used anywhere
|
||||||
|
*/
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
const SuspenseLoader: React.FC = () => {
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center justify-center min-h-screen gap-4">
|
||||||
|
<div className="h-10 w-10 border-4 border-gray-200 border-t-brand-500 rounded-full animate-spin" />
|
||||||
|
<p className="text-gray-600 dark:text-gray-400 text-sm">Loading page...</p>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SuspenseLoader;
|
||||||
@@ -11,8 +11,7 @@ import { useHeaderMetrics } from "../context/HeaderMetricsContext";
|
|||||||
import { useErrorHandler } from "../hooks/useErrorHandler";
|
import { useErrorHandler } from "../hooks/useErrorHandler";
|
||||||
import { trackLoading } from "../components/common/LoadingStateMonitor";
|
import { trackLoading } from "../components/common/LoadingStateMonitor";
|
||||||
import PendingPaymentBanner from "../components/billing/PendingPaymentBanner";
|
import PendingPaymentBanner from "../components/billing/PendingPaymentBanner";
|
||||||
import { PageLoadingProvider, usePageLoadingContext } from "../context/PageLoadingContext";
|
import { PageLoadingProvider } from "../context/PageLoadingContext";
|
||||||
import PageLoader from "../components/common/PageLoader";
|
|
||||||
|
|
||||||
const LayoutContent: React.FC = () => {
|
const LayoutContent: React.FC = () => {
|
||||||
const { isExpanded, isHovered, isMobileOpen } = useSidebar();
|
const { isExpanded, isHovered, isMobileOpen } = useSidebar();
|
||||||
@@ -178,14 +177,8 @@ const LayoutContent: React.FC = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Wrapper component to conditionally render Outlet or PageLoader
|
// Wrapper component - just renders children (global loading removed to avoid unmounting issues)
|
||||||
const PageLoaderWrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
const PageLoaderWrapper: React.FC<{ children: React.ReactNode }> = ({ children }) => {
|
||||||
const { isLoading } = usePageLoadingContext();
|
|
||||||
|
|
||||||
if (isLoading) {
|
|
||||||
return <PageLoader />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <>{children}</>;
|
return <>{children}</>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -372,14 +372,6 @@ const AutomationPage: React.FC = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return (
|
|
||||||
<div className="flex items-center justify-center min-h-[60vh]">
|
|
||||||
<div className="text-lg text-gray-600 dark:text-gray-400">Loading automation...</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!activeSite) {
|
if (!activeSite) {
|
||||||
return (
|
return (
|
||||||
<div className="flex items-center justify-center min-h-[60vh]">
|
<div className="flex items-center justify-center min-h-[60vh]">
|
||||||
|
|||||||
@@ -184,12 +184,6 @@ export default function OptimizerContentSelector() {
|
|||||||
{/* Filters */}
|
{/* Filters */}
|
||||||
<ContentFilter onFilterChange={setFilters} />
|
<ContentFilter onFilterChange={setFilters} />
|
||||||
|
|
||||||
{loading ? (
|
|
||||||
<div className="text-center py-12">
|
|
||||||
<div className="inline-block animate-spin rounded-full h-8 w-8 border-b-2 border-brand-500"></div>
|
|
||||||
<p className="mt-2 text-gray-600 dark:text-gray-400">Loading content...</p>
|
|
||||||
</div>
|
|
||||||
) : (
|
|
||||||
<div className="bg-white dark:bg-gray-800 rounded-lg shadow overflow-hidden">
|
<div className="bg-white dark:bg-gray-800 rounded-lg shadow overflow-hidden">
|
||||||
<div className="overflow-x-auto">
|
<div className="overflow-x-auto">
|
||||||
<table className="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
<table className="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
|
||||||
@@ -300,7 +294,6 @@ export default function OptimizerContentSelector() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
|
||||||
|
|
||||||
{/* Module footer placeholder - module on hold */}
|
{/* Module footer placeholder - module on hold */}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -119,17 +119,6 @@ export default function ClusterDetail() {
|
|||||||
setActiveTab(tab);
|
setActiveTab(tab);
|
||||||
};
|
};
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<PageMeta title="Cluster Details" description="Loading cluster information" />
|
|
||||||
<div className="flex items-center justify-center h-64">
|
|
||||||
<div className="text-gray-500">Loading cluster...</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!cluster) {
|
if (!cluster) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -392,17 +392,6 @@ export default function ContentCalendar() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<PageMeta title="Content Calendar" description="View and manage scheduled content" />
|
|
||||||
<div className="flex items-center justify-center h-64">
|
|
||||||
<div className="text-gray-500">Loading calendar...</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageMeta title="Content Calendar" description="View and manage scheduled content" />
|
<PageMeta title="Content Calendar" description="View and manage scheduled content" />
|
||||||
|
|||||||
@@ -64,18 +64,6 @@ const CreditsAndBilling: React.FC = () => {
|
|||||||
).join(' ');
|
).join(' ');
|
||||||
};
|
};
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<PageMeta title="Usage & Billing" description="View your usage and billing" />
|
|
||||||
<div className="text-center py-12">
|
|
||||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-primary-600 mx-auto"></div>
|
|
||||||
<p className="mt-4 text-gray-600 dark:text-gray-400">Loading billing data...</p>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageMeta title="Usage & Billing" description="View your usage and billing" />
|
<PageMeta title="Usage & Billing" description="View your usage and billing" />
|
||||||
|
|||||||
@@ -375,17 +375,6 @@ export default function Sites() {
|
|||||||
return industry?.sectors || [];
|
return industry?.sectors || [];
|
||||||
};
|
};
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return (
|
|
||||||
<div className="flex h-screen items-center justify-center">
|
|
||||||
<div className="text-center">
|
|
||||||
<div className="mb-4 h-8 w-8 animate-spin rounded-full border-4 border-gray-300 border-t-brand-600 mx-auto"></div>
|
|
||||||
<p className="text-gray-600 dark:text-gray-400">Loading sites...</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageMeta title="Your Websites" description="Manage all your websites here - Add new sites, configure settings, and track content for each one" />
|
<PageMeta title="Your Websites" description="Manage all your websites here - Add new sites, configure settings, and track content for each one" />
|
||||||
|
|||||||
@@ -215,11 +215,7 @@ export default function SiteContentManager() {
|
|||||||
</Card>
|
</Card>
|
||||||
|
|
||||||
{/* Content List */}
|
{/* Content List */}
|
||||||
{loading ? (
|
{filteredContent.length === 0 ? (
|
||||||
<Card className="p-12 text-center">
|
|
||||||
<div className="text-gray-500">Loading content...</div>
|
|
||||||
</Card>
|
|
||||||
) : filteredContent.length === 0 ? (
|
|
||||||
<Card className="p-12 text-center">
|
<Card className="p-12 text-center">
|
||||||
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
<p className="text-gray-600 dark:text-gray-400 mb-4">
|
||||||
No content found
|
No content found
|
||||||
|
|||||||
@@ -229,17 +229,6 @@ export default function SiteDashboard() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<PageMeta title="Site Dashboard" />
|
|
||||||
<div className="flex items-center justify-center h-64">
|
|
||||||
<div className="text-gray-500">Loading site dashboard...</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!site) {
|
if (!site) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -477,17 +477,6 @@ export default function SiteList() {
|
|||||||
|
|
||||||
const hasActiveFilters = searchTerm || siteTypeFilter || hostingTypeFilter || statusFilter || integrationFilter;
|
const hasActiveFilters = searchTerm || siteTypeFilter || hostingTypeFilter || statusFilter || integrationFilter;
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<PageMeta title="Site List" />
|
|
||||||
<div className="flex items-center justify-center h-64">
|
|
||||||
<div className="text-gray-500">Loading sites...</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageMeta title="Your Websites - IGNY8" />
|
<PageMeta title="Your Websites - IGNY8" />
|
||||||
|
|||||||
@@ -264,17 +264,6 @@ export default function PageManager() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<PageMeta title="Page Manager" />
|
|
||||||
<div className="flex items-center justify-center h-64">
|
|
||||||
<div className="text-gray-500">Loading pages...</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageMeta title="Page Manager - IGNY8" />
|
<PageMeta title="Page Manager - IGNY8" />
|
||||||
|
|||||||
@@ -216,17 +216,6 @@ export default function PostEditor() {
|
|||||||
{ value: 'published', label: 'Published' },
|
{ value: 'published', label: 'Published' },
|
||||||
];
|
];
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<PageMeta title="Post Editor" />
|
|
||||||
<div className="flex items-center justify-center h-64">
|
|
||||||
<div className="text-gray-500">Loading post...</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageMeta title={content.id ? 'Edit Post' : 'New Post'} />
|
<PageMeta title={content.id ? 'Edit Post' : 'New Post'} />
|
||||||
|
|||||||
@@ -206,17 +206,6 @@ export default function PublishingQueue() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<PageMeta title="Publishing Queue" description="View and manage scheduled content" />
|
|
||||||
<div className="flex items-center justify-center h-64">
|
|
||||||
<div className="text-gray-500">Loading queue...</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageMeta title="Publishing Queue" description="View and manage scheduled content" />
|
<PageMeta title="Publishing Queue" description="View and manage scheduled content" />
|
||||||
|
|||||||
@@ -667,17 +667,6 @@ export default function SiteSettings() {
|
|||||||
{ value: 'multi', label: 'Multi-Destination' },
|
{ value: 'multi', label: 'Multi-Destination' },
|
||||||
];
|
];
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<PageMeta title="Site Settings" />
|
|
||||||
<div className="flex items-center justify-center h-64">
|
|
||||||
<div className="text-gray-500">Loading site settings...</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageMeta title="Site Settings - IGNY8" />
|
<PageMeta title="Site Settings - IGNY8" />
|
||||||
|
|||||||
@@ -116,17 +116,6 @@ export default function SyncDashboard() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<PageMeta title="Sync Dashboard" />
|
|
||||||
<div className="flex items-center justify-center h-64">
|
|
||||||
<div className="text-gray-500">Loading sync data...</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!syncStatus) {
|
if (!syncStatus) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|||||||
@@ -184,20 +184,6 @@ export default function Prompts() {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<PageMeta title="Prompts - IGNY8" description="AI prompts management" />
|
|
||||||
<div className="flex items-center justify-center min-h-screen">
|
|
||||||
<div className="text-center">
|
|
||||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-primary-500 mx-auto"></div>
|
|
||||||
<p className="mt-4 text-gray-600 dark:text-gray-400">Loading prompts...</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<PageMeta title="Prompt Library - IGNY8" description="Manage your AI writing prompts" />
|
<PageMeta title="Prompt Library - IGNY8" description="Manage your AI writing prompts" />
|
||||||
|
|||||||
@@ -832,7 +832,6 @@ export default function AccountSettingsPage() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Modal>
|
</Modal>
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -311,20 +311,6 @@ export default function ContentSettingsPage() {
|
|||||||
images: 'Image Settings',
|
images: 'Image Settings',
|
||||||
};
|
};
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return (
|
|
||||||
<div className="p-6">
|
|
||||||
<PageMeta title="Content Settings" description="Configure your content generation settings" />
|
|
||||||
<div className="flex items-center justify-center h-64">
|
|
||||||
<div className="flex flex-col items-center gap-3">
|
|
||||||
<Loader2Icon className="w-8 h-8 animate-spin text-[var(--color-brand-500)]" />
|
|
||||||
<div className="text-gray-500 dark:text-gray-400">Loading settings...</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="p-6">
|
<div className="p-6">
|
||||||
<PageMeta title="Content Settings" description="Configure your content generation settings" />
|
<PageMeta title="Content Settings" description="Configure your content generation settings" />
|
||||||
|
|||||||
@@ -145,23 +145,6 @@ export default function PurchaseCreditsPage() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<PageMeta title="Purchase Credits" description="Top up your account with credit packages" />
|
|
||||||
<PageHeader
|
|
||||||
title="Purchase Credits"
|
|
||||||
badge={{ icon: <ZapIcon className="w-4 h-4" />, color: 'blue' }}
|
|
||||||
/>
|
|
||||||
<div className="p-6">
|
|
||||||
<div className="flex items-center justify-center h-64">
|
|
||||||
<Loader2Icon className="w-8 h-8 animate-spin text-[var(--color-brand-500)]" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showManualPaymentForm && invoiceData) {
|
if (showManualPaymentForm && invoiceData) {
|
||||||
const selectedMethod = paymentMethods.find((m) => m.type === selectedPaymentMethod);
|
const selectedMethod = paymentMethods.find((m) => m.type === selectedPaymentMethod);
|
||||||
|
|
||||||
|
|||||||
@@ -56,26 +56,6 @@ export default function UsageAnalyticsPage() {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if (loading) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<PageMeta title="Usage & Analytics" description="Monitor your plan limits and usage" />
|
|
||||||
<PageHeader
|
|
||||||
title="Usage & Analytics"
|
|
||||||
badge={{ icon: <TrendingUpIcon className="w-4 h-4" />, color: 'blue' }}
|
|
||||||
/>
|
|
||||||
<div className="p-6">
|
|
||||||
<div className="flex items-center justify-center h-64">
|
|
||||||
<div className="flex flex-col items-center gap-3">
|
|
||||||
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-brand-500"></div>
|
|
||||||
<div className="text-gray-500 dark:text-gray-400">Loading usage data...</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const tabTitles: Record<TabType, string> = {
|
const tabTitles: Record<TabType, string> = {
|
||||||
limits: 'Limits & Usage',
|
limits: 'Limits & Usage',
|
||||||
activity: 'Credit History',
|
activity: 'Credit History',
|
||||||
@@ -280,7 +260,6 @@ export default function UsageAnalyticsPage() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user