From 94ddc1b03080d9aa7d2f6ee2e005d587fdb51e49 Mon Sep 17 00:00:00 2001 From: Satyam Rastogi Date: Fri, 29 May 2026 18:59:50 +0530 Subject: [PATCH] fix(owner): replace native Team Management select with custom dark-mode-aware project picker --- src/data/selectors.js | 43 +++++++++++++++++ src/pages/owner/OwnerSnapshot.jsx | 77 ++++++++++++++++++++++++------- 2 files changed, 104 insertions(+), 16 deletions(-) diff --git a/src/data/selectors.js b/src/data/selectors.js index 805a342..789b794 100644 --- a/src/data/selectors.js +++ b/src/data/selectors.js @@ -101,3 +101,46 @@ export function stormAttribution(kanbanLeads = [], projects = []) { revenuePct: totalRevenue ? Math.round((stormRevenue / totalRevenue) * 100) : 0, }; } + +// Owner Financial-Overview metrics, each carrying the underlying line items so the dashboard +// cards can drill down to the real records. Pass `today` (ISO date) to flag payouts due soon. +// - collected: cash received from clients (paid paymentSchedule entries) +// - outstandingAR: client money still owed (unpaid paymentSchedule entries) +// - pendingPayouts: money owed to vendors (pending invoices); dueSoon = within 7 days of `today` +// - vendorSpend: cash paid out to vendors (paid invoices) +export function financialOverview(projects = [], today) { + const now = today ? new Date(today) : null; + const within7 = (d) => { + if (!now || !d) return false; + const diff = (new Date(d) - now) / 86400000; + return diff >= 0 && diff <= 7; + }; + const collected = { total: 0, items: [] }; + const outstandingAR = { total: 0, count: 0, items: [] }; + const pendingPayouts = { total: 0, dueSoonCount: 0, dueSoonTotal: 0, items: [] }; + const vendorSpend = { total: 0, items: [] }; + for (const p of projects) { + const project = p.customerName || p.projectType || p.id; + for (const ps of p.paymentSchedule || []) { + if (ps.status === 'paid') { + collected.total += ps.amount || 0; + collected.items.push({ projectId: p.id, project, label: ps.milestone, amount: ps.amount || 0, date: ps.paidDate || ps.dueDate }); + } else { + outstandingAR.total += ps.amount || 0; + outstandingAR.count += 1; + outstandingAR.items.push({ projectId: p.id, project, label: ps.milestone, amount: ps.amount || 0, dueDate: ps.dueDate }); + } + } + for (const inv of p.invoices || []) { + if (inv.status === 'pending') { + pendingPayouts.total += inv.amount || 0; + pendingPayouts.items.push({ projectId: p.id, project, label: inv.submittedBy, amount: inv.amount || 0, dueDate: inv.dueDate }); + if (within7(inv.dueDate)) { pendingPayouts.dueSoonCount += 1; pendingPayouts.dueSoonTotal += inv.amount || 0; } + } else if (inv.status === 'paid') { + vendorSpend.total += inv.amount || 0; + vendorSpend.items.push({ projectId: p.id, project, label: inv.submittedBy, amount: inv.amount || 0, date: inv.datePaid }); + } + } + } + return { collected, outstandingAR, pendingPayouts, vendorSpend }; +} diff --git a/src/pages/owner/OwnerSnapshot.jsx b/src/pages/owner/OwnerSnapshot.jsx index 2ff4b35..e1eb99f 100644 --- a/src/pages/owner/OwnerSnapshot.jsx +++ b/src/pages/owner/OwnerSnapshot.jsx @@ -1,4 +1,4 @@ -import React, { useState, useMemo } from 'react'; +import React, { useState, useMemo, useRef, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import { useAuth } from '../../context/AuthContext'; import { useMockStore } from '../../data/mockStore'; @@ -15,9 +15,63 @@ import { BarChart, Bar, XAxis, YAxis, Tooltip, ResponsiveContainer, PieChart, Pie, Cell, Legend } from 'recharts'; import { - Briefcase, TrendingUp, AlertTriangle, Shield, ChevronRight, ArrowUpRight, Users, ChevronDown + Briefcase, TrendingUp, AlertTriangle, Shield, ChevronRight, ArrowUpRight, Users, ChevronDown, Check } from 'lucide-react'; +// Custom dark-mode-aware project picker (replaces the native setSelectedProjectId(e.target.value)} - className="appearance-none pl-3 pr-8 py-2 text-sm font-semibold rounded-xl bg-zinc-100 dark:bg-white/5 border border-zinc-200 dark:border-white/10 text-zinc-900 dark:text-white outline-none focus:border-violet-500 transition-colors cursor-pointer" - > - {ownerProjects.map(p => ( - - ))} - - - +