Add LynkedUp Pro dashboard (dark + light) from Figma

- Full dashboard at /dashboard matching the Figma Linkedup-Web frame
- Dark/light theme toggle (persisted to localStorage)
- Live animated charts: stat sparkbars, P&L grouped bars, donut, gauge,
  pipeline bars, progress, revenue-potential
- Sidebar, topbar, stat cards, weather, top sales reps
- 29 icons exported from Figma (currentColor) + lucide fallbacks
- Fix: bar charts had zero height (percentage in indefinite flex parent)
- Fix: removed content max-width that left a right-side gap

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-27 20:12:35 +05:30
parent 8c5df83d63
commit 5ff655d786
38 changed files with 1223 additions and 48 deletions
+88
View File
@@ -0,0 +1,88 @@
"use client";
import { ChevronsUpDown } from "lucide-react";
import { FigIcon } from "./figicon";
type Item = { key: string; label: string };
const GROUPS: { title: string; items: Item[] }[] = [
{
title: "Workspace",
items: [
{ key: "dashboard", label: "Dashboard" },
{ key: "owners", label: "Owners Box" },
{ key: "projects", label: "Projects" },
{ key: "leads", label: "Leads" },
{ key: "verify", label: "Lead Verification" },
{ key: "pipeline", label: "Pipeline" },
],
},
{
title: "Workspace",
items: [
{ key: "dispatch", label: "LynkDispatch" },
{ key: "storm", label: "Storm Intel" },
{ key: "territory", label: "Territory Map" },
{ key: "procanvas", label: "ProCanvas" },
{ key: "estimates", label: "Estimates" },
],
},
{
title: "Team",
items: [
{ key: "schedule", label: "Team Schedule" },
{ key: "leaderboard", label: "Leaderboard" },
{ key: "subtasks", label: "Subcontractor Tasks" },
{ key: "people", label: "People" },
],
},
];
const EXTRA: Item[] = [
{ key: "settings", label: "Org Settings" },
{ key: "ai", label: "AI Assistant" },
{ key: "home", label: "Home" },
];
export function Sidebar({ active, onSelect }: { active: string; onSelect: (k: string) => void }) {
return (
<aside className="dash-sidebar">
<div className="dash-brand">
{/* eslint-disable-next-line @next/next/no-img-element */}
<img src="/image/logo.png" alt="LynkedUp Pro" />
<span className="bk">LynkedUp Pro</span>
</div>
<nav className="dash-nav">
{GROUPS.map((g, gi) => (
<div key={gi}>
<div className="nav-group">{g.title}</div>
{g.items.map((it) => <NavBtn key={it.key} it={it} active={active} onSelect={onSelect} />)}
</div>
))}
<div style={{ height: 8 }} />
{EXTRA.map((it) => <NavBtn key={it.key} it={it} active={active} onSelect={onSelect} />)}
</nav>
<div className="sb-foot">
<div className="sb-user">
<span className="av" style={{ background: "linear-gradient(135deg,#285ef0,#09b9c6)", display: "grid", placeItems: "center", color: "#fff", fontWeight: 700, fontSize: 12 }}>JJ</span>
<div style={{ flex: 1 }}>
<div className="nm">Justin Johnson</div>
<div className="rl">Owner</div>
</div>
<ChevronsUpDown size={15} />
</div>
</div>
</aside>
);
}
function NavBtn({ it, active, onSelect }: { it: Item; active: string; onSelect: (k: string) => void }) {
return (
<button className={`nav-item ${active === it.key ? "active" : ""}`} onClick={() => onSelect(it.key)}>
<FigIcon name={it.key} size={18} />
{it.label}
</button>
);
}