diff --git a/src/components/dashboard/TopSalesLeaderboard.jsx b/src/components/dashboard/TopSalesLeaderboard.jsx index 8e17ef2..a951965 100644 --- a/src/components/dashboard/TopSalesLeaderboard.jsx +++ b/src/components/dashboard/TopSalesLeaderboard.jsx @@ -1,18 +1,15 @@ -import React from 'react'; +import React, { useMemo } from 'react'; import { SpotlightCard } from '../SpotlightCard'; import { Trophy, ChevronRight, User } from 'lucide-react'; import { Link } from 'react-router-dom'; +import { useMockStore } from '../../data/mockStore'; +import { repLeaderboard } from '../../data/selectors'; -const MOCK_TOP_REPS = [ - { id: 1, name: "Sarah Connor", sales: "$1.2M", deals: 42, avatar: null }, - { id: 2, name: "Michael Ross", sales: "$980K", deals: 35, avatar: null }, - { id: 3, name: "Jessica Pearson", sales: "$850K", deals: 28, avatar: null }, - { id: 4, name: "Harvey Specter", sales: "$720K", deals: 24, avatar: null }, - { id: 5, name: "Louis Litt", sales: "$640K", deals: 20, avatar: null }, - { id: 6, name: "Donna Paulsen", sales: "$590K", deals: 18, avatar: null }, -]; +const fmtSales = (n) => n >= 1_000_000 ? `$${(n / 1_000_000).toFixed(1)}M` : `$${Math.round(n / 1000)}K`; export const TopSalesLeaderboard = () => { + const { projects, resolvePerson } = useMockStore(); + const reps = useMemo(() => repLeaderboard(projects, resolvePerson), [projects, resolvePerson]); return (
@@ -32,7 +29,7 @@ export const TopSalesLeaderboard = () => {
- {MOCK_TOP_REPS.map((rep, index) => ( + {reps.map((rep, index) => ( {
- {rep.sales} + {fmtSales(rep.sales)}
diff --git a/src/data/selectors.js b/src/data/selectors.js index 789b794..731bb3e 100644 --- a/src/data/selectors.js +++ b/src/data/selectors.js @@ -144,3 +144,22 @@ export function financialOverview(projects = [], today) { } return { collected, outstandingAR, pendingPayouts, vendorSpend }; } + +// Top performers leaderboard — credits each project to its closer (canvasser/sales rep on the team), +// ranked by sales (Σ contract value). Returns [{ id, name, deals, sales, commission }]. +export function repLeaderboard(projects = [], resolvePerson) { + const by = {}; + for (const p of projects) { + const members = p.teamMembers || []; + const closer = members.find((t) => /Canvasser|Sales|Estimator/i.test(t.jobRole || '')) + || members.find((t) => /FIELD_AGENT|SALES/i.test(t.orgRole || '')); + const id = closer?.userId || p.salesRepId; + if (!id) continue; + const name = closer?.name || resolvePerson?.(id)?.name || id; + by[id] = by[id] || { id, name, deals: 0, sales: 0, commission: 0 }; + by[id].deals += 1; + by[id].sales += Number(p.approvedBudget ?? p.budget ?? 0); + by[id].commission += Number(p.commission || 0); + } + return Object.values(by).sort((a, b) => b.sales - a.sales); +} diff --git a/src/pages/Dashboard.jsx b/src/pages/Dashboard.jsx index c161287..14de736 100644 --- a/src/pages/Dashboard.jsx +++ b/src/pages/Dashboard.jsx @@ -201,8 +201,8 @@ const Dashboard = () => { />
- {/* --- COMPANY-WIDE ANALYTICS (ADMIN) --- */} - {user?.role === 'ADMIN' && ( + {/* --- COMPANY-WIDE ANALYTICS (ADMIN + OWNER) --- */} + {['ADMIN', 'OWNER'].includes(user?.role) && (
{/* Section header */}
@@ -398,7 +398,7 @@ const Dashboard = () => {

{lead.propertyData.propertyAddress.split(',')[0]}

-

{lead.propertyData.propertyType} • ${lead.propertyData.currentEstimatedMarketValue.toLocaleString()}

+

{lead.propertyData.propertyType} • Home value ${lead.propertyData.currentEstimatedMarketValue.toLocaleString('en-US')}