'use client' import { useState, useCallback, useEffect, useRef } from 'react' import dynamic from 'next/dynamic' import Link from 'next/link' import { usePathname } from 'next/navigation' import { formatUpdatedAgo } from '@ciphera-net/ui' import { CaretRight, SidebarSimple } from '@phosphor-icons/react' import { SidebarProvider, useSidebar } from '@/lib/sidebar-context' import { useRealtime } from '@/lib/swr/dashboard' import { getSite } from '@/lib/api/sites' import ContentHeader from './ContentHeader' const PAGE_TITLES: Record = { '': 'Dashboard', journeys: 'Journeys', funnels: 'Funnels', behavior: 'Behavior', search: 'Search', cdn: 'CDN', uptime: 'Uptime', pagespeed: 'PageSpeed', settings: 'Site Settings', } function usePageTitle() { const pathname = usePathname() // pathname is /sites/:id or /sites/:id/section/... const segment = pathname.replace(/^\/sites\/[^/]+\/?/, '').split('/')[0] return PAGE_TITLES[segment] ?? (segment ? segment.charAt(0).toUpperCase() + segment.slice(1) : 'Dashboard') } const HOME_PAGE_TITLES: Record = { '': 'Your Sites', integrations: 'Integrations', pricing: 'Pricing', } function useHomePageTitle() { const pathname = usePathname() const segment = pathname.split('/').filter(Boolean)[0] ?? '' return HOME_PAGE_TITLES[segment] ?? (segment ? segment.charAt(0).toUpperCase() + segment.slice(1) : 'Your Sites') } // Load sidebar only on the client — prevents SSR flash const Sidebar = dynamic(() => import('./Sidebar'), { ssr: false, loading: () => (
), }) function GlassTopBar({ siteId }: { siteId: string | null }) { const { collapsed, toggle } = useSidebar() const { data: realtime } = useRealtime(siteId ?? '') const lastUpdatedRef = useRef(null) const [, setTick] = useState(0) const [siteName, setSiteName] = useState(null) useEffect(() => { if (siteId && realtime) lastUpdatedRef.current = Date.now() }, [siteId, realtime]) useEffect(() => { if (lastUpdatedRef.current == null) return const timer = setInterval(() => setTick((t) => t + 1), 1000) return () => clearInterval(timer) }, [realtime]) useEffect(() => { if (!siteId) { setSiteName(null); return } getSite(siteId).then((s) => setSiteName(s.name)).catch(() => {}) }, [siteId]) const dashboardTitle = usePageTitle() const homeTitle = useHomePageTitle() const pageTitle = siteId ? dashboardTitle : homeTitle return (
{/* Left: collapse toggle + breadcrumbs */}
{siteId && siteName ? ( ) : ( {pageTitle} )}
{/* Realtime indicator */} {siteId && lastUpdatedRef.current != null && (
Live · {formatUpdatedAgo(lastUpdatedRef.current)}
)}
) } export default function DashboardShell({ siteId, children, }: { siteId: string | null children: React.ReactNode }) { const [mobileOpen, setMobileOpen] = useState(false) const closeMobile = useCallback(() => setMobileOpen(false), []) const openMobile = useCallback(() => setMobileOpen(true), []) return (
{/* Glass top bar — above content only, collapse icon reaches back into sidebar column */} {/* Content panel */}
{children}
) }