Replace dashboard with Profile, Support Center & Rules modules

- Remove old dashboard stats/charts content; keep sidebar + header shell
  which now routes between Profile, Support Center and Rules views.
- Profile: hero card + 6 tabs (Personal Info with verify-to-reveal +
  editable contacts via OTP, KYC two-doc rule + completion %, Security
  password policy + 2FA, Notifications matrix + contact windows, Privacy
  consents, Devices + activity timeline).
- Support Center: 5 channel cards, support team, Message Center with
  typing, New Ticket category->department routing + attachment rules,
  My Tickets + status-timeline modal, Live Chat handshake, Callback/Email
  modals, Help Center search + accordion.
- Rules: the 1-13 business-rules checklist.
- Shared design system (Icon, Avatar, PageHead, Pill, Toggle, OtpField,
  Modal, Toast, Field, Tabs) + full mock data in account-data.ts.
- Premium dark visual pass: brand gradients, depth, glow accents, pill tabs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-29 21:52:34 +05:30
parent 5ff655d786
commit 3ab6768c1c
13 changed files with 2663 additions and 512 deletions
+21 -44
View File
@@ -1,49 +1,32 @@
"use client";
import { ChevronsUpDown } from "lucide-react";
import { FigIcon } from "./figicon";
import { Icon } from "./ui";
import { user } from "./account-data";
type Item = { key: string; label: string };
type Item = { key: string; label: string; icon: string };
const GROUPS: { title: string; items: Item[] }[] = [
{
title: "Workspace",
title: "Account",
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" },
{ key: "profile", label: "Profile", icon: "user" },
],
},
{
title: "Workspace",
title: "Help & Support",
items: [
{ key: "dispatch", label: "LynkDispatch" },
{ key: "storm", label: "Storm Intel" },
{ key: "territory", label: "Territory Map" },
{ key: "procanvas", label: "ProCanvas" },
{ key: "estimates", label: "Estimates" },
{ key: "support", label: "Support Center", icon: "chat" },
],
},
{
title: "Team",
title: "Reference",
items: [
{ key: "schedule", label: "Team Schedule" },
{ key: "leaderboard", label: "Leaderboard" },
{ key: "subtasks", label: "Subcontractor Tasks" },
{ key: "people", label: "People" },
{ key: "rules", label: "Rules Checklist", icon: "check-circle" },
],
},
];
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">
@@ -57,32 +40,26 @@ export function Sidebar({ active, onSelect }: { active: string; onSelect: (k: st
{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} />)}
{g.items.map((it) => (
<button key={it.key} className={`nav-item ${active === it.key ? "active" : ""}`} onClick={() => onSelect(it.key)}>
<Icon name={it.icon} size={18} />
{it.label}
</button>
))}
</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>
<button className="sb-user">
<span className="av" style={{ background: user.avatarGradient, display: "grid", placeItems: "center", color: "#fff", fontWeight: 700, fontSize: 12 }}>{user.initials}</span>
<div style={{ flex: 1, textAlign: "left" }}>
<div className="nm">{user.name}</div>
<div className="rl">{user.role}</div>
</div>
<ChevronsUpDown size={15} />
</div>
</button>
</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>
);
}