import { useState, useEffect, useRef } from "react";
// ── DESIGN TOKENS ─────────────────────────────────────────────────────────────
const C = {
primary: "#6760f9",
primaryHover: "#5a53e0",
primarySurface: "#eff2fe",
textBase: "#040711",
textSecondary: "#656975",
textDisabled: "#9fa2ab",
white: "#ffffff",
bg0: "#ffffff",
bg05: "#fbfbfb",
bg1: "#f6f6f7",
bg2: "#f2f2f2",
bg3: "#ebebed",
borderT0: "rgba(0,0,0,0.05)",
borderT1: "rgba(0,0,0,0.10)",
borderT2: "rgba(0,0,0,0.20)",
solidBorder1: "#d6d8db",
quoted: "#ef9536",
newClient: "#49a08a",
imported: "#3382f4",
sold: "#5353f0",
followUpBg: "#f8d2e0",
lost: "#b7b5b5",
errorRed: "#ef4444",
successGreen: "#10b981",
infoBlue: "#3b82f6",
};
const FONT = { fontFamily: "Inter, -apple-system, sans-serif" };
// ── NOTIFICATION DATA ─────────────────────────────────────────────────────────
const initNotifications = [
{ id: 1, type: "error", action: "Fix Now", time: "2m ago", read: false, order: 1 },
{ id: 2, type: "error", action: "Add Email Address", time: "5m ago", read: false, order: 2 },
{ id: 3, type: "error", action: "Add Policy", time: "12m ago", read: false, order: 3 },
{ id: 12, type: "error", action: "Fix Now", time: "2h ago", read: false, order: 4 },
{ id: 6, type: "info", action: null, time: "3h ago", read: true, order: 5 },
{ id: 14, type: "error", action: "Fix Now", time: "3h ago", read: true, order: 6 },
{ id: 15, type: "info", action: null, time: "3h ago", read: true, order: 7 },
{ id: 7, type: "info", action: null, time: "4h ago", read: true, order: 8 },
{ id: 5, type: "error", action: "Resend Link", time: "4h ago", read: true, order: 9 },
{ id: 17, type: "success", action: "Download", time: "5h ago", read: true, order: 10 },
{ id: 16, type: "info", action: null, time: "5h ago", read: true, order: 11 },
{ id: 9, type: "info", action: null, time: "6h ago", read: true, order: 12 },
{ id: 18, type: "info", action: null, time: "6h ago", read: true, order: 13 },
{ id: 11, type: "info", action: null, time: "7h ago", read: true, order: 14 },
{ id: 19, type: "info", action: null, time: "7h ago", read: true, order: 15 },
{ id: 10, type: "info", action: null, time: "Yesterday", read: true, order: 16 },
];
const whatsNew = [
{ id: 1, title: "Screenshot-to-Quote", desc: "Capture a screenshot of any carrier quote and let AI extract the data automatically." },
{ id: 2, title: "Client Events", desc: "Track client activity including portal visits, quote responses, document uploads, and more." },
{ id: 3, title: "Create New Clients via OCR", desc: "Create new clients manually or by uploading a document and letting OCR extract the details." },
];
// ── SETTINGS DATA ─────────────────────────────────────────────────────────────
const settingsGroups = [
{
id: "client", label: "Client Updates", desc: "New leads, assignments, and status changes.",
items: [
{ id: "client_assigned", label: "You were assigned a new client" },
{ id: "client_lead", label: "A new lead came in" },
{ id: "client_status", label: "Client status changed" },
],
},
{
id: "forms", label: "Form & Document Activity", desc: "Form submissions, document uploads, and share link activity.",
items: [
{ id: "form_submission", label: "Client completed a form submission" },
{ id: "form_pdf", label: "Client completed a PDF supplement" },
{ id: "form_upload", label: "Client uploaded a document" },
{ id: "form_quote", label: "Client responded to their quote" },
{ id: "form_link_opened", label: "Form share link was opened" },
{ id: "form_link_expired", label: "Form link expired" },
{ id: "form_export", label: "Your Advanced Export is ready" },
],
},
{
id: "workflow", label: "Workflow Activity", desc: "Completed workflows and workflow errors.",
items: [
{ id: "workflow_completed", label: "Workflow completed" },
{ id: "workflow_failed", label: "Workflow failed" },
],
},
];
// ── CLIENTS DATA ──────────────────────────────────────────────────────────────
const CLIENTS = [
{ id: 1, name: "John Doe", status: "Follow Up", days: 8, pipeline: "New Business", lines: ["home","auto"], form: "New Policy Application Form", ftype: "Intake form", source: "Website", agent: "John Doe" },
{ id: 2, name: "Alex Carter", status: "Lost", days: 0, pipeline: "Re-Marketing", lines: ["life"], form: "Audit Request Form", ftype: "Customer form", source: "Referral", agent: "Alex Carter" },
{ id: 3, name: "Sophia Taylor", status: "Quoted", days: 4, pipeline: "Re-Marketing", lines: ["home","umbrella"], form: "Account Verification Form", ftype: "Intake form", source: "Call-In", agent: "Sophia Taylor" },
{ id: 4, name: "Jessica Brown", status: "Imported", days: 2, pipeline: "Marketing Leads", lines: ["auto","home","umbrella","life"], form: "User Access Request Form", ftype: "Intake form", source: "Referral", agent: "Jessica Brown" },
{ id: 5, name: "Daniel Martinez", status: "Sold", days: 4, pipeline: "New Business", lines: ["home"], form: "Service Request Form", ftype: "Customer form", source: "Website", agent: "Daniel Martinez" },
{ id: 6, name: "Olivia Garcia", status: "Quoted", days: 1, pipeline: "Re-Marketing", lines: ["auto","home","life"], form: "Data Privacy Consent Form", ftype: "Customer form", source: "Call-In", agent: "Olivia Garcia" },
{ id: 7, name: "Mia Harris", status: "Follow Up", days: 8, pipeline: "New Business", lines: ["auto"], form: "Incident Report Form", ftype: "Intake form", source: "Referral", agent: "Mia Harris" },
{ id: 8, name: "Ethan White", status: "New Client", days: 0, pipeline: "Marketing Leads", lines: ["home"], form: "Claim Submission Form", ftype: "Intake form", source: "Call-In", agent: "Ethan White" },
{ id: 9, name: "Isabella Lewis", status: "Lost", days: 8, pipeline: "Marketing Leads", lines: [], form: "Feedback Survey Form", ftype: "Intake form", source: "Website", agent: "Isabella Lewis" },
{ id: 10, name: "Ava Jackson", status: "Quoted", days: 1, pipeline: "Re-Marketing", lines: ["auto","home","life"], form: "Customer Feedback Form", ftype: "Customer form", source: "Referral", agent: "Ava Jackson" },
{ id: 11, name: "William Thomas", status: "Sold", days: 0, pipeline: "New Business", lines: ["auto"], form: "Change of Beneficiary Form", ftype: "Intake form", source: "Call-In", agent: "William Thomas" },
{ id: 12, name: "David Wilson", status: "Imported", days: 4, pipeline: "Re-Marketing", lines: ["home"], form: "Coverage Adjustment Form", ftype: "Customer form", source: "Call-In", agent: "David Wilson" },
{ id: 13, name: "Emily Davis", status: "Follow Up", days: 8, pipeline: "Re-Marketing", lines: ["home","auto"], form: "Risk Assessment Form", ftype: "Intake form", source: "Referral", agent: "Emily Davis" },
];
const STATUS_STYLE = {
"Follow Up": { bg: C.followUpBg, color: C.textBase },
"Lost": { bg: C.lost, color: C.white },
"Quoted": { bg: C.quoted, color: C.white },
"Imported": { bg: C.imported, color: C.white },
"Sold": { bg: C.sold, color: C.white },
"New Client": { bg: C.newClient, color: C.white },
};
const STATUS_TABS = [
{ label: "Total (80)", amount: "$500k" },
{ label: "Quoted (20)", amount: "$200k", bg: C.quoted, color: C.white },
{ label: "New Client (30)", amount: "$50k", bg: C.newClient, color: C.white },
{ label: "Imported (10)", amount: "$100k", bg: C.imported, color: C.white },
{ label: "Sold (5)", amount: "$75k", bg: C.sold, color: C.white },
{ label: "Follow Up (10)", amount: "$50k", bg: C.followUpBg, color: C.textBase },
{ label: "Lost (5)", amount: "$25k", bg: C.lost, color: C.white },
];
// ── AVATAR ────────────────────────────────────────────────────────────────────
const PALETTE = [
{ bg: "#eff2fe", fg: "#6760f9" },
{ bg: "#e4fbe8", fg: "#3c7f3f" },
{ bg: "#cefafe", fg: "#007595" },
{ bg: "#f8e4e2", fg: "#cc311a" },
{ bg: "#fbf4cb", fg: "#a85701" },
{ bg: "#f2f2f2", fg: "#656975" },
];
const initials = (n) => n.split(" ").map(w => w[0]).join("").slice(0,2).toUpperCase();
const Avatar = ({ name, size = 24 }) => {
const { bg, fg } = PALETTE[name.charCodeAt(0) % PALETTE.length];
return (
{initials(name)}
);
};
// ── POLICY LINE ICONS ─────────────────────────────────────────────────────────
const PolicyIcon = ({ type }) => {
const paths = {
home: "M2 7.5L8 2l6 5.5V14a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V7.5zM6 15v-5h4v5",
auto: "M2 9l1.5-4h9L14 9v3H2V9zM4.5 12a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3zm7 0a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3",
umbrella: "M8 2a6 6 0 0 0-6 6h12a6 6 0 0 0-6-6zM8 8v5a2 2 0 0 0 4 0",
life: "M8 14s-6-3.5-6-7.5a3.5 3.5 0 0 1 6-2.45A3.5 3.5 0 0 1 14 6.5C14 10.5 8 14 8 14z",
};
return (
);
};
const PolicyLines = ({ lines }) => (
{lines.slice(0,3).map((l,i) =>
)}
{lines.length > 3 &&
+{lines.length-3} }
);
// ── SOURCE CELL ───────────────────────────────────────────────────────────────
const SOURCE_CFG = {
Website: { color: C.quoted, path: "M10 3a7 7 0 1 0 0 14A7 7 0 0 0 10 3zm0 0c-2 2-3 4.5-3 7s1 5 3 7m0-14c2 2 3 4.5 3 7s-1 5-3 7M3 10h14" },
Referral: { color: C.newClient, path: "M7 9a3 3 0 1 0 0-6 3 3 0 0 0 0 6zm-5.5 8a5.5 5.5 0 0 1 11 0" },
"Call-In":{ color: C.imported, path: "M4 9a6 6 0 0 1 12 0v3.5L17.5 15h-15L4 12.5V9zM8.5 15.5a1.5 1.5 0 0 0 3 0" },
};
const SourceCell = ({ source }) => {
const { color, path } = SOURCE_CFG[source];
return (
);
};
// ── NOTIFICATION HELPERS ──────────────────────────────────────────────────────
const NL = ({ children }) => (
{children}
);
const renderTitle = (n) => {
switch (n.id) {
case 1: return <>Applied Epic Policy Renewal Workflow : Outlook disconnected>;
case 2: return <>Applied Epic Policy Renewal Workflow for Michael Chen : Missing email address>;
case 3: return <>Applied Epic Policy Renewal Workflow for Michael Chen : Policy not found in Applied Epic>;
case 5: return <>Form link expired for David Kim >;
case 6: return <>Sarah Johnson completed a form submission >;
case 7: return <>David Kim uploaded a document>;
case 9: return <>You were assigned a new client: Emily Ross >;
case 10: return <>Workflow "Auto Follow-Up" completed for Michael Chen >;
case 11: return <>Emily Ross responded to their quote>;
case 12: return <>Applied Epic Policy Renewal Workflow : Google Gmail disconnected>;
case 14: return <>AMS360 connection lost>;
case 15: return <>Michael Chen completed a PDF supplement>;
case 16: return <>Form share link was opened by David Kim >;
case 17: return <>Your Advanced Export is ready>;
case 18: return <>A new lead came in from Referral Partner: State Farm >;
case 19: return <>Michael Chen status changed to Quoted>;
default: return null;
}
};
const NIcon = ({ type }) => {
if (type === "error") return (
);
if (type === "success") return (
);
return (
);
};
// ── TOGGLE ────────────────────────────────────────────────────────────────────
const Toggle = ({ checked, onChange }) => (
);
// ── NAV ICONS ─────────────────────────────────────────────────────────────────
const NavIcons = {
search: (
),
clients: (
),
folder: (
),
file: (
),
form: (
),
check: (
),
chart: (
),
connect: (
),
settings: (
),
help: (
),
};
const NAV_ITEMS = [
{ id: "clients", icon: NavIcons.clients, active: true },
{ id: "folder", icon: NavIcons.folder },
{ id: "file", icon: NavIcons.file },
{ id: "form", icon: NavIcons.form },
{ id: "check", icon: NavIcons.check },
{ id: "chart", icon: NavIcons.chart },
{ id: "connect", icon: NavIcons.connect },
{ id: "settings", icon: NavIcons.settings, settings: true },
];
// ── OUTLINE BUTTON STYLE ──────────────────────────────────────────────────────
const outlineBtn = {
display: "inline-flex", alignItems: "center", gap: 2, padding: "4px", background: C.white,
borderRadius: 8, border: `1px solid ${C.borderT1}`, boxShadow: "0 2px 3px rgba(0,0,0,0.03)",
color: C.textBase, fontSize: 15, fontWeight: 500, cursor: "pointer", ...FONT,
};
// ── MAIN APP ──────────────────────────────────────────────────────────────────
export default function App() {
const [items, setItems] = useState(initNotifications);
const [filter, setFilter] = useState("all");
const [notifTab, setNotifTab] = useState("notifications");
const [popoverOpen, setPopover] = useState(false);
const [activePage, setActivePage] = useState("clients");
const [activeStatusTab, setActiveStatusTab] = useState(0);
const [selectedRows, setSelectedRows] = useState(new Set([8]));
const popoverRef = useRef(null);
const bellRef = useRef(null);
// Init settings
const initPrefs = {};
settingsGroups.forEach(g => { initPrefs[g.id] = true; g.items.forEach(i => { initPrefs[i.id] = true; }); });
const [prefs, setPrefs] = useState(initPrefs);
const togglePref = (id, groupId) => {
setPrefs(p => {
const next = { ...p, [id]: !p[id] };
if (groupId) {
const g = settingsGroups.find(g => g.id === groupId);
next[groupId] = g.items.every(item => item.id === id ? !p[id] : p[item.id]);
} else {
const g = settingsGroups.find(g => g.id === id);
if (g) g.items.forEach(item => { next[item.id] = !p[id]; });
}
return next;
});
};
useEffect(() => {
const handler = (e) => {
if (popoverOpen && popoverRef.current && !popoverRef.current.contains(e.target) && bellRef.current && !bellRef.current.contains(e.target)) {
setItems(prev => prev.map(n => ({ ...n, read: true })));
setPopover(false);
}
};
document.addEventListener("mousedown", handler);
return () => document.removeEventListener("mousedown", handler);
}, [popoverOpen]);
const unreadCount = items.filter(n => !n.read).length;
const markAllRead = () => { setItems(items.map(n => ({ ...n, read: true }))); setFilter("all"); };
const sorted = [...items].sort((a, b) => a.read !== b.read ? (a.read ? 1 : -1) : a.order - b.order);
const filtered = filter === "unread" ? sorted.filter(n => !n.read) : sorted;
const openPopover = () => {
if (popoverOpen) setItems(prev => prev.map(n => ({ ...n, read: true })));
setPopover(p => !p);
};
const toggleRow = (id) => setSelectedRows(prev => { const n = new Set(prev); n.has(id) ? n.delete(id) : n.add(id); return n; });
// ── SIDEBAR ──────────────────────────────────────────────────────────────
const Sidebar = () => (
{/* Logo */}
{/* Search + Bell */}
{/* Globe */}
{/* Nav */}
{NAV_ITEMS.map(item => {
const isActive = (item.active && activePage === "clients") || (item.settings && activePage === "settings");
return (
{ if (item.settings) { setActivePage("settings"); setPopover(false); } else if (item.active) { setActivePage("clients"); } }}
style={{ padding: "6px", borderRadius: 8, border: "none", background: isActive ? C.primarySurface : "transparent", display: "flex", justifyContent: "center", cursor: "pointer", color: isActive ? C.primary : C.textSecondary, width: "100%" }}
>
{item.icon}
);
})}
{/* Help */}
);
// ── CLIENTS PAGE ──────────────────────────────────────────────────────────
const ClientsPage = () => (
{/* Actions bar */}
Re-marketing
{[
{ label: "Export PDF", icon: "M5 3h7l4 4v10a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1zm7 0v4h4" },
{ label: "Import", icon: "M10 13V4M7 7l3-3 3 3m-9 8v2a1 1 0 0 0 1 1h10a1 1 0 0 0 1-1v-2" },
].map((b, i) => (
))}
New Client
{/* Status tabs */}
{STATUS_TABS.map((tab, i) => (
setActiveStatusTab(i)} style={{ display: "flex", alignItems: "center", gap: 8, padding: "6px 8px", borderRadius: 8, border: `1px solid ${activeStatusTab === i ? C.borderT2 : C.borderT1}`, background: activeStatusTab === i ? C.bg2 : C.white, cursor: "pointer", opacity: activeStatusTab === i ? 1 : 0.7 }}>
{tab.bg ? (
{tab.label}
) : (
{tab.label}
)}
{tab.amount}
))}
{/* Search & filter */}
{/* Table */}
{[
{ label: "Client", icon: "M8 9a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM2 17a6 6 0 0 1 12 0" },
{ label: "Status", icon: "M10 3a7 7 0 1 0 0 14A7 7 0 0 0 10 3zM10 7v3.5l2 1.5" },
{ label: "Pipeline", icon: "M2 5h4v4H2zm6 0h4v4H8zm6 0h4v4h-4zM2 11h4v4H2zm6 0h4v4H8zm6 0h4v4h-4z" },
{ label: "Policy Lines", icon: "M8 6a6 6 0 1 1 0 12A6 6 0 0 1 8 6z" },
{ label: "Form", icon: "M5 2h7l4 4v10H5V2zm7 0v4h4m-9 4h6m-6 3h6m-6 3h4" },
{ label: "Source", icon: "M8 6a6 6 0 1 1 0 12A6 6 0 0 1 8 6z" },
{ label: "Assigned Agent", icon: "M8 9a3 3 0 1 0 0-6 3 3 0 0 0 0 6zM2 17a6 6 0 0 1 12 0" },
].map(h => (
))}
{CLIENTS.map(c => {
const sel = selectedRows.has(c.id);
return (
{ if (!sel) e.currentTarget.style.background = C.bg05; }}
onMouseLeave={e => { if (!sel) e.currentTarget.style.background = C.white; }}
>
toggleRow(c.id)} style={{ accentColor: C.primary, cursor: "pointer" }} />
{c.status}
{c.days}d
{c.pipeline}
{c.form}
{c.ftype}
);
})}
Show More
);
// ── SETTINGS PAGE ─────────────────────────────────────────────────────────
const SettingsPage = () => (
{/* Settings sidebar */}
{[
{ section: "Organization", items: ["Users", "Groups"] },
{ section: "CRM & Data", items: ["CRM (Tracking)", "Clients", "Referral Source"] },
{ section: "Content", items: ["Forms", "Custom Field", "Proposal Templates"] },
{ section: "Automation", items: ["Workflow", "Applications"] },
{ section: "Brand & Company", items: ["Company", "Branding", "Proposal Templates"] },
].map(g => (
{g.items.map(item => (
e.currentTarget.style.background = C.bg1}
onMouseLeave={e => e.currentTarget.style.background = "transparent"}
>{item}
))}
))}
{["API", "Sys Admins", "Notifications"].map(item => (
{ if (item !== "Notifications") e.currentTarget.style.background = C.bg1; }}
onMouseLeave={e => { if (item !== "Notifications") e.currentTarget.style.background = "transparent"; }}
>{item}
))}
{/* Settings content */}
Notification Preferences
Choose which notifications you receive in-app.
{settingsGroups.map(g => (
{prefs[g.id] && g.items.map((item, ii) => (
{item.label}
togglePref(item.id, g.id)} />
))}
))}
);
// ── POPOVER ───────────────────────────────────────────────────────────────
const Popover = () => (
{/* Tabs */}
{[["notifications","Notifications"],["whats_new","What's New"]].map(([id, label]) => (
setNotifTab(id)} style={{ padding: "12px 0", marginRight: 18, border: "none", background: "none", cursor: "pointer", fontSize: 14, fontWeight: 600, color: notifTab === id ? C.primary : C.textDisabled, borderBottom: notifTab === id ? `2px solid ${C.primary}` : "2px solid transparent", marginBottom: -1, display: "flex", alignItems: "center", gap: 6, ...FONT }}>
{label}
{id === "notifications" && unreadCount > 0 && (
{unreadCount}
)}
))}
{notifTab === "notifications" && <>
{/* Controls */}
{[["all","All"],["unread","Unread"]].map(([f, label]) => (
setFilter(f)} style={{ padding: "3px 9px", borderRadius: 6, border: "none", background: filter === f ? C.primarySurface : "transparent", color: filter === f ? C.primary : C.textDisabled, fontSize: 14, fontWeight: 500, cursor: "pointer", ...FONT }}>{label}
))}
Mark all as read
{/* List */}
{filtered.length === 0 ? (
You're all caught up
No new notifications
) : filtered.map((n, i) => (
e.currentTarget.style.background = C.bg1}
onMouseLeave={e => e.currentTarget.style.background = n.read ? C.white : C.primarySurface + "55"}
>
{n.action && (
{n.action}
)}
))}
>}
{notifTab === "whats_new" && (
{whatsNew.map((item, i) => (
e.currentTarget.style.background = C.bg1}
onMouseLeave={e => e.currentTarget.style.background = C.white}
>
))}
)}
{/* Footer */}
{ setActivePage("settings"); setPopover(false); }} style={{ background: "none", border: "none", fontSize: 14, color: C.primary, cursor: "pointer", fontWeight: 500, ...FONT }}>
Notification Settings
);
// ── RENDER ────────────────────────────────────────────────────────────────
return (
{activePage === "clients" ?
:
}
{popoverOpen &&
}
);
}