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
145 lines
6.1 KiB
TypeScript
145 lines
6.1 KiB
TypeScript
"use client";
|
|
|
|
import { Suspense } from "react";
|
|
import { useParams, useSearchParams, useRouter } from "next/navigation";
|
|
|
|
const SECTIONS = [
|
|
{
|
|
id: "marketing-site",
|
|
label: "Marketing Site",
|
|
icon: "◌",
|
|
title: "Marketing Site",
|
|
desc: "Your public-facing website — hero, features, pricing, blog, and landing pages. Connected to your design surface and deployed via your infrastructure.",
|
|
items: ["Hero & Landing", "Features", "Pricing Page", "Blog", "Case Studies", "About"],
|
|
},
|
|
{
|
|
id: "communications",
|
|
label: "Communications",
|
|
icon: "◈",
|
|
title: "Communications",
|
|
desc: "Outbound messaging — product announcements, newsletters, launch emails, and drip campaigns sent to your audience.",
|
|
items: ["Announcements", "Newsletter", "Launch Sequence", "Drip Campaigns"],
|
|
},
|
|
{
|
|
id: "channels",
|
|
label: "Channels",
|
|
icon: "↗",
|
|
title: "Distribution Channels",
|
|
desc: "Where your product gets discovered — SEO, social, Product Hunt, app stores, partnerships, and paid acquisition.",
|
|
items: ["SEO & Search", "Social Media", "Product Hunt", "App Stores", "Partnerships", "Paid Ads"],
|
|
},
|
|
{
|
|
id: "pages",
|
|
label: "Pages",
|
|
icon: "▭",
|
|
title: "Pages",
|
|
desc: "Individual landing pages for campaigns, experiments, and specific audience segments. Build, publish, and A/B test.",
|
|
items: ["Campaign Pages", "A/B Tests", "Event Pages", "Partner Pages", "Waitlist"],
|
|
},
|
|
] as const;
|
|
|
|
type SectionId = typeof SECTIONS[number]["id"];
|
|
|
|
const NAV_GROUP: React.CSSProperties = {
|
|
fontSize: "0.6rem", fontWeight: 700, color: "#b5b0a6",
|
|
letterSpacing: "0.09em", textTransform: "uppercase",
|
|
padding: "14px 12px 6px", fontFamily: "Outfit, sans-serif",
|
|
};
|
|
|
|
function GrowthInner() {
|
|
const params = useParams();
|
|
const searchParams = useSearchParams();
|
|
const router = useRouter();
|
|
const workspace = params.workspace as string;
|
|
const projectId = params.projectId as string;
|
|
|
|
const activeId = (searchParams.get("section") ?? "marketing-site") as SectionId;
|
|
const active = SECTIONS.find(s => s.id === activeId) ?? SECTIONS[0];
|
|
|
|
const setSection = (id: string) =>
|
|
router.push(`/${workspace}/project/${projectId}/growth?section=${id}`, { scroll: false });
|
|
|
|
return (
|
|
<div style={{ display: "flex", height: "100%", fontFamily: "Outfit, sans-serif", overflow: "hidden" }}>
|
|
|
|
{/* Left nav */}
|
|
<div style={{ width: 200, flexShrink: 0, borderRight: "1px solid #e8e4dc", background: "#faf8f5", display: "flex", flexDirection: "column", overflow: "auto" }}>
|
|
<div style={NAV_GROUP}>Growth</div>
|
|
{SECTIONS.map(s => {
|
|
const isActive = activeId === s.id;
|
|
return (
|
|
<button key={s.id} onClick={() => setSection(s.id)} style={{
|
|
display: "flex", alignItems: "center", gap: 8, width: "100%", textAlign: "left",
|
|
background: isActive ? "#f0ece4" : "transparent", border: "none", cursor: "pointer",
|
|
padding: "6px 12px", borderRadius: 5,
|
|
fontSize: "0.78rem", fontWeight: isActive ? 600 : 440,
|
|
color: isActive ? "#1a1a1a" : "#5a5550",
|
|
}}
|
|
onMouseEnter={e => { if (!isActive) (e.currentTarget as HTMLElement).style.background = "#f6f4f0"; }}
|
|
onMouseLeave={e => { if (!isActive) (e.currentTarget as HTMLElement).style.background = "transparent"; }}
|
|
>
|
|
<span style={{ fontSize: "0.65rem", opacity: 0.55, width: 14, textAlign: "center" }}>{s.icon}</span>
|
|
{s.label}
|
|
</button>
|
|
);
|
|
})}
|
|
</div>
|
|
|
|
{/* Content */}
|
|
<div style={{ flex: 1, overflow: "auto", display: "flex", flexDirection: "column" }}>
|
|
<div style={{ padding: "28px 32px", maxWidth: 800 }}>
|
|
<div style={{ marginBottom: 24 }}>
|
|
<div style={{ fontSize: "1.1rem", fontWeight: 700, color: "#1a1a1a", marginBottom: 6 }}>{active.title}</div>
|
|
<div style={{ fontSize: "0.82rem", color: "#6b6560", lineHeight: 1.65, maxWidth: 520 }}>{active.desc}</div>
|
|
</div>
|
|
|
|
{/* Feature items */}
|
|
<div style={{ display: "grid", gridTemplateColumns: "repeat(auto-fill, minmax(200px, 1fr))", gap: 12, marginBottom: 32 }}>
|
|
{active.items.map(item => (
|
|
<div key={item} style={{
|
|
background: "#fff", border: "1px solid #e8e4dc", borderRadius: 9,
|
|
padding: "14px 16px", display: "flex", alignItems: "center", justifyContent: "space-between",
|
|
}}>
|
|
<span style={{ fontSize: "0.8rem", fontWeight: 500, color: "#1a1a1a" }}>{item}</span>
|
|
<span style={{ fontSize: "0.65rem", color: "#c5c0b8", background: "#f6f4f0", padding: "2px 7px", borderRadius: 4 }}>Soon</span>
|
|
</div>
|
|
))}
|
|
</div>
|
|
|
|
{/* CTA */}
|
|
<div style={{
|
|
background: "linear-gradient(135deg, #1a1a1a 0%, #2d2820 100%)",
|
|
borderRadius: 12, padding: "24px 28px",
|
|
display: "flex", alignItems: "center", justifyContent: "space-between",
|
|
gap: 20,
|
|
}}>
|
|
<div>
|
|
<div style={{ fontSize: "0.85rem", fontWeight: 600, color: "#fff", marginBottom: 4 }}>
|
|
{active.title} is coming to VIBN
|
|
</div>
|
|
<div style={{ fontSize: "0.75rem", color: "#8a8478", lineHeight: 1.5 }}>
|
|
We're building this section next. Shape it by telling us what you need.
|
|
</div>
|
|
</div>
|
|
<button style={{
|
|
background: "#d4a04a", color: "#fff", border: "none", borderRadius: 8,
|
|
padding: "9px 20px", fontSize: "0.78rem", fontWeight: 600,
|
|
cursor: "pointer", whiteSpace: "nowrap", flexShrink: 0,
|
|
}}>
|
|
Give feedback
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default function GrowthPage() {
|
|
return (
|
|
<Suspense fallback={<div style={{ display: "flex", height: "100%", alignItems: "center", justifyContent: "center", color: "#a09a90", fontFamily: "Outfit, sans-serif", fontSize: "0.85rem" }}>Loading…</div>}>
|
|
<GrowthInner />
|
|
</Suspense>
|
|
);
|
|
}
|