import React, { useEffect, useRef } from 'react';
import {
HashRouter as Router,
Routes,
Route,
useLocation,
useNavigate,
} from 'react-router-dom';
import { LuMenu } from 'react-icons/lu';
import { recomaze_ai_personalization_env } from './env';
import { SidebarProvider, useSidebar } from './context/sidebar.context';
import Support from './pages/Support';
import SideBar from './components/layout/SideBar';
import Customization from './pages/Customization';
import ImportProducts from './pages/ImportProducts';
import { useAppStateContext } from './context/user.data.context';
import { isCopilotEnabled } from './service/copilot/access';
import Pricing from './pages/Pricing';
import AgenticFeedPage from './pages/AgenticFeed';
import AiVisibility from './pages/AiVisibility';
import AiVisibilityContents from './pages/AiVisibilityContents';
import AiVisibilityPageTemplates from './pages/AiVisibilityPageTemplates';
import ProductImprovements from './pages/ProductImprovements';
import LlmTxtPage from './pages/LlmTxt';
import AgentAnalytics from './pages/AgentAnalytics';
import KnowledgeBase from './pages/KnowledgeBase';
import ContentGenerator from './pages/ContentGenerator';
import ContentChecker from './pages/ContentChecker';
import OptimizedTextPages from './pages/OptimizedTextPages';
import PromptTemplatesPage from './pages/PromptTemplatesPage';
import Campaigns from './pages/Campaigns';
import CampaignCreate from './pages/CampaignCreate';
import CampaignDetail from './pages/CampaignDetail';
import CatalogQuality from './pages/CatalogQuality';
import Connectors from './pages/Connectors';
import Dashboard from './pages/Dashboard';
import CompanyId from './pages/CompanyId';
import SignupMirror from './pages/SignupMirror';
import LaunchDiscount from './pages/LaunchDiscount';
import OnboardingChain from './pages/OnboardingChain';
import { Spinner } from './components/ui/Spinner';
import { useAccessControl } from './hooks/useAccessControl';
import { signupMirrorRedirect } from './lib/signup-mirror/access';
import PreviewBanner from './components/PreviewBanner';
import { updatePreviewMode } from './service/user/user.service';
import VerificationModal from './components/VerificationModal';
import ErrorBoundary from './components/ErrorBoundary';
import { CopilotPanel } from './components/copilot/CopilotPanel';
const BARE_ROUTES = ['/signup-mirror', '/launch-discount', '/onboarding-chain'];
const MobileTopBar = (): JSX.Element => {
const { setMobileOpen } = useSidebar();
const logo = recomaze_ai_personalization_env?.plugin_icon;
return (
{/* A bare hamburger read as decoration, so the only way back to the
navigation went unnoticed. The trigger names itself instead. */}
{logo ? (
) : null}
Recomaze
);
};
// Everything that needs router hooks (useLocation / useNavigate) lives here,
// inside . This is also where the Signup Mirror resume redirect fires
// and where the full-screen onboarding routes bypass the app chrome.
const AppShell = (): JSX.Element => {
const { token, clientId, user, setUser } = useAppStateContext();
const { canAccessAllRoutes, canSeeBulkUpload } = useAccessControl(user);
const location = useLocation();
const navigate = useNavigate();
const resumedRef = useRef(false);
useEffect(() => {
if (resumedRef.current || !user) return;
if (location.pathname !== '/' && location.pathname !== '/dashboard') return;
const target = signupMirrorRedirect(user);
if (target) {
resumedRef.current = true;
navigate(target, { replace: true });
}
}, [user, location.pathname, navigate]);
const handleActivatePreview = async () => {
if (!token) return;
try {
await updatePreviewMode({ token, preview_mode: true });
if (user) setUser({ ...user, preview_mode: true });
} catch (error) {
console.error('Error enabling preview mode:', error);
}
};
const handleDeactivatePreview = async () => {
if (!token) return;
try {
await updatePreviewMode({ token, preview_mode: false });
if (user) setUser({ ...user, preview_mode: false });
} catch (error) {
console.error('Error disabling preview mode:', error);
}
};
// A fresh install lands on the app root while /me is still in flight, so
// rendering straight away painted the dashboard and only then jumped to the
// Mirror. Hold the shell until the account is known and the merchant sees
// one screen: the one they belong on.
const awaitingResumeDecision: boolean =
!!token &&
!user &&
(location.pathname === '/' || location.pathname === '/dashboard');
if (awaitingResumeDecision) {
return (
);
}
// Full-screen onboarding routes: no sidebar, no banners, no copilot. They
// still take the pinned shell, so they fill the viewport instead of leaving
// dead space under a short screen - and, since the admin stylesheet stops
// the document itself from scrolling, they carry their own scroll or a tall
// screen would be unreachable.
if (BARE_ROUTES.includes(location.pathname)) {
return (