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
134 lines
5.9 KiB
TypeScript
134 lines
5.9 KiB
TypeScript
"use client";
|
|
|
|
import { Suspense } from "react";
|
|
import { useParams, useSearchParams, useRouter } from "next/navigation";
|
|
|
|
const SECTIONS = [
|
|
{
|
|
id: "customers",
|
|
label: "Customers",
|
|
icon: "◉",
|
|
title: "Customer List",
|
|
desc: "Every user who has signed up, their plan, usage, last seen, and lifecycle stage. Filter, search, and act on any segment.",
|
|
items: ["User Directory", "Lifecycle Stages", "Plan & Billing", "Activity Timeline", "Segment Builder"],
|
|
},
|
|
{
|
|
id: "usage",
|
|
label: "Usage",
|
|
icon: "∿",
|
|
title: "Usage & Activity",
|
|
desc: "How users interact with your product — feature adoption, session frequency, retention curves, and activation funnels.",
|
|
items: ["Feature Adoption", "Session Metrics", "Retention Curves", "Activation Funnel", "Power Users"],
|
|
},
|
|
{
|
|
id: "events",
|
|
label: "Events",
|
|
icon: "◬",
|
|
title: "Events & Tracking",
|
|
desc: "Every event your product fires — page views, clicks, conversions, and custom events — all tagged and queryable.",
|
|
items: ["Event Stream", "Custom Events", "Page Views", "Conversion Events", "Tag Manager"],
|
|
},
|
|
{
|
|
id: "reports",
|
|
label: "Reports",
|
|
icon: "▭",
|
|
title: "Reports",
|
|
desc: "MRR, churn, DAU/MAU, cohort analysis, and revenue reports. Export or share with your team on a schedule.",
|
|
items: ["Revenue (MRR/ARR)", "Churn Report", "DAU / MAU", "Cohort Analysis", "Custom Reports", "Scheduled Exports"],
|
|
},
|
|
] 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 AnalyticsInner() {
|
|
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") ?? "customers") as SectionId;
|
|
const active = SECTIONS.find(s => s.id === activeId) ?? SECTIONS[0];
|
|
|
|
const setSection = (id: string) =>
|
|
router.push(`/${workspace}/project/${projectId}/analytics?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}>Analytics</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>
|
|
|
|
<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>
|
|
|
|
<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 AnalyticsPage() {
|
|
return (
|
|
<Suspense fallback={<div style={{ display: "flex", height: "100%", alignItems: "center", justifyContent: "center", color: "#a09a90", fontFamily: "Outfit, sans-serif", fontSize: "0.85rem" }}>Loading…</div>}>
|
|
<AnalyticsInner />
|
|
</Suspense>
|
|
);
|
|
}
|