The sidebar now uses next/dynamic with ssr:false, meaning it renders NOTHING in the server HTML. No DOM content = no possible flash of "Ci" or any text during SSR-to-hydration gap. The sidebar only mounts on the client where localStorage is immediately available, so collapsed state is correct from the very first render.
38 lines
1.0 KiB
TypeScript
38 lines
1.0 KiB
TypeScript
'use client'
|
|
|
|
import { useState, useCallback } from 'react'
|
|
import dynamic from 'next/dynamic'
|
|
import ContentHeader from './ContentHeader'
|
|
|
|
// Load sidebar only on the client — prevents any SSR flash of text/labels
|
|
const Sidebar = dynamic(() => import('./Sidebar'), { ssr: false })
|
|
|
|
export default function DashboardShell({
|
|
siteId,
|
|
children,
|
|
}: {
|
|
siteId: string
|
|
children: React.ReactNode
|
|
}) {
|
|
const [mobileOpen, setMobileOpen] = useState(false)
|
|
const closeMobile = useCallback(() => setMobileOpen(false), [])
|
|
const openMobile = useCallback(() => setMobileOpen(true), [])
|
|
|
|
return (
|
|
<div className="flex h-screen overflow-hidden">
|
|
<Sidebar
|
|
siteId={siteId}
|
|
mobileOpen={mobileOpen}
|
|
onMobileClose={closeMobile}
|
|
onMobileOpen={openMobile}
|
|
/>
|
|
<div className="flex-1 flex flex-col min-w-0 overflow-hidden">
|
|
<ContentHeader onMobileMenuOpen={openMobile} />
|
|
<main className="flex-1 overflow-y-auto pt-6">
|
|
{children}
|
|
</main>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|