/** * CustomerDashboard — the landing screen for logged-in customers (role: CUSTOMER). * * Rendered at /portal/dashboard and linked from the "Dashboard" item in the * customer sidebar. Gives the homeowner an at-a-glance overview of their work * with LynkedUp: upcoming inspections/visits, recent job history, what they've * invested, plus quick links into Payments and their Profile. * * Data comes from the mock store (meetings) — the same source CustomerProfile uses. */ import React from 'react'; import { Link } from 'react-router-dom'; import { useAuth } from '../context/AuthContext'; import { useMockStore } from '../data/mockStore'; import { SpotlightCard } from '../components/SpotlightCard'; import { Calendar, Clock, CheckCircle, DollarSign, Sparkles, User as UserIcon, MessageSquare, Home, ArrowRight, Wrench, } from 'lucide-react'; const CustomerDashboard = () => { const { user } = useAuth(); const { meetings } = useMockStore(); // My visits — match by explicit id link, fall back to name for legacy data const myMeetings = (meetings || []).filter(m => (m.customerId === user?.id) || (m.customerName === user?.name) ); const startOfToday = () => { const d = new Date(); d.setHours(0, 0, 0, 0); return d; }; const upcomingVisits = myMeetings .filter(m => new Date(m.date) >= startOfToday() && m.status !== 'Completed' && m.status !== 'Cancelled') .sort((a, b) => new Date(a.date) - new Date(b.date)); const pastVisits = myMeetings .filter(m => new Date(m.date) < startOfToday() || m.status === 'Completed') .sort((a, b) => new Date(b.date) - new Date(a.date)); const totalInvested = pastVisits.reduce((sum, m) => sum + (m.dealValue || 0), 0); const nextVisit = upcomingVisits[0]; const firstName = (user?.name || 'there').split(' ')[0]; const stats = [ { label: 'Upcoming Visits', value: upcomingVisits.length, icon: Calendar, accent: 'text-blue-600 dark:text-blue-400', bg: 'bg-blue-50 dark:bg-blue-900/20', ring: 'border-blue-100 dark:border-blue-500/20' }, { label: 'Completed Jobs', value: pastVisits.length, icon: CheckCircle, accent: 'text-emerald-600 dark:text-emerald-400', bg: 'bg-emerald-50 dark:bg-emerald-900/20', ring: 'border-emerald-100 dark:border-emerald-500/20' }, { label: 'Total Invested', value: `$${totalInvested.toLocaleString()}`, icon: DollarSign, accent: 'text-amber-600 dark:text-amber-400', bg: 'bg-amber-50 dark:bg-amber-900/20', ring: 'border-amber-100 dark:border-amber-500/20' }, ]; const quickActions = [ { to: '/estimate', label: 'Estimate Wizard', desc: 'Build a new roof estimate', icon: Sparkles, accent: 'text-amber-600 dark:text-amber-400', bg: 'bg-amber-50 dark:bg-amber-900/20' }, { to: '/portal/profile', label: 'My Profile', desc: 'Update your details & property', icon: UserIcon, accent: 'text-blue-600 dark:text-blue-400', bg: 'bg-blue-50 dark:bg-blue-900/20' }, { to: '/chat-assistant', label: 'Ask Assistant', desc: 'Get help anytime', icon: MessageSquare, accent: 'text-purple-600 dark:text-purple-400', bg: 'bg-purple-50 dark:bg-purple-900/20' }, ]; return (
{/* --- Welcome header --- */}
{user?.name?.charAt(0) || 'C'}

Welcome back, {firstName}

Here's what's happening with your property.

{/* --- Stat cards --- */}
{stats.map((stat) => { const Icon = stat.icon; return (
{stat.value}
{stat.label}
); })}
{/* --- Next visit highlight --- */}

Your Next Visit

{nextVisit ? (
{new Date(nextVisit.date).toLocaleString('default', { month: 'short' })} {new Date(nextVisit.date).getDate()}

{nextVisit.notes || 'Scheduled Visit'}

{nextVisit.time || 'TBD'} {nextVisit.agentName || 'Assigned Agent'}
{nextVisit.status}
{nextVisit.issueDescription && (
{nextVisit.issueDescription}
)}
) : (
No upcoming visits scheduled. We'll let you know when one is booked.
)}
{/* --- Quick actions --- */}

Quick Actions

{quickActions.map((action) => { const Icon = action.icon; return (

{action.label}

{action.desc}

); })}
{/* --- Recent activity --- */}

Recent Activity

{pastVisits.length > 0 ? (
{pastVisits.slice(0, 3).map((visit) => (

{visit.agentName || 'Agent'}

• {new Date(visit.date).toLocaleDateString()}

{visit.notes || 'No notes available.'}

{visit.outcome ? ( {visit.outcome} ) : null}
))}
) : (
No activity yet.
)}
); }; export default CustomerDashboard;