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 ? ( Recomaze ) : 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 (
} /> } /> } />
); } return (
{!canAccessAllRoutes ? ( }> ) : ( } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> : } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> } /> )}
{isCopilotEnabled(clientId) ? ( ) : null}
); }; const App = (): JSX.Element => { const { token, clientId, user, setToken, setClientId, fetchUser, setCanUseWpApi, } = useAppStateContext(); useEffect(() => { if (!token && recomaze_ai_personalization_env?.auth?.token) setToken(recomaze_ai_personalization_env?.auth?.token); if (!clientId && recomaze_ai_personalization_env?.auth?.client_id) setClientId(recomaze_ai_personalization_env?.auth?.client_id); if (!user && token) fetchUser(); if (!recomaze_ai_personalization_env?.can_use_wp_api) setCanUseWpApi(false); }, [token, clientId]); return ( ); }; export default App;