'use client'
import { useEffect, useState } from 'react'
import { NavbarTabRail } from '@/components/blocks/navbar-tab-rail'
const SECTIONS = [
{
label: 'Catalog',
href: '#catalog',
items: [
{ label: 'Overview', href: '#catalog-overview' },
{ label: 'Collections', href: '#catalog-collections' },
{ label: 'Inventory', href: '#catalog-inventory' },
{ label: 'Price lists', href: '#catalog-prices' },
],
},
{
label: 'Orders',
href: '#orders',
items: [
{ label: 'Open', href: '#orders-open' },
{ label: 'Fulfilled', href: '#orders-fulfilled' },
{ label: 'Returns', href: '#orders-returns' },
{ label: 'Invoices', href: '#orders-invoices' },
],
},
{
label: 'Customers',
href: '#customers',
items: [
{ label: 'Directory', href: '#customers-directory' },
{ label: 'Segments', href: '#customers-segments' },
{ label: 'Loyalty', href: '#customers-loyalty' },
],
},
{
label: 'Reports',
href: '#reports',
items: [
{ label: 'Revenue', href: '#reports-revenue' },
{ label: 'Velocity', href: '#reports-velocity' },
{ label: 'Channels', href: '#reports-channels' },
{ label: 'Exports', href: '#reports-exports' },
],
},
{
label: 'Settings',
href: '#settings',
// No items, rail collapses
},
]
const COPY: Record<string, { title: string; body: string }> = {
'#catalog-overview': {
title: 'Catalog overview',
body: '1,842 SKUs across 14 collections. 38 need photography refresh before the fall lookbook ships.',
},
'#catalog-collections': {
title: 'Collections',
body: 'Walnut Desk System leads units this quarter. Brass Hardware Kit is 12 days from stockout at current velocity.',
},
'#catalog-inventory': {
title: 'Inventory positions',
body: 'Oakland DC holds 64% of units. Transfer 40 desk legs to Denver before the trade-show allocation freezes.',
},
'#catalog-prices': {
title: 'Price lists',
body: 'Trade A is live through September. MSRP sheet v4.2 includes the new matte nickel finish family.',
},
'#orders-open': {
title: 'Open orders',
body: '27 open, 6 awaiting payment confirmation. Largest: Meridian Studio, 48 desk legs, white-glove, dock code MS-441.',
},
'#orders-fulfilled': {
title: 'Fulfilled this week',
body: '119 shipments, average transit 2.4 days. Two white-glove reschedules on the East Bay run.',
},
'#orders-returns': {
title: 'Returns queue',
body: 'Four open RMAs. Two are finish mismatches, photography brief already filed with studio ops.',
},
'#orders-invoices': {
title: 'Invoices',
body: 'Net-30 aging: $48.2k under 15 days, $6.1k at 30+. Atlas Cloud paid early again.',
},
'#customers-directory': {
title: 'Customer directory',
body: '612 active accounts. 41 new this month, mostly interior studios under 20 seats.',
},
'#customers-segments': {
title: 'Segments',
body: 'Trade A accounts drive 58% of revenue. Residential gift buyers peak Fridays after lookbook emails.',
},
'#customers-loyalty': {
title: 'Loyalty ledger',
body: 'Studio credits expire end of quarter. Top earner: Field & Form, 14,200 points, one free freight voucher.',
},
'#reports-revenue': {
title: 'Revenue',
body: 'MTD $412k, +9% vs prior. Desk systems alone cover 44% of the line.',
},
'#reports-velocity': {
title: 'Sell-through velocity',
body: 'Brass hardware moved 2.1× faster after the material sample pack launched.',
},
'#reports-channels': {
title: 'Channels',
body: 'Wholesale 61%, direct 28%, marketplace 11%. Marketplace returns run 1.8× wholesale.',
},
'#reports-exports': {
title: 'Exports',
body: 'CSV and JSON available nightly. Warehouse EDI drops at 02:00 PT to Oakland and Denver.',
},
'#settings': {
title: 'Workspace settings',
body: 'Team seats, tax profiles, and dock appointment defaults. No sub-rail, this section stands alone.',
},
}
export default function NavbarTabRailDemo() {
const [activeSection, setActiveSection] = useState('#catalog')
const [activeItem, setActiveItem] = useState('#catalog-overview')
const section = SECTIONS.find((s) => s.href === activeSection)
const pageKey =
section?.items && section.items.length > 0 ? activeItem : activeSection
const page = COPY[pageKey] ?? {
title: section?.label ?? 'Workspace',
body: 'Select a section or sub-link in the rail above.',
}
// Demo-only: keep the rail keyline in sync without a real router
useEffect(() => {
const onClick = (e: MouseEvent) => {
const anchor = (e.target as HTMLElement).closest('a')
if (!anchor) return
const href = anchor.getAttribute('href')
if (!href || !href.startsWith('#')) return
if (href === '#invite') return
e.preventDefault()
const matchedSection = SECTIONS.find((s) => s.href === href)
if (matchedSection) {
setActiveSection(matchedSection.href)
const first = matchedSection.items?.[0]?.href
if (first) setActiveItem(first)
return
}
for (const s of SECTIONS) {
const item = s.items?.find((i) => i.href === href)
if (item) {
setActiveSection(s.href)
setActiveItem(item.href)
return
}
}
}
document.addEventListener('click', onClick)
return () => document.removeEventListener('click', onClick)
}, [])
return (
<div className="min-h-[640px] bg-background">
<NavbarTabRail
brand={
<span className="inline-flex items-baseline gap-1.5">
Northline
<span className="font-mono text-[10px] uppercase tracking-[0.2em] text-muted-foreground">
ops
</span>
</span>
}
sections={SECTIONS}
activeSection={activeSection}
activeItem={activeItem}
onSectionChange={(href) => {
setActiveSection(href)
const next = SECTIONS.find((s) => s.href === href)
const first = next?.items?.[0]?.href
if (first) setActiveItem(first)
}}
cta={{ label: 'Invite teammate', href: '#invite' }}
/>
<div className="mx-auto max-w-3xl px-4 py-10 sm:px-6">
<p className="font-mono text-[11px] uppercase tracking-[0.2em] text-muted-foreground sm:text-xs">
{section?.label ?? 'Workspace'}
{section?.items?.some((i) => i.href === activeItem)
? ` · ${section.items.find((i) => i.href === activeItem)?.label}`
: ''}
</p>
<h1 className="mt-3 text-balance text-2xl font-semibold tracking-tight sm:text-3xl">
{page.title}
</h1>
<p className="mt-3 max-w-xl text-sm leading-relaxed text-muted-foreground sm:text-base">
{page.body}
</p>
<div className="mt-10 space-y-3">
{[
'Oakland DC · cycle count due Friday',
'Trade A price list · review before Sep 1',
'White-glove slots · 4 remaining this week',
].map((line) => (
<div
key={line}
className="rounded-2xl border border-border/60 bg-card px-5 py-4 text-sm text-muted-foreground"
>
{line}
</div>
))}
</div>
</div>
</div>
)
}