fix(dashboard): container leaks after extension workspace navigation (#414)
* fix(dashboard): keep container in sync with route after extension navigation The shared (dashboard)/layout.tsx picked between the centered max-w-5xl chrome and the full-width extension wrapper based on a server-side pathname header. App Router caches shared layouts across sibling-route navigations, so whichever branch was rendered on the first server pass stuck on subsequent client navigations until a hard reload. In practice this only mattered because /e/* is the first route family that opts out of the centered card — visiting the invoice-inbox workspace and then clicking back to /, /transactions, etc. left the unconstrained h-full wrapper in place, and the dashboard rendered edge-to-edge. Move the conditional into a small client component that subscribes to usePathname(). The hook re-runs on every navigation, so the wrapper className always tracks the current route. The layout still reads x-pathname for the isNoCompanyAllowed redirect check; that path only fires on the initial server render and isn't subject to the soft-navigation issue. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(dashboard): drop redundant nullish guards on usePathname Per Greptile review on #414. usePathname() in App Router always returns a string, so the optional chaining and ?? false were dead. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
9e1e13d388
commit
8aff6dc684
@@ -2,6 +2,7 @@ import { createClient } from '@/lib/supabase/server'
|
||||
import { redirect } from 'next/navigation'
|
||||
import { headers } from 'next/headers'
|
||||
import DashboardNav from '@/components/dashboard/DashboardNav'
|
||||
import { MainContainer } from '@/components/dashboard/MainContainer'
|
||||
import CompanyTabSync from '@/components/dashboard/CompanyTabSync'
|
||||
import { RecaptIdentify } from '@/components/RecaptIdentify'
|
||||
import { SandboxBanner } from '@/components/dashboard/SandboxBanner'
|
||||
@@ -222,16 +223,7 @@ export default async function DashboardLayout({
|
||||
extensionNavItems={getExtensionNavItems()}
|
||||
/>
|
||||
<main id="main-content" className="safe-area-main-padding md:!pb-0 md:pl-[232px]" role="main">
|
||||
{pathname.startsWith('/e/') ? (
|
||||
// Extension workspaces opt out of the centered max-w-5xl chrome
|
||||
// because their content (file viewers, dashboards) wants the full
|
||||
// viewport width.
|
||||
<div key={companyId} className="h-full">{children}</div>
|
||||
) : (
|
||||
<div key={companyId} className="max-w-5xl mx-auto px-5 py-8 md:px-8 md:py-10">
|
||||
{children}
|
||||
</div>
|
||||
)}
|
||||
<MainContainer companyId={companyId}>{children}</MainContainer>
|
||||
</main>
|
||||
{!isSandbox && (
|
||||
<RecaptIdentify
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
'use client'
|
||||
|
||||
import { usePathname } from 'next/navigation'
|
||||
import type { ReactNode } from 'react'
|
||||
|
||||
/**
|
||||
* Picks the dashboard chrome container based on route. Extension workspaces
|
||||
* (/e/*) want the full viewport for file viewers and dashboards; everything
|
||||
* else gets the centered max-w-5xl card.
|
||||
*
|
||||
* Lives in a client component because the parent (dashboard) layout is
|
||||
* shared across all dashboard routes. Server-side pathname checks done in
|
||||
* the layout don't re-evaluate reliably on soft navigation between sibling
|
||||
* routes, so the wrapper class would otherwise stick on whichever branch
|
||||
* the first render picked.
|
||||
*/
|
||||
export function MainContainer({
|
||||
companyId,
|
||||
children,
|
||||
}: {
|
||||
companyId: string | null
|
||||
children: ReactNode
|
||||
}) {
|
||||
const pathname = usePathname()
|
||||
const isExtensionWorkspace = pathname.startsWith('/e/')
|
||||
|
||||
return isExtensionWorkspace ? (
|
||||
<div key={companyId ?? ''} className="h-full">{children}</div>
|
||||
) : (
|
||||
<div
|
||||
key={companyId ?? ''}
|
||||
className="max-w-5xl mx-auto px-5 py-8 md:px-8 md:py-10"
|
||||
>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user