import * as React from "react"; import { Link, useLocation } from "@tanstack/react-router"; import { ChevronsUpDown, CircleHelp, CreditCard, Menu, User, } from "lucide-react"; import { AppContent, MissingSeoSetupModal, SeoApiStatusBanners, } from "@/client/layout/AppShellParts"; import { ThemePreferenceMenuItems } from "@/client/components/ThemePreferenceMenuItems"; import { getProjectNavItems } from "@/client/navigation/items"; import { signOutAndRedirect, useSession } from "@/lib/auth-client"; import { isHostedClientAuthMode } from "@/lib/auth-mode"; import { BILLING_ROUTE } from "@/shared/billing"; import { getSeoApiKeyStatus } from "@/serverFunctions/config"; const DATAFORSEO_HELP_PATH = "/help/dataforseo-api-key"; const SUPPORT_PATH = "/support"; export function AuthenticatedAppLayout({ children, projectId, banner, }: { children: React.ReactNode; projectId?: string; banner?: React.ReactNode; }) { const location = useLocation(); const [drawerOpen, setDrawerOpen] = React.useState(false); const setupModalRef = React.useRef(null); const [isSeoApiKeyConfigured, setIsSeoApiKeyConfigured] = React.useState< boolean | null >(null); const [seoApiKeyStatusError, setSeoApiKeyStatusError] = React.useState(false); const [showMissingSeoApiKeyModal, setShowMissingSeoApiKeyModal] = React.useState(false); React.useEffect(() => { if (location.pathname === BILLING_ROUTE) { setSeoApiKeyStatusError(false); setIsSeoApiKeyConfigured(null); setShowMissingSeoApiKeyModal(false); return; } let cancelled = false; const checkSeoApiKeyStatus = async () => { try { const result = await getSeoApiKeyStatus(); if (cancelled) return; setSeoApiKeyStatusError(false); setIsSeoApiKeyConfigured(result.configured); if (!result.configured) { setShowMissingSeoApiKeyModal(true); } } catch { if (cancelled) return; setSeoApiKeyStatusError(true); setIsSeoApiKeyConfigured(null); setShowMissingSeoApiKeyModal(false); } }; void checkSeoApiKeyStatus(); return () => { cancelled = true; }; }, [location.pathname]); const shouldShowMissingSeoApiKeyModal = showMissingSeoApiKeyModal && location.pathname !== DATAFORSEO_HELP_PATH; const shouldShowSeoApiWarning = !seoApiKeyStatusError && isSeoApiKeyConfigured === false && !shouldShowMissingSeoApiKeyModal; React.useEffect(() => { if (!shouldShowMissingSeoApiKeyModal) return; setupModalRef.current?.focus(); const onKeyDown = (event: KeyboardEvent) => { if (event.key === "Escape") { setShowMissingSeoApiKeyModal(false); } }; window.addEventListener("keydown", onKeyDown); return () => { window.removeEventListener("keydown", onKeyDown); }; }, [shouldShowMissingSeoApiKeyModal]); React.useEffect(() => { if (!projectId) { setDrawerOpen(false); } }, [projectId]); return (
setDrawerOpen(true)} /> {banner} setDrawerOpen(false)} > {children} setShowMissingSeoApiKeyModal(false)} />
); } function TopNav({ drawerOpen, projectId, pathname, onOpenDrawer, }: { drawerOpen: boolean; projectId: string | null; pathname: string; onOpenDrawer: () => void; }) { const projectNavItems = projectId ? getProjectNavItems(projectId) : []; const isSupportActive = pathname === SUPPORT_PATH; return (
{projectId ? ( ) : null} OpenSEO
OpenSEO {projectId ? projectNavItems.map((item) => { const { icon: Icon, matchSegment, ...linkProps } = item; const isActive = pathname.includes(matchSegment); return ( {item.label} ); }) : null}
); } function AccountMenu({ mobileOnly = false }: { mobileOnly?: boolean }) { const { data: session } = useSession(); const isHostedMode = isHostedClientAuthMode(); const email = session?.user?.email; const handleSignOut = () => signOutAndRedirect(); const menu = (
    {email ? (
  • {email}
  • ) : null} {mobileOnly ? (
  • Help & Community
  • ) : null} {isHostedMode ? (
  • Billing
  • ) : null} {isHostedMode && email ? (
  • ) : null}
); if (mobileOnly) { return menu; } return ( <>
{menu} ); }