update ui update and fix layout issue

This commit is contained in:
2026-08-06 13:21:10 +05:30
parent 92322bff16
commit e5f8144fb3
251 changed files with 9041 additions and 2119 deletions

View File

@@ -1,160 +1,16 @@
'use client';
import {AppShell} from '@astryxdesign/core/AppShell';
import {
Layout,
LayoutContent,
LayoutHeader,
LayoutPanel,
VStack,
} from '@astryxdesign/core/Layout';
import {AppSideNav} from '@/components/shell/AppSideNav';
import {AppTopNav} from '@/components/shell/AppTopNav';
import {MobileMenu} from '@/components/shell/MobileMenu';
import {SidebarProvider, useSidebar} from '@/components/shell/SidebarProvider';
import {CopilotPanel} from '@/features/copilot/CopilotPanel';
import {CopilotSlideOver} from '@/features/copilot/CopilotSlideOver';
import {useCopilot} from '@/features/copilot/CopilotProvider';
import {
useBreakpoint,
isPanelInline,
isSideNavInline,
copilotWidth,
contentMaxWidth,
} from '@/lib/breakpoints';
import {ProtectedLayout} from '@/shared/layouts/ProtectedLayout';
/**
* The three-column shell, and the reason the Copilot survives navigation.
* The route-group boundary for every authenticated screen.
*
* Next's App Router preserves a layout's element identity across sibling route
* changes, so /dashboard → /staff swaps only {children}. Everything else here —
* nav, scroll position, the Copilot — is untouched.
*
* Structure, and why AppShell alone is not enough: AppShell renders
* Layout{header, start, content} internally and exposes no end/panel slot. The
* Copilot column therefore comes from a NESTED Layout inside its children.
*
* AppShell(sideNav)
* └─ Layout(header = TopNav, content = page, end = LayoutPanel > Copilot)
*
* The top nav is deliberately NOT passed to AppShell's `topNav` slot. That slot
* renders Layout{header} at the shell root, which spans the full viewport width
* and pushes the sidebar — and therefore the branding — 48px down the screen.
* Measured in the browser, the logo sat at y=64 with the top-left 260x48 region
* empty. Moving the bar into the content column's own header starts the sidebar
* at y=0, so the brand anchors in the window's top-left corner and the bar
* begins where the content does. This is the arrangement Linear, Cursor and
* Stripe use, and no amount of padding inside the sidebar could produce it.
*
* The one thing given up: with no `topNav`, AppShell drops the --radius-page
* corner it draws where the top bar meets the sidebar. The content area still
* paints elevated --color-background-surface (#0F0F10) against the #000 rails,
* and with a full-height sidebar that corner had nothing left to round.
* Deliberately thin: the composition lives in ProtectedLayout, so this file
* never needs editing again and the shell can be reused (tests, a future
* embedded view) without a route existing for it.
*/
export default function WorkspaceLayout({
export default function WorkspaceRouteLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<SidebarProvider>
<WorkspaceShell>{children}</WorkspaceShell>
</SidebarProvider>
);
}
/**
* Split out from the layout only so it can READ the sidebar context that the
* layout mounts — a provider's own component sits above its value.
*/
function WorkspaceShell({children}: {children: React.ReactNode}) {
const bp = useBreakpoint();
const {isOpen} = useCopilot();
const inline = isPanelInline(bp);
const showInlinePanel = inline && isOpen;
// Both flags come from SidebarProvider now. AppShell keeps a copy of the
// open state for its own context (MobileNav reads it for aria wiring), but
// ours is the one that decides — uncontrolled, there would be no way to
// dismiss the drawer when a nav item is selected.
const {isDrawerOpen, setDrawerOpen} = useSidebar();
// Below the breakpoint the rail is gone — it lives in the drawer — so there
// is no top-left corner to anchor the brand to and nothing for the bar to
// start after. Hand the top nav back to AppShell there. Keeping it in the
// content column instead pushed the SideNav into AppShell's
// `autoMobileTopBar` fallback, which crops the lockup into a 48px bar.
const sideNavInline = isSideNavInline(bp);
return (
<AppShell
variant="elevated"
height="fill"
// Layout/LayoutContent own the padding so the Copilot panel can sit
// flush against the workspace edge.
contentPadding={0}
topNav={sideNavInline ? undefined : <AppTopNav />}
sideNav={<AppSideNav />}
// 'sm' (640px) matches the mobile/tablet line in lib/breakpoints —
// AppShell's own threshold and ours have to agree or the hamburger and
// the rail are both visible, or neither is.
mobileNav={{
breakpoint: 'sm',
isOpen: isDrawerOpen,
onOpenChange: setDrawerOpen,
// hasToggle: false — AppShell would otherwise inject a SECOND
// hamburger into TopNav's mobile bar, next to the one in `heading`
// that works at every width. See AppTopNav.
hasToggle: false,
content: <MobileMenu />,
}}
>
<Layout
height="fill"
header={
sideNavInline ? (
<LayoutHeader padding={0}>
<AppTopNav />
</LayoutHeader>
) : undefined
}
content={
<LayoutContent padding={5}>
{/*
Above 1920 the workspace is capped and centred. Uncapped, a
30-day line chart stretches across ~1900px, which flattens every
trend it exists to show, and body copy runs well past the ~90ch
where reading breaks down. Below the cap this is a no-op —
maxWidth only binds once there is more width than content wants.
*/}
<VStack
width="100%"
maxWidth={contentMaxWidth(bp)}
className={bp === 'ultrawide' ? 'mx-auto' : undefined}
>
{children}
</VStack>
</LayoutContent>
}
end={
showInlinePanel ? (
<LayoutPanel
hasDivider
width={copilotWidth(bp)}
// CopilotPanel supplies its own header and tab chrome.
padding={0}
role="complementary"
label="Loyaly AI"
>
<CopilotPanel />
</LayoutPanel>
) : undefined
}
/>
{/* Mounted only below the inline breakpoint. Because every piece of
Copilot state lives in CopilotProvider (above the route tree), moving
between these two presentations is lossless. */}
{!inline ? <CopilotSlideOver /> : null}
</AppShell>
);
return <ProtectedLayout>{children}</ProtectedLayout>;
}