import { useState, useMemo } from “react”; import { LineChart, Line, BarChart, Bar, ComposedChart, XAxis, YAxis, Tooltip, Legend, ResponsiveContainer, CartesianGrid, Area, AreaChart, ReferenceLine } from “recharts”; const RAW = [ { month: “January”, name: “Andrew Collado”, doors: 1091, contacts: 358, appts: 30, sitdowns: 6, qfd: 2, deals: 2 }, { month: “January”, name: “Elijah Jean”, doors: 451, contacts: 116, appts: 11, sitdowns: 2, qfd: 2, deals: 2 }, { month: “January”, name: “Isabella Velez”, doors: 305, contacts: 49, appts: 11, sitdowns: 1, qfd: 1, deals: 1 }, { month: “February”, name: “Andrew Collado”, doors: 994, contacts: 357, appts: 34, sitdowns: 11, qfd: 5, deals: 2 }, { month: “February”, name: “Elijah Jean”, doors: 662, contacts: 175, appts: 18, sitdowns: 6, qfd: 2, deals: 2 }, { month: “February”, name: “Isabella Velez”, doors: 389, contacts: 73, appts: 22, sitdowns: 1, qfd: 1, deals: 1 }, { month: “February”, name: “German Perez”, doors: 955, contacts: 99, appts: 22, sitdowns: 12, qfd: 3, deals: 5 }, { month: “February”, name: “Phillip Hall”, doors: 751, contacts: 170, appts: 23, sitdowns: 5, qfd: 3, deals: 1 }, { month: “February”, name: “Jason Mullen”, doors: 437, contacts: 157, appts: 25, sitdowns: 5, qfd: 3, deals: 2 }, { month: “February”, name: “Eddie Lopez”, doors: 743, contacts: 178, appts: 24, sitdowns: 8, qfd: 6, deals: 3 }, { month: “March”, name: “Andrew Collado”, doors: 978, contacts: 407, appts: 39, sitdowns: 8, qfd: 3, deals: 1 }, { month: “March”, name: “Elijah Jean”, doors: 487, contacts: 154, appts: 20, sitdowns: 2, qfd: 2, deals: 2 }, { month: “March”, name: “Isabella Velez”, doors: 382, contacts: 92, appts: 28, sitdowns: 7, qfd: 4, deals: 2 }, { month: “March”, name: “German Perez”, doors: 856, contacts: 142, appts: 28, sitdowns: 15, qfd: 3, deals: 3 }, { month: “March”, name: “Phillip Hall”, doors: 831, contacts: 219, appts: 52, sitdowns: 12, qfd: 7, deals: 6 }, { month: “March”, name: “Jason Mullen”, doors: 262, contacts: 88, appts: 17, sitdowns: 5, qfd: 4, deals: 3 }, { month: “March”, name: “Eddie Lopez”, doors: 729, contacts: 211, appts: 27, sitdowns: 10, qfd: 6, deals: 2 }, { month: “April”, name: “Andrew Collado”, doors: 890, contacts: 293, appts: 35, sitdowns: 8, qfd: 4, deals: 5 }, { month: “April”, name: “Elijah Jean”, doors: 335, contacts: 83, appts: 16, sitdowns: 3, qfd: 3, deals: 2 }, { month: “April”, name: “Isabella Velez”, doors: 354, contacts: 80, appts: 15, sitdowns: 1, qfd: 1, deals: 1 }, { month: “April”, name: “German Perez”, doors: 790, contacts: 156, appts: 27, sitdowns: 5, qfd: 1, deals: 1 }, { month: “April”, name: “Phillip Hall”, doors: 672, contacts: 239, appts: 51, sitdowns: 17, qfd: 7, deals: 9 }, { month: “April”, name: “Jason Mullen”, doors: 255, contacts: 93, appts: 13, sitdowns: 4, qfd: 2, deals: 1 }, { month: “April”, name: “Eddie Lopez”, doors: 756, contacts: 207, appts: 25, sitdowns: 16, qfd: 12, deals: 10 }, { month: “April”, name: “Brent Aldridge”, doors: 243, contacts: 55, appts: 5, sitdowns: 2, qfd: 1, deals: 1 }, ]; const MONTHS = [“January”, “February”, “March”, “April”]; const REPS = […new Set(RAW.map(d => d.name))]; const COLORS = { “Andrew Collado”: “#38bdf8”, “Elijah Jean”: “#a78bfa”, “Isabella Velez”: “#fb7185”, “German Perez”: “#34d399”, “Phillip Hall”: “#fbbf24”, “Jason Mullen”: “#f97316”, “Eddie Lopez”: “#e879f9”, “Brent Aldridge”: “#94a3b8”, }; function calc(d) { return { …d, doorsDealRatio: d.deals > 0 ? +(d.doors / d.deals).toFixed(1) : null, dealsApptPct: d.appts > 0 ? +(d.deals / d.appts * 100).toFixed(1) : 0, qfdRatePct: d.appts > 0 ? +(d.qfd / d.appts * 100).toFixed(1) : 0, contactRatePct: d.doors > 0 ? +(d.contacts / d.doors * 100).toFixed(1) : 0, rawAppRatePct: d.doors > 0 ? +(d.appts / d.doors * 100).toFixed(1) : 0, appSchedRatePct: d.contacts > 0 ? +(d.appts / d.contacts * 100).toFixed(1) : 0, }; } const DATA = RAW.map(calc); function repMonthly(name) { return MONTHS.map(m => { const d = DATA.find(r => r.name === name && r.month === m); return d ? { month: m.slice(0,3), …d } : { month: m.slice(0,3) }; }); } function teamMonthly() { return MONTHS.map(m => { const rows = DATA.filter(r => r.month === m); const sum = (k) => rows.reduce((s, r) => s + (r[k] || 0), 0); const doors = sum(“doors”), contacts = sum(“contacts”), appts = sum(“appts”), qfd = sum(“qfd”), deals = sum(“deals”); return { month: m.slice(0,3), doors, contacts, appts, deals, contactRatePct: +(contacts/doors*100).toFixed(1), rawAppRatePct: +(appts/doors*100).toFixed(1), appSchedRatePct: +(appts/contacts*100).toFixed(1), dealsApptPct: +(deals/appts*100).toFixed(1), qfdRatePct: +(qfd/appts*100).toFixed(1), doorsDealRatio: deals > 0 ? +(doors/deals).toFixed(1) : null, }; }); } function repTotals() { return REPS.map(name => { const rows = DATA.filter(r => r.name === name); const sum = (k) => rows.reduce((s, r) => s + (r[k] || 0), 0); const doors = sum(“doors”), contacts = sum(“contacts”), appts = sum(“appts”), qfd = sum(“qfd”), deals = sum(“deals”); return { name: name.split(” “)[0], fullName: name, doors, contacts, appts, qfd, deals, contactRatePct: +(contacts/doors*100).toFixed(1), rawAppRatePct: +(appts/doors*100).toFixed(1), appSchedRatePct: +(appts/contacts*100).toFixed(1), dealsApptPct: +(deals/appts*100).toFixed(1), qfdRatePct: +(qfd/appts*100).toFixed(1), doorsDealRatio: deals > 0 ? +(doors/deals).toFixed(1) : null, color: COLORS[name], }; }).sort((a,b) => b.deals - a.deals); } const FMT = { pct: v => `${v}%`, ratio: v => `${v}x`, num: v => v?.toLocaleString(), }; const CustomTooltip = ({ active, payload, label, unit }) => { if (!active || !payload?.length) return null; return ( <div style={{ background: “rgba(10,14,26,0.95)”, border: “1px solid rgba(255,255,255,0.1)”, borderRadius: 8, padding: “10px 14px”, fontSize: 12, fontFamily: “‘DM Mono’, monospace” }}> <div style={{ color: “#94a3b8”, marginBottom: 6, fontWeight: 600 }}>{label}</div> {payload.map((p, i) => ( <div key={i} style={{ color: p.color, display: “flex”, gap: 10, justifyContent: “space-between” }}> <span>{p.name}</span> <span style={{ fontWeight: 700 }}>{p.value != null ? (unit === “pct” ? `${p.value}%` : unit === “ratio” ? `${p.value}` : p.value) : “—”}</span> </div> ))} </div> ); }; const CHART_STYLE = { background: “rgba(255,255,255,0.03)”, borderRadius: 12, border: “1px solid rgba(255,255,255,0.07)”, padding: “18px 8px 8px 8px”, }; const axisStyle = { fill: “#475569”, fontSize: 11, fontFamily: “‘DM Mono’, monospace” }; function SectionLabel({ children }) { return ( <div style={{ fontFamily: “‘Bebas Neue’, sans-serif”, fontSize: 11, letterSpacing: “0.25em”, color: “#38bdf8”, textTransform: “uppercase”, marginBottom: 6 }}>{children}</div> ); } function StatBadge({ label, value, color = “#38bdf8” }) { return ( <div style={{ background: “rgba(255,255,255,0.04)”, borderRadius: 8, border: “1px solid rgba(255,255,255,0.06)”, padding: “10px 14px”, textAlign: “center”, minWidth: 90 }}> <div style={{ fontFamily: “‘DM Mono’, monospace”, fontSize: 18, fontWeight: 700, color }}>{value}</div> <div style={{ fontFamily: “‘DM Mono’, monospace”, fontSize: 10, color: “#64748b”, marginTop: 2 }}>{label}</div> </div> ); } // ── REP CARD ────────────────────────────────────────────────────────────────── function RepCard({ name }) { const monthly = repMonthly(name); const color = COLORS[name]; const totals = useMemo(() => { const rows = DATA.filter(r => r.name === name); const sum = k => rows.reduce((s, r) => s + (r[k] || 0), 0); const doors = sum(“doors”), contacts = sum(“contacts”), appts = sum(“appts”), qfd = sum(“qfd”), deals = sum(“deals”); return { doors, contacts, appts, qfd, deals, contactRatePct: +(contacts/doors*100).toFixed(1), rawAppRatePct: +(appts/doors*100).toFixed(1), appSchedRatePct: +(appts/contacts*100).toFixed(1), dealsApptPct: +(deals/appts*100).toFixed(1), qfdRatePct: +(qfd/appts*100).toFixed(1), doorsDealRatio: deals > 0 ? +(doors/deals).toFixed(1) : “—”, }; }, [name]); const months = monthly.filter(m => m.doors); return ( <div style={{ background: “rgba(255,255,255,0.025)”, border: `1px solid ${color}28`, borderLeft: `3px solid ${color}`, borderRadius: 12, padding: 20, marginBottom: 20, }}> {/* Header */} <div style={{ display: “flex”, alignItems: “center”, gap: 12, marginBottom: 16 }}> <div style={{ width: 36, height: 36, borderRadius: “50%”, background: `${color}22`, border: `2px solid ${color}`, display: “flex”, alignItems: “center”, justifyContent: “center”, fontFamily: “‘Bebas Neue’, sans-serif”, fontSize: 16, color }}>{name.split(” “).map(w=>w[0]).join(””)}</div> <div> <div style={{ fontFamily: “‘Bebas Neue’, sans-serif”, fontSize: 22, color: “#f1f5f9”, letterSpacing: “0.05em” }}>{name}</div> <div style={{ fontFamily: “‘DM Mono’, monospace”, fontSize: 10, color: “#475569” }}> {months.length} month{months.length !== 1 ? “s” : “”} active </div> </div> <div style={{ marginLeft: “auto”, display: “flex”, gap: 8, flexWrap: “wrap”, justifyContent: “flex-end” }}> <StatBadge label="Total Doors" value={totals.doors.toLocaleString()} color="#94a3b8" /> <StatBadge label="Total Deals" value={totals.deals} color={color} /> <StatBadge label="Doors/Deal" value={totals.doorsDealRatio} color="#fbbf24" /> <StatBadge label=“Contact%” value={`${totals.contactRatePct}%`} color=”#34d399” /> <StatBadge label=“App/Deal%” value={`${totals.dealsApptPct}%`} color=”#a78bfa” /> </div> </div> ``` {/* Charts row */} <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}> {/* Chart 1: Doors vs Deals (input/output) */} <div style={CHART_STYLE}> <SectionLabel>Input → Output: Doors vs Deals</SectionLabel> <ResponsiveContainer width="100%" height={160}> <ComposedChart data={months} margin={{ top: 4, right: 8, left: -10, bottom: 0 }}> <CartesianGrid strokeDasharray="3 3" stroke="rgba(255,255,255,0.05)" /> <XAxis dataKey="month" tick={axisStyle} /> <YAxis yAxisId="left" tick={axisStyle} /> <YAxis yAxisId="right" orientation="right" tick={axisStyle} /> <Tooltip content={<CustomTooltip />} /> <Bar yAxisId="left" dataKey="doors" name="Doors" fill={`${color}33`} stroke={color} strokeWidth={1} radius={[3,3,0,0]} /> <Line yAxisId="right" type="monotone" dataKey="deals" name="Deals" stroke="#fbbf24" strokeWidth={2.5} dot={{ fill: "#fbbf24", r: 4 }} /> </ComposedChart> </ResponsiveContainer> </div> {/* Chart 2: Doors/Deal Ratio + Contact Rate */} <div style={CHART_STYLE}> <SectionLabel>Efficiency: Doors/Deal Ratio & Contact Rate</SectionLabel> <ResponsiveContainer width="100%" height={160}> <ComposedChart data={months} margin={{ top: 4, right: 8, left: -10, bottom: 0 }}> <CartesianGrid strokeDasharray="3 3" stroke="rgba(255,255,255,0.05)" /> <XAxis dataKey="month" tick={axisStyle} /> <YAxis yAxisId="left" tick={axisStyle} /> <YAxis yAxisId="right" orientation="right" tick={axisStyle} unit="%" /> <Tooltip content={<CustomTooltip unit="mixed" />} /> <Bar yAxisId="left" dataKey="doorsDealRatio" name="Doors/Deal" fill={`#fbbf2422`} stroke="#fbbf24" strokeWidth={1} radius={[3,3,0,0]} /> <Line yAxisId="right" type="monotone" dataKey="contactRatePct" name="Contact %" stroke="#34d399" strokeWidth={2.5} dot={{ fill: "#34d399", r: 4 }} /> </ComposedChart> </ResponsiveContainer> </div> {/* Chart 3: Scheduling funnel */} <div style={CHART_STYLE}> <SectionLabel>Scheduling: Raw App Rate vs App/Contact Rate</SectionLabel> <ResponsiveContainer width="100%" height={160}> <LineChart data={months} margin={{ top: 4, right: 8, left: -10, bottom: 0 }}> <CartesianGrid strokeDasharray="3 3" stroke="rgba(255,255,255,0.05)" /> <XAxis dataKey="month" tick={axisStyle} /> <YAxis tick={axisStyle} unit="%" /> <Tooltip content={<CustomTooltip unit="pct" />} /> <Line type="monotone" dataKey="rawAppRatePct" name="Raw App Rate" stroke="#38bdf8" strokeWidth={2.5} dot={{ fill: "#38bdf8", r: 4 }} strokeDasharray="5 3" /> <Line type="monotone" dataKey="appSchedRatePct" name="App/Contact Rate" stroke={color} strokeWidth={2.5} dot={{ fill: color, r: 4 }} /> </LineChart> </ResponsiveContainer> </div> {/* Chart 4: QFD Rate & Deals/Appt Rate */} <div style={CHART_STYLE}> <SectionLabel>Conversion: QFD Rate vs Deal Close Rate</SectionLabel> <ResponsiveContainer width="100%" height={160}> <ComposedChart data={months} margin={{ top: 4, right: 8, left: -10, bottom: 0 }}> <CartesianGrid strokeDasharray="3 3" stroke="rgba(255,255,255,0.05)" /> <XAxis dataKey="month" tick={axisStyle} /> <YAxis tick={axisStyle} unit="%" /> <Tooltip content={<CustomTooltip unit="pct" />} /> <Area type="monotone" dataKey="qfdRatePct" name="QFD Rate" fill="#a78bfa22" stroke="#a78bfa" strokeWidth={2} /> <Line type="monotone" dataKey="dealsApptPct" name="Deal Close %" stroke="#fb7185" strokeWidth={2.5} dot={{ fill: "#fb7185", r: 4 }} /> </ComposedChart> </ResponsiveContainer> </div> </div> </div> ``` ); } // ── TEAM OVERVIEW ───────────────────────────────────────────────────────────── function TeamOverview() { const tData = teamMonthly(); const totals = repTotals(); return ( <div style={{ marginBottom: 32 }}> <div style={{ display: “grid”, gridTemplateColumns: “1fr 1fr”, gap: 12, marginBottom: 12 }}> {/* Team Volume */} <div style={CHART_STYLE}> <SectionLabel>Team Volume: Doors, Contacts, Appointments & Deals</SectionLabel> <ResponsiveContainer width="100%" height={180}> <ComposedChart data={tData} margin={{ top: 4, right: 8, left: -10, bottom: 0 }}> <CartesianGrid strokeDasharray="3 3" stroke="rgba(255,255,255,0.05)" /> <XAxis dataKey="month" tick={axisStyle} /> <YAxis yAxisId="left" tick={axisStyle} /> <YAxis yAxisId="right" orientation="right" tick={axisStyle} /> <Tooltip content={<CustomTooltip />} /> <Bar yAxisId="left" dataKey="doors" name="Doors" fill="rgba(56,189,248,0.15)" stroke="#38bdf8" strokeWidth={1} radius={[3,3,0,0]} /> <Bar yAxisId="left" dataKey="contacts" name="Contacts" fill="rgba(52,211,153,0.2)" stroke="#34d399" strokeWidth={1} radius={[3,3,0,0]} /> <Line yAxisId=“right” type=“monotone” dataKey=“appts” name=“Appts” stroke=”#a78bfa” strokeWidth={2} dot={{ r: 3 }} /> <Line yAxisId=“right” type=“monotone” dataKey=“deals” name=“Deals” stroke=”#fbbf24” strokeWidth={3} dot={{ fill: “#fbbf24”, r: 5 }} /> </ComposedChart> </ResponsiveContainer> </div> ``` {/* Team Efficiency Rates */} <div style={CHART_STYLE}> <SectionLabel>Team Efficiency Rates Over Time</SectionLabel> <ResponsiveContainer width="100%" height={180}> <LineChart data={tData} margin={{ top: 4, right: 8, left: -10, bottom: 0 }}> <CartesianGrid strokeDasharray="3 3" stroke="rgba(255,255,255,0.05)" /> <XAxis dataKey="month" tick={axisStyle} /> <YAxis tick={axisStyle} unit="%" /> <Tooltip content={<CustomTooltip unit="pct" />} /> <Line type="monotone" dataKey="contactRatePct" name="Contact %" stroke="#34d399" strokeWidth={2} dot={{ r: 3 }} /> <Line type="monotone" dataKey="rawAppRatePct" name="Raw App %" stroke="#38bdf8" strokeWidth={2} dot={{ r: 3 }} /> <Line type="monotone" dataKey="dealsApptPct" name="Deal Close %" stroke="#fbbf24" strokeWidth={2.5} dot={{ fill: "#fbbf24", r: 4 }} /> <Line type="monotone" dataKey="qfdRatePct" name="QFD %" stroke="#a78bfa" strokeWidth={2} dot={{ r: 3 }} /> </LineChart> </ResponsiveContainer> </div> </div> {/* Rep Comparison Bars */} <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}> <div style={CHART_STYLE}> <SectionLabel>Rep Totals: Deals vs Appointments Booked</SectionLabel> <ResponsiveContainer width="100%" height={200}> <BarChart data={totals} layout="vertical" margin={{ top: 4, right: 30, left: 50, bottom: 0 }}> <CartesianGrid strokeDasharray="3 3" stroke="rgba(255,255,255,0.05)" horizontal={false} /> <XAxis type="number" tick={axisStyle} /> <YAxis dataKey="name" type="category" tick={{ ...axisStyle, fill: "#94a3b8" }} width={48} /> <Tooltip content={<CustomTooltip />} /> <Bar dataKey="appts" name="Appointments" fill="rgba(167,139,250,0.25)" stroke="#a78bfa" strokeWidth={1} radius={[0,3,3,0]} /> <Bar dataKey="deals" name="Deals" radius={[0,3,3,0]} label={{ position: "right", fill: "#fbbf24", fontSize: 11, fontFamily: "'DM Mono', monospace", formatter: v => v }} > {totals.map((t, i) => ( <rect key={i} fill={t.color} /> ))} </Bar> </BarChart> </ResponsiveContainer> </div> <div style={CHART_STYLE}> <SectionLabel>Rep Doors/Deal Ratio (lower = more efficient)</SectionLabel> <ResponsiveContainer width="100%" height={200}> <BarChart data={totals} layout="vertical" margin={{ top: 4, right: 30, left: 50, bottom: 0 }}> <CartesianGrid strokeDasharray="3 3" stroke="rgba(255,255,255,0.05)" horizontal={false} /> <XAxis type="number" tick={axisStyle} /> <YAxis dataKey="name" type="category" tick={{ ...axisStyle, fill: "#94a3b8" }} width={48} /> <Tooltip content={<CustomTooltip unit="ratio" />} /> <Bar dataKey="doorsDealRatio" name="Doors per Deal" radius={[0,3,3,0]} label={{ position: "right", fill: "#94a3b8", fontSize: 11, fontFamily: "'DM Mono', monospace" }} > {totals.map((t, i) => ( <rect key={i} fill={t.color} /> ))} </Bar> </BarChart> </ResponsiveContainer> </div> </div> </div> ``` ); } // ── LEADERBOARD ─────────────────────────────────────────────────────────────── function Leaderboard() { const totals = repTotals(); const cols = [ { key: “deals”, label: “Deals”, fmt: v => v }, { key: “doorsDealRatio”, label: “Doors/Deal”, fmt: v => v ?? “—” }, { key: “contactRatePct”, label: “Contact %”, fmt: v => `${v}%` }, { key: “dealsApptPct”, label: “Deal Close %”, fmt: v => `${v}%` }, { key: “qfdRatePct”, label: “QFD Rate %”, fmt: v => `${v}%` }, { key: “appSchedRatePct”,label: “App/Contact %”, fmt: v => `${v}%` }, ]; return ( <div style={{ …CHART_STYLE, marginBottom: 20, padding: 20 }}> <SectionLabel>Season Leaderboard (YTD)</SectionLabel> <div style={{ overflowX: “auto” }}> <table style={{ width: “100%”, borderCollapse: “collapse”, fontFamily: “‘DM Mono’, monospace”, fontSize: 12 }}> <thead> <tr> <th style={{ textAlign: “left”, color: “#475569”, padding: “8px 10px”, borderBottom: “1px solid rgba(255,255,255,0.08)” }}>#</th> <th style={{ textAlign: “left”, color: “#475569”, padding: “8px 10px”, borderBottom: “1px solid rgba(255,255,255,0.08)” }}>Rep</th> {cols.map(c => ( <th key={c.key} style={{ textAlign: “right”, color: “#475569”, padding: “8px 10px”, borderBottom: “1px solid rgba(255,255,255,0.08)”, whiteSpace: “nowrap” }}>{c.label}</th> ))} </tr> </thead> <tbody> {totals.map((rep, i) => ( <tr key={rep.fullName} style={{ borderBottom: “1px solid rgba(255,255,255,0.04)” }}> <td style={{ padding: “10px”, color: “#475569” }}>{i + 1}</td> <td style={{ padding: “10px” }}> <div style={{ display: “flex”, alignItems: “center”, gap: 8 }}> <div style={{ width: 8, height: 8, borderRadius: “50%”, background: rep.color, flexShrink: 0 }} /> <span style={{ color: “#e2e8f0” }}>{rep.fullName}</span> </div> </td> {cols.map(c => ( <td key={c.key} style={{ textAlign: “right”, padding: “10px”, color: c.key === “deals” ? rep.color : c.key === “doorsDealRatio” ? “#fbbf24” : “#94a3b8”, fontWeight: c.key === “deals” ? 700 : 400 }}> {c.fmt(rep[c.key])} </td> ))} </tr> ))} </tbody> </table> </div> </div> ); } // ── APP ─────────────────────────────────────────────────────────────────────── export default function App() { const [tab, setTab] = useState(“team”); return ( <div style={{ minHeight: “100vh”, background: “#080c18”, backgroundImage: “radial-gradient(ellipse 80% 50% at 50% -20%, rgba(56,189,248,0.08), transparent)”, color: “#f1f5f9”, fontFamily: “‘DM Mono’, monospace”, padding: “0 0 60px 0”, }}> <link href="https://fonts.googleapis.com/css2?family=Bebas+Neue&family=DM+Mono:wght@300;400;500;700&display=swap" rel="stylesheet" /> ``` {/* Header */} <div style={{ borderBottom: "1px solid rgba(255,255,255,0.06)", padding: "20px 28px 16px", display: "flex", alignItems: "center", justifyContent: "space-between", background: "rgba(0,0,0,0.3)", backdropFilter: "blur(12px)", position: "sticky", top: 0, zIndex: 50 }}> <div> <div style={{ fontFamily: "'Bebas Neue', sans-serif", fontSize: 28, letterSpacing: "0.1em", color: "#f1f5f9" }}> FIELD MARKETING <span style={{ color: "#38bdf8" }}>PERFORMANCE</span> </div> <div style={{ fontSize: 10, color: "#475569", letterSpacing: "0.2em", textTransform: "uppercase" }}> January – April 2025 · Monthly Review </div> </div> <div style={{ display: "flex", gap: 6 }}> {[["team", "Team Overview"], ...REPS.map(r => [r, r.split(" ")[0]])].map(([key, label]) => ( <button key={key} onClick={() => setTab(key)} style={{ background: tab === key ? "#38bdf8" : "rgba(255,255,255,0.05)", color: tab === key ? "#080c18" : "#64748b", border: "1px solid " + (tab === key ? "#38bdf8" : "rgba(255,255,255,0.08)"), borderRadius: 6, padding: "5px 12px", fontSize: 11, fontFamily: "'DM Mono', monospace", cursor: "pointer", fontWeight: tab === key ? 700 : 400, transition: "all 0.15s", whiteSpace: "nowrap" }}>{label}</button> ))} </div> </div> {/* Content */} <div style={{ padding: "24px 28px" }}> {tab === "team" ? ( <> <div style={{ fontFamily: "'Bebas Neue', sans-serif", fontSize: 20, color: "#38bdf8", letterSpacing: "0.15em", marginBottom: 16 }}> TEAM SUMMARY </div> <Leaderboard /> <TeamOverview /> </> ) : ( <RepCard name={tab} /> )} </div> </div> ``` ); }
I'll implement this sales performance dashboard in your project. Let me install the required dependency and create the page.
Now let me update the layout with the required fonts and create the dashboard page:
I've implemented your Field Marketing Performance dashboard. The dashboard includes:
Rep Cards - Individual performance cards for each sales rep with 4 focused charts:
The code has been converted to TypeScript, uses Tailwind CSS classes instead of inline styles where practical, and is fully responsive for mobile devices. The custom fonts (Bebas Neue and DM Mono) are properly loaded via Next.js font optimization.
import { useState, useMemo } from “react”; import { LineChart, Line, BarChart, Bar, ComposedChart, XAxis, YAxis, Tooltip, Legend, ResponsiveContainer, CartesianGrid, Area, AreaChart, ReferenceLine } from “recharts”; const RAW = [ { month: “January”, name: “Andrew Collado”, doors: 1091, contacts: 358, appts: 30, sitdowns: 6, qfd: 2, deals: 2 }, { month: “January”, name: “Elijah Jean”, doors: 451, contacts: 116, appts: 11, sitdowns: 2, qfd: 2, deals: 2 }, { month: “January”, name: “Isabella Velez”, doors: 305, contacts: 49, appts: 11, sitdowns: 1, qfd: 1, deals: 1 }, { month: “February”, name: “Andrew Collado”, doors: 994, contacts: 357, appts: 34, sitdowns: 11, qfd: 5, deals: 2 }, { month: “February”, name: “Elijah Jean”, doors: 662, contacts: 175, appts: 18, sitdowns: 6, qfd: 2, deals: 2 }, { month: “February”, name: “Isabella Velez”, doors: 389, contacts: 73, appts: 22, sitdowns: 1, qfd: 1, deals: 1 }, { month: “February”, name: “German Perez”, doors: 955, contacts: 99, appts: 22, sitdowns: 12, qfd: 3, deals: 5 }, { month: “February”, name: “Phillip Hall”, doors: 751, contacts: 170, appts: 23, sitdowns: 5, qfd: 3, deals: 1 }, { month: “February”, name: “Jason Mullen”, doors: 437, contacts: 157, appts: 25, sitdowns: 5, qfd: 3, deals: 2 }, { month: “February”, name: “Eddie Lopez”, doors: 743, contacts: 178, appts: 24, sitdowns: 8, qfd: 6, deals: 3 }, { month: “March”, name: “Andrew Collado”, doors: 978, contacts: 407, appts: 39, sitdowns: 8, qfd: 3, deals: 1 }, { month: “March”, name: “Elijah Jean”, doors: 487, contacts: 154, appts: 20, sitdowns: 2, qfd: 2, deals: 2 }, { month: “March”, name: “Isabella Velez”, doors: 382, contacts: 92, appts: 28, sitdowns: 7, qfd: 4, deals: 2 }, { month: “March”, name: “German Perez”, doors: 856, contacts: 142, appts: 28, sitdowns: 15, qfd: 3, deals: 3 }, { month: “March”, name: “Phillip Hall”, doors: 831, contacts: 219, appts: 52, sitdowns: 12, qfd: 7, deals: 6 }, { month: “March”, name: “Jason Mullen”, doors: 262, contacts: 88, appts: 17, sitdowns: 5, qfd: 4, deals: 3 }, { month: “March”, name: “Eddie Lopez”, doors: 729, contacts: 211, appts: 27, sitdowns: 10, qfd: 6, deals: 2 }, { month: “April”, name: “Andrew Collado”, doors: 890, contacts: 293, appts: 35, sitdowns: 8, qfd: 4, deals: 5 }, { month: “April”, name: “Elijah Jean”, doors: 335, contacts: 83, appts: 16, sitdowns: 3, qfd: 3, deals: 2 }, { month: “April”, name: “Isabella Velez”, doors: 354, contacts: 80, appts: 15, sitdowns: 1, qfd: 1, deals: 1 }, { month: “April”, name: “German Perez”, doors: 790, contacts: 156, appts: 27, sitdowns: 5, qfd: 1, deals: 1 }, { month: “April”, name: “Phillip Hall”, doors: 672, contacts: 239, appts: 51, sitdowns: 17, qfd: 7, deals: 9 }, { month: “April”, name: “Jason Mullen”, doors: 255, contacts: 93, appts: 13, sitdowns: 4, qfd: 2, deals: 1 }, { month: “April”, name: “Eddie Lopez”, doors: 756, contacts: 207, appts: 25, sitdowns: 16, qfd: 12, deals: 10 }, { month: “April”, name: “Brent Aldridge”, doors: 243, contacts: 55, appts: 5, sitdowns: 2, qfd: 1, deals: 1 }, ]; const MONTHS = [“January”, “February”, “March”, “April”]; const REPS = […new Set(RAW.map(d => d.name))]; const COLORS = { “Andrew Collado”: “#38bdf8”, “Elijah Jean”: “#a78bfa”, “Isabella Velez”: “#fb7185”, “German Perez”: “#34d399”, “Phillip Hall”: “#fbbf24”, “Jason Mullen”: “#f97316”, “Eddie Lopez”: “#e879f9”, “Brent Aldridge”: “#94a3b8”, }; function calc(d) { return { …d, doorsDealRatio: d.deals > 0 ? +(d.doors / d.deals).toFixed(1) : null, dealsApptPct: d.appts > 0 ? +(d.deals / d.appts * 100).toFixed(1) : 0, qfdRatePct: d.appts > 0 ? +(d.qfd / d.appts * 100).toFixed(1) : 0, contactRatePct: d.doors > 0 ? +(d.contacts / d.doors * 100).toFixed(1) : 0, rawAppRatePct: d.doors > 0 ? +(d.appts / d.doors * 100).toFixed(1) : 0, appSchedRatePct: d.contacts > 0 ? +(d.appts / d.contacts * 100).toFixed(1) : 0, }; } const DATA = RAW.map(calc); function repMonthly(name) { return MONTHS.map(m => { const d = DATA.find(r => r.name === name && r.month === m); return d ? { month: m.slice(0,3), …d } : { month: m.slice(0,3) }; }); } function teamMonthly() { return MONTHS.map(m => { const rows = DATA.filter(r => r.month === m); const sum = (k) => rows.reduce((s, r) => s + (r[k] || 0), 0); const doors = sum(“doors”), contacts = sum(“contacts”), appts = sum(“appts”), qfd = sum(“qfd”), deals = sum(“deals”); return { month: m.slice(0,3), doors, contacts, appts, deals, contactRatePct: +(contacts/doors*100).toFixed(1), rawAppRatePct: +(appts/doors*100).toFixed(1), appSchedRatePct: +(appts/contacts*100).toFixed(1), dealsApptPct: +(deals/appts*100).toFixed(1), qfdRatePct: +(qfd/appts*100).toFixed(1), doorsDealRatio: deals > 0 ? +(doors/deals).toFixed(1) : null, }; }); } function repTotals() { return REPS.map(name => { const rows = DATA.filter(r => r.name === name); const sum = (k) => rows.reduce((s, r) => s + (r[k] || 0), 0); const doors = sum(“doors”), contacts = sum(“contacts”), appts = sum(“appts”), qfd = sum(“qfd”), deals = sum(“deals”); return { name: name.split(” “)[0], fullName: name, doors, contacts, appts, qfd, deals, contactRatePct: +(contacts/doors*100).toFixed(1), rawAppRatePct: +(appts/doors*100).toFixed(1), appSchedRatePct: +(appts/contacts*100).toFixed(1), dealsApptPct: +(deals/appts*100).toFixed(1), qfdRatePct: +(qfd/appts*100).toFixed(1), doorsDealRatio: deals > 0 ? +(doors/deals).toFixed(1) : null, color: COLORS[name], }; }).sort((a,b) => b.deals - a.deals); } const FMT = { pct: v => `${v}%`, ratio: v => `${v}x`, num: v => v?.toLocaleString(), }; const CustomTooltip = ({ active, payload, label, unit }) => { if (!active || !payload?.length) return null; return ( <div style={{ background: “rgba(10,14,26,0.95)”, border: “1px solid rgba(255,255,255,0.1)”, borderRadius: 8, padding: “10px 14px”, fontSize: 12, fontFamily: “‘DM Mono’, monospace” }}> <div style={{ color: “#94a3b8”, marginBottom: 6, fontWeight: 600 }}>{label}</div> {payload.map((p, i) => ( <div key={i} style={{ color: p.color, display: “flex”, gap: 10, justifyContent: “space-between” }}> <span>{p.name}</span> <span style={{ fontWeight: 700 }}>{p.value != null ? (unit === “pct” ? `${p.value}%` : unit === “ratio” ? `${p.value}` : p.value) : “—”}</span> </div> ))} </div> ); }; const CHART_STYLE = { background: “rgba(255,255,255,0.03)”, borderRadius: 12, border: “1px solid rgba(255,255,255,0.07)”, padding: “18px 8px 8px 8px”, }; const axisStyle = { fill: “#475569”, fontSize: 11, fontFamily: “‘DM Mono’, monospace” }; function SectionLabel({ children }) { return ( <div style={{ fontFamily: “‘Bebas Neue’, sans-serif”, fontSize: 11, letterSpacing: “0.25em”, color: “#38bdf8”, textTransform: “uppercase”, marginBottom: 6 }}>{children}</div> ); } function StatBadge({ label, value, color = “#38bdf8” }) { return ( <div style={{ background: “rgba(255,255,255,0.04)”, borderRadius: 8, border: “1px solid rgba(255,255,255,0.06)”, padding: “10px 14px”, textAlign: “center”, minWidth: 90 }}> <div style={{ fontFamily: “‘DM Mono’, monospace”, fontSize: 18, fontWeight: 700, color }}>{value}</div> <div style={{ fontFamily: “‘DM Mono’, monospace”, fontSize: 10, color: “#64748b”, marginTop: 2 }}>{label}</div> </div> ); } // ── REP CARD ────────────────────────────────────────────────────────────────── function RepCard({ name }) { const monthly = repMonthly(name); const color = COLORS[name]; const totals = useMemo(() => { const rows = DATA.filter(r => r.name === name); const sum = k => rows.reduce((s, r) => s + (r[k] || 0), 0); const doors = sum(“doors”), contacts = sum(“contacts”), appts = sum(“appts”), qfd = sum(“qfd”), deals = sum(“deals”); return { doors, contacts, appts, qfd, deals, contactRatePct: +(contacts/doors*100).toFixed(1), rawAppRatePct: +(appts/doors*100).toFixed(1), appSchedRatePct: +(appts/contacts*100).toFixed(1), dealsApptPct: +(deals/appts*100).toFixed(1), qfdRatePct: +(qfd/appts*100).toFixed(1), doorsDealRatio: deals > 0 ? +(doors/deals).toFixed(1) : “—”, }; }, [name]); const months = monthly.filter(m => m.doors); return ( <div style={{ background: “rgba(255,255,255,0.025)”, border: `1px solid ${color}28`, borderLeft: `3px solid ${color}`, borderRadius: 12, padding: 20, marginBottom: 20, }}> {/* Header */} <div style={{ display: “flex”, alignItems: “center”, gap: 12, marginBottom: 16 }}> <div style={{ width: 36, height: 36, borderRadius: “50%”, background: `${color}22`, border: `2px solid ${color}`, display: “flex”, alignItems: “center”, justifyContent: “center”, fontFamily: “‘Bebas Neue’, sans-serif”, fontSize: 16, color }}>{name.split(” “).map(w=>w[0]).join(””)}</div> <div> <div style={{ fontFamily: “‘Bebas Neue’, sans-serif”, fontSize: 22, color: “#f1f5f9”, letterSpacing: “0.05em” }}>{name}</div> <div style={{ fontFamily: “‘DM Mono’, monospace”, fontSize: 10, color: “#475569” }}> {months.length} month{months.length !== 1 ? “s” : “”} active </div> </div> <div style={{ marginLeft: “auto”, display: “flex”, gap: 8, flexWrap: “wrap”, justifyContent: “flex-end” }}> <StatBadge label="Total Doors" value={totals.doors.toLocaleString()} color="#94a3b8" /> <StatBadge label="Total Appts" value={totals.appts} color="#a78bfa" /> <StatBadge label="Total Deals" value={totals.deals} color={color} /> <StatBadge label="Doors/Deal" value={totals.doorsDealRatio} color="#fbbf24" /> <StatBadge label=“Contact%” value={`${totals.contactRatePct}%`} color=”#34d399” /> <StatBadge label=“App/Deal%” value={`${totals.dealsApptPct}%`} color=”#a78bfa” /> </div> </div> ``` {/* Full funnel span chart */} <div style={{ marginBottom: 12 }}> <div style={CHART_STYLE}> <SectionLabel>Full Funnel: Doors → Appointments → Deals</SectionLabel> <ResponsiveContainer width="100%" height={160}> <ComposedChart data={months} margin={{ top: 4, right: 8, left: -10, bottom: 0 }}> <CartesianGrid strokeDasharray="3 3" stroke="rgba(255,255,255,0.05)" /> <XAxis dataKey="month" tick={axisStyle} /> <YAxis yAxisId="left" tick={axisStyle} /> <YAxis yAxisId="right" orientation="right" tick={axisStyle} /> <Tooltip content={<CustomTooltip />} /> <Legend wrapperStyle={{ fontSize: 11, fontFamily: "'DM Mono', monospace", color: "#64748b" }} /> <Bar yAxisId="left" dataKey="doors" name="Doors" fill={`${color}22`} stroke={color} strokeWidth={1} radius={[3,3,0,0]} /> <Line yAxisId="right" type="monotone" dataKey="appts" name="Appointments" stroke="#a78bfa" strokeWidth={2.5} dot={{ fill: "#a78bfa", r: 4 }} /> <Line yAxisId="right" type="monotone" dataKey="deals" name="Deals" stroke="#fbbf24" strokeWidth={2.5} dot={{ fill: "#fbbf24", r: 4 }} /> </ComposedChart> </ResponsiveContainer> </div> </div> {/* Charts row */} <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}> {/* Chart 1: Appts vs Deals monthly */} <div style={CHART_STYLE}> <SectionLabel>Appointments Booked vs Deals Closed</SectionLabel> <ResponsiveContainer width="100%" height={160}> <ComposedChart data={months} margin={{ top: 4, right: 8, left: -10, bottom: 0 }}> <CartesianGrid strokeDasharray="3 3" stroke="rgba(255,255,255,0.05)" /> <XAxis dataKey="month" tick={axisStyle} /> <YAxis tick={axisStyle} /> <Tooltip content={<CustomTooltip />} /> <Bar dataKey="appts" name="Appointments" fill="rgba(167,139,250,0.2)" stroke="#a78bfa" strokeWidth={1} radius={[3,3,0,0]} /> <Line type="monotone" dataKey="deals" name="Deals" stroke="#fbbf24" strokeWidth={2.5} dot={{ fill: "#fbbf24", r: 4 }} /> </ComposedChart> </ResponsiveContainer> </div> {/* Chart 2: Doors/Deal Ratio + Contact Rate */} <div style={CHART_STYLE}> <SectionLabel>Efficiency: Doors/Deal Ratio & Contact Rate</SectionLabel> <ResponsiveContainer width="100%" height={160}> <ComposedChart data={months} margin={{ top: 4, right: 8, left: -10, bottom: 0 }}> <CartesianGrid strokeDasharray="3 3" stroke="rgba(255,255,255,0.05)" /> <XAxis dataKey="month" tick={axisStyle} /> <YAxis yAxisId="left" tick={axisStyle} /> <YAxis yAxisId="right" orientation="right" tick={axisStyle} unit="%" /> <Tooltip content={<CustomTooltip unit="mixed" />} /> <Bar yAxisId="left" dataKey="doorsDealRatio" name="Doors/Deal" fill={`#fbbf2422`} stroke="#fbbf24" strokeWidth={1} radius={[3,3,0,0]} /> <Line yAxisId="right" type="monotone" dataKey="contactRatePct" name="Contact %" stroke="#34d399" strokeWidth={2.5} dot={{ fill: "#34d399", r: 4 }} /> </ComposedChart> </ResponsiveContainer> </div> {/* Chart 3: Scheduling funnel */} <div style={CHART_STYLE}> <SectionLabel>Scheduling: Raw App Rate vs App/Contact Rate</SectionLabel> <ResponsiveContainer width="100%" height={160}> <LineChart data={months} margin={{ top: 4, right: 8, left: -10, bottom: 0 }}> <CartesianGrid strokeDasharray="3 3" stroke="rgba(255,255,255,0.05)" /> <XAxis dataKey="month" tick={axisStyle} /> <YAxis tick={axisStyle} unit="%" /> <Tooltip content={<CustomTooltip unit="pct" />} /> <Line type="monotone" dataKey="rawAppRatePct" name="Raw App Rate" stroke="#38bdf8" strokeWidth={2.5} dot={{ fill: "#38bdf8", r: 4 }} strokeDasharray="5 3" /> <Line type="monotone" dataKey="appSchedRatePct" name="App/Contact Rate" stroke={color} strokeWidth={2.5} dot={{ fill: color, r: 4 }} /> </LineChart> </ResponsiveContainer> </div> {/* Chart 4: QFD Rate & Deals/Appt Rate */} <div style={CHART_STYLE}> <SectionLabel>Conversion: QFD Rate vs Deal Close Rate</SectionLabel> <ResponsiveContainer width="100%" height={160}> <ComposedChart data={months} margin={{ top: 4, right: 8, left: -10, bottom: 0 }}> <CartesianGrid strokeDasharray="3 3" stroke="rgba(255,255,255,0.05)" /> <XAxis dataKey="month" tick={axisStyle} /> <YAxis tick={axisStyle} unit="%" /> <Tooltip content={<CustomTooltip unit="pct" />} /> <Area type="monotone" dataKey="qfdRatePct" name="QFD Rate" fill="#a78bfa22" stroke="#a78bfa" strokeWidth={2} /> <Line type="monotone" dataKey="dealsApptPct" name="Deal Close %" stroke="#fb7185" strokeWidth={2.5} dot={{ fill: "#fb7185", r: 4 }} /> </ComposedChart> </ResponsiveContainer> </div> </div> </div> ``` ); } // ── TEAM OVERVIEW ───────────────────────────────────────────────────────────── function TeamOverview() { const tData = teamMonthly(); const totals = repTotals(); return ( <div style={{ marginBottom: 32 }}> <div style={{ display: “grid”, gridTemplateColumns: “1fr 1fr”, gap: 12, marginBottom: 12 }}> {/* Team Volume */} <div style={CHART_STYLE}> <SectionLabel>Team Volume: Doors, Contacts, Appointments & Deals</SectionLabel> <ResponsiveContainer width="100%" height={180}> <ComposedChart data={tData} margin={{ top: 4, right: 8, left: -10, bottom: 0 }}> <CartesianGrid strokeDasharray="3 3" stroke="rgba(255,255,255,0.05)" /> <XAxis dataKey="month" tick={axisStyle} /> <YAxis yAxisId="left" tick={axisStyle} /> <YAxis yAxisId="right" orientation="right" tick={axisStyle} /> <Tooltip content={<CustomTooltip />} /> <Bar yAxisId="left" dataKey="doors" name="Doors" fill="rgba(56,189,248,0.15)" stroke="#38bdf8" strokeWidth={1} radius={[3,3,0,0]} /> <Bar yAxisId="left" dataKey="contacts" name="Contacts" fill="rgba(52,211,153,0.2)" stroke="#34d399" strokeWidth={1} radius={[3,3,0,0]} /> <Line yAxisId=“right” type=“monotone” dataKey=“appts” name=“Appts” stroke=”#a78bfa” strokeWidth={2} dot={{ r: 3 }} /> <Line yAxisId=“right” type=“monotone” dataKey=“deals” name=“Deals” stroke=”#fbbf24” strokeWidth={3} dot={{ fill: “#fbbf24”, r: 5 }} /> </ComposedChart> </ResponsiveContainer> </div> ``` {/* Team Appts vs Deals trend */} <div style={CHART_STYLE}> <SectionLabel>Team Appointments → Deals Conversion Trend</SectionLabel> <ResponsiveContainer width="100%" height={180}> <ComposedChart data={tData} margin={{ top: 4, right: 8, left: -10, bottom: 0 }}> <CartesianGrid strokeDasharray="3 3" stroke="rgba(255,255,255,0.05)" /> <XAxis dataKey="month" tick={axisStyle} /> <YAxis yAxisId="left" tick={axisStyle} /> <YAxis yAxisId="right" orientation="right" tick={axisStyle} unit="%" /> <Tooltip content={<CustomTooltip />} /> <Bar yAxisId="left" dataKey="appts" name="Appointments" fill="rgba(167,139,250,0.2)" stroke="#a78bfa" strokeWidth={1} radius={[3,3,0,0]} /> <Line yAxisId="left" type="monotone" dataKey="deals" name="Deals" stroke="#fbbf24" strokeWidth={2.5} dot={{ fill: "#fbbf24", r: 4 }} /> <Line yAxisId="right" type="monotone" dataKey="dealsApptPct" name="Close %" stroke="#fb7185" strokeWidth={2} strokeDasharray="4 3" dot={{ r: 3 }} /> </ComposedChart> </ResponsiveContainer> </div> </div> <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12, marginBottom: 12 }}> <div style={CHART_STYLE}> <SectionLabel>Team Efficiency Rates Over Time</SectionLabel> <ResponsiveContainer width="100%" height={180}> <LineChart data={tData} margin={{ top: 4, right: 8, left: -10, bottom: 0 }}> <CartesianGrid strokeDasharray="3 3" stroke="rgba(255,255,255,0.05)" /> <XAxis dataKey="month" tick={axisStyle} /> <YAxis tick={axisStyle} unit="%" /> <Tooltip content={<CustomTooltip unit="pct" />} /> <Legend wrapperStyle={{ fontSize: 11, fontFamily: "'DM Mono', monospace", color: "#64748b" }} /> <Line type="monotone" dataKey="contactRatePct" name="Contact %" stroke="#34d399" strokeWidth={2} dot={{ r: 3 }} /> <Line type="monotone" dataKey="rawAppRatePct" name="Raw App %" stroke="#38bdf8" strokeWidth={2} dot={{ r: 3 }} /> <Line type="monotone" dataKey="dealsApptPct" name="Deal Close %" stroke="#fbbf24" strokeWidth={2.5} dot={{ fill: "#fbbf24", r: 4 }} /> <Line type="monotone" dataKey="qfdRatePct" name="QFD %" stroke="#a78bfa" strokeWidth={2} dot={{ r: 3 }} /> </LineChart> </ResponsiveContainer> </div> <div style={CHART_STYLE}> <SectionLabel>Team App Scheduling Rate vs Close Rate</SectionLabel> <ResponsiveContainer width="100%" height={180}> <ComposedChart data={tData} margin={{ top: 4, right: 8, left: -10, bottom: 0 }}> <CartesianGrid strokeDasharray="3 3" stroke="rgba(255,255,255,0.05)" /> <XAxis dataKey="month" tick={axisStyle} /> <YAxis yAxisId="left" tick={axisStyle} /> <YAxis yAxisId="right" orientation="right" tick={axisStyle} unit="%" /> <Tooltip content={<CustomTooltip />} /> <Legend wrapperStyle={{ fontSize: 11, fontFamily: "'DM Mono', monospace", color: "#64748b" }} /> <Bar yAxisId="left" dataKey="appts" name="Appointments" fill="rgba(167,139,250,0.15)" stroke="#a78bfa" strokeWidth={1} radius={[3,3,0,0]} /> <Line yAxisId="right" type="monotone" dataKey="appSchedRatePct" name="App/Contact %" stroke="#38bdf8" strokeWidth={2} dot={{ r: 3 }} /> <Line yAxisId="right" type="monotone" dataKey="dealsApptPct" name="Close %" stroke="#fbbf24" strokeWidth={2.5} dot={{ fill: "#fbbf24", r: 4 }} /> </ComposedChart> </ResponsiveContainer> </div> </div> {/* Rep Comparison Bars */} <div style={{ display: "grid", gridTemplateColumns: "1fr 1fr", gap: 12 }}> <div style={CHART_STYLE}> <SectionLabel>Rep Totals: Appointments Booked & Deals Closed</SectionLabel> <ResponsiveContainer width="100%" height={220}> <BarChart data={totals} layout="vertical" margin={{ top: 4, right: 40, left: 50, bottom: 0 }}> <CartesianGrid strokeDasharray="3 3" stroke="rgba(255,255,255,0.05)" horizontal={false} /> <XAxis type="number" tick={axisStyle} /> <YAxis dataKey="name" type="category" tick={{ ...axisStyle, fill: "#94a3b8" }} width={48} /> <Tooltip content={<CustomTooltip />} /> <Legend wrapperStyle={{ fontSize: 11, fontFamily: "'DM Mono', monospace", color: "#64748b" }} /> <Bar dataKey="appts" name="Appointments" fill="rgba(167,139,250,0.2)" stroke="#a78bfa" strokeWidth={1} radius={[0,3,3,0]} /> <Bar dataKey="deals" name="Deals" radius={[0,3,3,0]} label={{ position: "right", fill: "#fbbf24", fontSize: 11, fontFamily: "'DM Mono', monospace", formatter: v => v }} > {totals.map((t, i) => ( <rect key={i} fill={t.color} /> ))} </Bar> </BarChart> </ResponsiveContainer> </div> <div style={CHART_STYLE}> <SectionLabel>Rep Doors/Deal Ratio (lower = more efficient)</SectionLabel> <ResponsiveContainer width="100%" height={220}> <BarChart data={totals} layout="vertical" margin={{ top: 4, right: 30, left: 50, bottom: 0 }}> <CartesianGrid strokeDasharray="3 3" stroke="rgba(255,255,255,0.05)" horizontal={false} /> <XAxis type="number" tick={axisStyle} /> <YAxis dataKey="name" type="category" tick={{ ...axisStyle, fill: "#94a3b8" }} width={48} /> <Tooltip content={<CustomTooltip unit="ratio" />} /> <Bar dataKey="doorsDealRatio" name="Doors per Deal" radius={[0,3,3,0]} label={{ position: "right", fill: "#94a3b8", fontSize: 11, fontFamily: "'DM Mono', monospace" }} > {totals.map((t, i) => ( <rect key={i} fill={t.color} /> ))} </Bar> </BarChart> </ResponsiveContainer> </div> </div> </div> ``` ); } // ── LEADERBOARD ─────────────────────────────────────────────────────────────── function Leaderboard() { const totals = repTotals(); const cols = [ { key: “appts”, label: “Appts”, fmt: v => v }, { key: “deals”, label: “Deals”, fmt: v => v }, { key: “doorsDealRatio”, label: “Doors/Deal”, fmt: v => v ?? “—” }, { key: “contactRatePct”, label: “Contact %”, fmt: v => `${v}%` }, { key: “dealsApptPct”, label: “Deal Close %”, fmt: v => `${v}%` }, { key: “qfdRatePct”, label: “QFD Rate %”, fmt: v => `${v}%` }, { key: “appSchedRatePct”,label: “App/Contact %”, fmt: v => `${v}%` }, ]; return ( <div style={{ …CHART_STYLE, marginBottom: 20, padding: 20 }}> <SectionLabel>Season Leaderboard (YTD)</SectionLabel> <div style={{ overflowX: “auto” }}> <table style={{ width: “100%”, borderCollapse: “collapse”, fontFamily: “‘DM Mono’, monospace”, fontSize: 12 }}> <thead> <tr> <th style={{ textAlign: “left”, color: “#475569”, padding: “8px 10px”, borderBottom: “1px solid rgba(255,255,255,0.08)” }}>#</th> <th style={{ textAlign: “left”, color: “#475569”, padding: “8px 10px”, borderBottom: “1px solid rgba(255,255,255,0.08)” }}>Rep</th> {cols.map(c => ( <th key={c.key} style={{ textAlign: “right”, color: “#475569”, padding: “8px 10px”, borderBottom: “1px solid rgba(255,255,255,0.08)”, whiteSpace: “nowrap” }}>{c.label}</th> ))} </tr> </thead> <tbody> {totals.map((rep, i) => ( <tr key={rep.fullName} style={{ borderBottom: “1px solid rgba(255,255,255,0.04)” }}> <td style={{ padding: “10px”, color: “#475569” }}>{i + 1}</td> <td style={{ padding: “10px” }}> <div style={{ display: “flex”, alignItems: “center”, gap: 8 }}> <div style={{ width: 8, height: 8, borderRadius: “50%”, background: rep.color, flexShrink: 0 }} /> <span style={{ color: “#e2e8f0” }}>{rep.fullName}</span> </div> </td> {cols.map(c => ( <td key={c.key} style={{ textAlign: “right”, padding: “10px”, color: c.key === “deals” ? rep.color : c.key === “appts” ? “#a78bfa” : c.key === “doorsDealRatio” ? “#fbbf24” : “#94a3b8”, fontWeight: c.key === “deals” || c.key === “appts” ? 700 : 400 }}> {c.fmt(rep[c.key])} </td> ))} </tr> ))} </tbody> </table> </div> </div> ); } // ── APP ─────────────────────────────────────────────────────────────────────── export default function App() { const [tab, setTab] = useState(“team”); return ( <div style={{ minHeight: “100vh”, background: “#080c18”, backgroundImage: “radial-gradient(ellipse 80% 50% at 50% -20%, rgba(56,189,248,0.08), transparent)”, color: “#f1f5f9”, fontFamily: “‘DM Mono’, monospace”, padding: “0 0 60px 0”, }}> <link href="https://fonts.googleapis.com/css2?family=Bebas+Neue&family=DM+Mono:wght@300;400;500;700&display=swap" rel="stylesheet" /> ``` {/* Header */} <div style={{ borderBottom: "1px solid rgba(255,255,255,0.06)", padding: "20px 28px 16px", display: "flex", alignItems: "center", justifyContent: "space-between", background: "rgba(0,0,0,0.3)", backdropFilter: "blur(12px)", position: "sticky", top: 0, zIndex: 50 }}> <div> <div style={{ fontFamily: "'Bebas Neue', sans-serif", fontSize: 28, letterSpacing: "0.1em", color: "#f1f5f9" }}> FIELD MARKETING <span style={{ color: "#38bdf8" }}>PERFORMANCE</span> </div> <div style={{ fontSize: 10, color: "#475569", letterSpacing: "0.2em", textTransform: "uppercase" }}> January – April 2025 · Monthly Review </div> </div> <div style={{ display: "flex", gap: 6 }}> {[["team", "Team Overview"], ...REPS.map(r => [r, r.split(" ")[0]])].map(([key, label]) => ( <button key={key} onClick={() => setTab(key)} style={{ background: tab === key ? "#38bdf8" : "rgba(255,255,255,0.05)", color: tab === key ? "#080c18" : "#64748b", border: "1px solid " + (tab === key ? "#38bdf8" : "rgba(255,255,255,0.08)"), borderRadius: 6, padding: "5px 12px", fontSize: 11, fontFamily: "'DM Mono', monospace", cursor: "pointer", fontWeight: tab === key ? 700 : 400, transition: "all 0.15s", whiteSpace: "nowrap" }}>{label}</button> ))} </div> </div> {/* Content */} <div style={{ padding: "24px 28px" }}> {tab === "team" ? ( <> <div style={{ fontFamily: "'Bebas Neue', sans-serif", fontSize: 20, color: "#38bdf8", letterSpacing: "0.15em", marginBottom: 16 }}> TEAM SUMMARY </div> <Leaderboard /> <TeamOverview /> </> ) : ( <RepCard name={tab} /> )} </div> </div> ``` ); }
Now I'll update the file with the new changes. The key updates are:
I've updated the dashboard with all the requested changes: