Tab bar: - Removed: Design, Launch, Grow, Insights, Settings tabs - Added: Growth, Assist, Analytics as top-level tabs - Build remains, now a full hub Build hub (/build): - Left sub-nav groups: Code (apps), Layouts (surfaces), Infrastructure (6 items) - Code section: scoped file browser per selected app - Layouts section: surface overview cards with Edit link to /design - Infrastructure section: summary panel linking to /infrastructure?tab= Growth (/growth): - Left nav: Marketing Site, Communications, Channels, Pages - Each section: description + feature item grid + feedback CTA Assist (/assist): - Left nav: Emails, Chat Support, Support Site, Communications - Each section: description + feature item grid + feedback CTA Analytics (/analytics): - Left nav: Customers, Usage, Events, Reports - Each section: description + feature item grid + feedback CTA Made-with: Cursor
126 lines
4.1 KiB
TypeScript
126 lines
4.1 KiB
TypeScript
"use client";
|
|
|
|
import Link from "next/link";
|
|
import { usePathname } from "next/navigation";
|
|
import { ReactNode } from "react";
|
|
import { VIBNSidebar } from "./vibn-sidebar";
|
|
import { Toaster } from "sonner";
|
|
|
|
interface ProjectShellProps {
|
|
children: ReactNode;
|
|
workspace: string;
|
|
projectId: string;
|
|
projectName: string;
|
|
projectDescription?: string;
|
|
projectStatus?: string;
|
|
projectProgress?: number;
|
|
discoveryPhase?: number;
|
|
capturedData?: Record<string, string>;
|
|
createdAt?: string;
|
|
updatedAt?: string;
|
|
featureCount?: number;
|
|
creationMode?: "fresh" | "chat-import" | "code-import" | "migration";
|
|
}
|
|
|
|
const ALL_TABS = [
|
|
{ id: "overview", label: "Atlas", path: "overview" },
|
|
{ id: "prd", label: "PRD", path: "prd" },
|
|
{ id: "build", label: "Build", path: "build" },
|
|
{ id: "growth", label: "Growth", path: "growth" },
|
|
{ id: "assist", label: "Assist", path: "assist" },
|
|
{ id: "analytics", label: "Analytics", path: "analytics" },
|
|
];
|
|
|
|
function getTabsForMode(
|
|
mode: "fresh" | "chat-import" | "code-import" | "migration" = "fresh"
|
|
) {
|
|
switch (mode) {
|
|
case "code-import":
|
|
return ALL_TABS.filter(t => t.id !== "prd");
|
|
case "migration":
|
|
return ALL_TABS
|
|
.filter(t => t.id !== "prd")
|
|
.map(t => t.id === "overview" ? { ...t, label: "Migration Plan" } : t);
|
|
default:
|
|
return ALL_TABS;
|
|
}
|
|
}
|
|
|
|
export function ProjectShell({
|
|
children,
|
|
workspace,
|
|
projectId,
|
|
creationMode,
|
|
}: ProjectShellProps) {
|
|
const pathname = usePathname();
|
|
const TABS = getTabsForMode(creationMode);
|
|
const activeTab = TABS.find(t => pathname?.includes(`/${t.path}`))?.id ?? "overview";
|
|
|
|
return (
|
|
<>
|
|
<style>{`
|
|
@media (max-width: 768px) {
|
|
.vibn-left-sidebar { display: none !important; }
|
|
.vibn-tab-bar { overflow-x: auto; -webkit-overflow-scrolling: touch; }
|
|
.vibn-tab-bar a { padding: 10px 14px !important; font-size: 0.75rem !important; }
|
|
.vibn-page-content { padding-bottom: env(safe-area-inset-bottom); }
|
|
}
|
|
@media (max-width: 480px) {
|
|
.vibn-tab-bar a { padding: 10px 10px !important; }
|
|
}
|
|
`}</style>
|
|
|
|
<div style={{ display: "flex", height: "100dvh", background: "#f6f4f0", overflow: "hidden" }}>
|
|
|
|
{/* Left sidebar */}
|
|
<div className="vibn-left-sidebar" style={{ display: "flex" }}>
|
|
<VIBNSidebar workspace={workspace} />
|
|
</div>
|
|
|
|
{/* Main column — full width */}
|
|
<div style={{ flex: 1, display: "flex", flexDirection: "column", minWidth: 0 }}>
|
|
|
|
{/* Tab bar — sits at the top, no header above it */}
|
|
<div className="vibn-tab-bar" style={{
|
|
padding: "0 24px",
|
|
borderBottom: "1px solid #e8e4dc",
|
|
display: "flex",
|
|
background: "#fff",
|
|
flexShrink: 0,
|
|
}}>
|
|
{TABS.map(t => (
|
|
<Link
|
|
key={t.id}
|
|
href={`/${workspace}/project/${projectId}/${t.path}`}
|
|
style={{
|
|
padding: "13px 16px",
|
|
fontSize: "0.8rem",
|
|
fontWeight: 500,
|
|
color: activeTab === t.id ? "#1a1a1a" : "#a09a90",
|
|
borderBottom: activeTab === t.id ? "2px solid #1a1a1a" : "2px solid transparent",
|
|
transition: "color 0.12s",
|
|
fontFamily: "Outfit, sans-serif",
|
|
textDecoration: "none",
|
|
display: "block",
|
|
whiteSpace: "nowrap",
|
|
}}
|
|
onMouseEnter={e => { if (activeTab !== t.id) (e.currentTarget as HTMLElement).style.color = "#6b6560"; }}
|
|
onMouseLeave={e => { if (activeTab !== t.id) (e.currentTarget as HTMLElement).style.color = "#a09a90"; }}
|
|
>
|
|
{t.label}
|
|
</Link>
|
|
))}
|
|
</div>
|
|
|
|
{/* Page content — full width, each page manages its own layout */}
|
|
<div className="vibn-page-content" style={{ flex: 1, overflow: "auto" }}>
|
|
{children}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<Toaster position="top-center" />
|
|
</>
|
|
);
|
|
}
|