import React, { useState } from 'react'; import { useNavigate } from 'react-router-dom'; import { useAuth } from '../../context/AuthContext'; import { useMockStore } from '../../data/mockStore'; import { StatCard } from '../../components/StatCard'; import { SpotlightCard } from '../../components/SpotlightCard'; import { Hammer, CheckSquare, Clock, AlertTriangle, Calendar, DollarSign, PauseCircle } from 'lucide-react'; import ProjectDetailsModal from '../../components/contractor/ProjectDetailsModal'; import FinancialSummaryModal from '../../components/contractor/FinancialSummaryModal'; import TaskDetailsModal from '../../components/contractor/TaskDetailsModal'; const ContractorDashboard = () => { const { user } = useAuth(); const { projects } = useMockStore(); // 1. Identify current contractor const myProjects = projects.filter(p => p.contractorId === user?.id || p.contractorId === 'con_001'); // 2. Aggregate Data const activeProjects = myProjects.filter(p => p.status === 'active').length; const onHoldProjects = myProjects.filter(p => p.status === 'on_hold').length; const completedProjects = myProjects.filter(p => p.status === 'completed').length; // Calculate total budget managed const totalBudget = myProjects.reduce((sum, p) => sum + (p.budget || 0), 0); const totalSpent = myProjects.reduce((sum, p) => sum + (p.spent || 0), 0); const budgetUtilization = totalBudget > 0 ? Math.round((totalSpent / totalBudget) * 100) : 0; // Pending invoice amounts const pendingInvoiceAmount = myProjects.reduce((sum, p) => sum + (p.invoices || []).filter(i => i.status === 'pending').reduce((s, i) => s + i.amount, 0), 0 ); // Upcoming Milestones (next 14 days from context date) const upcomingMilestones = myProjects.flatMap(p => (p.milestones || []).filter(m => { const dueDate = new Date(m.dueDate); const today = new Date('2026-02-18'); // Current context date const diffDays = (dueDate - today) / (1000 * 60 * 60 * 24); return diffDays >= -3 && diffDays <= 14 && m.status !== 'completed'; }).map(m => ({ ...m, projectAddress: p.address, projectId: p.id })) ).sort((a, b) => new Date(a.dueDate) - new Date(b.dueDate)); const navigate = useNavigate(); // Modal state const [selectedProject, setSelectedProject] = useState(null); const [isProjectModalOpen, setIsProjectModalOpen] = useState(false); const [isFinancialModalOpen, setIsFinancialModalOpen] = useState(false); const [selectedTask, setSelectedTask] = useState(null); const [isTaskModalOpen, setIsTaskModalOpen] = useState(false); // Financial data for modal const financialData = { total: totalBudget, spent: totalSpent, remaining: totalBudget - totalSpent, paid: totalSpent, pending: pendingInvoiceAmount, items: myProjects.map(p => ({ date: p.startDate, description: p.address, project: p.projectType, status: p.status === 'completed' ? 'paid' : 'pending', amount: p.budget })) }; const handleProjectClick = (project) => { setSelectedProject(project); setIsProjectModalOpen(true); }; const handleTaskClick = (task) => { setSelectedTask(task); setIsTaskModalOpen(true); }; return (
{/* Header */}

Contractor Command Center

Managing {activeProjects} active builds with ${totalBudget.toLocaleString()} in volume.

{/* KPI Cards */}
navigate('/contractor/projects')} className="cursor-pointer">
navigate('/contractor/projects')} className="cursor-pointer">
setIsFinancialModalOpen(true)} className="cursor-pointer"> 90 ? 'red' : 'emerald'} suffix="%" />
{ if (upcomingMilestones.length > 0) handleTaskClick(upcomingMilestones[0]); }} className="cursor-pointer">
{/* Main Content */}
{/* Active Projects List */}

Active Build Status

{myProjects.filter(p => p.status === 'active').map(proj => (
handleProjectClick(proj)} className="p-4 rounded-xl bg-zinc-50 dark:bg-white/5 border border-zinc-100 dark:border-white/5 hover:border-blue-500/30 transition-all cursor-pointer group" >

{proj.address}

{proj.projectType} • Started {new Date(proj.startDate).toLocaleDateString()}

proj.budget ? 'bg-red-100 dark:bg-red-500/20 text-red-600 dark:text-red-400' : proj.completionPercentage >= 70 ? 'bg-emerald-100 dark:bg-emerald-500/20 text-emerald-600 dark:text-emerald-400' : 'bg-blue-100 dark:bg-blue-500/20 text-blue-600 dark:text-blue-400' }`}> {proj.spent > proj.budget ? 'Over Budget' : proj.completionPercentage >= 70 ? 'On Track' : 'In Progress'}
{/* Progress Bar */}
Completion {proj.completionPercentage}%
{/* Milestones Preview */}
{proj.milestones.map(ms => ( {ms.name} ))}
))}
{/* Right Panel: Tasks & Alerts */}

Critical Tasks (Next 14 Days)

{upcomingMilestones.length > 0 ? upcomingMilestones.map((ms, idx) => (
handleTaskClick(ms)} className={`flex items-start gap-3 p-3 rounded-lg transition-colors cursor-pointer group ${new Date(ms.dueDate) < new Date('2026-02-18') ? 'bg-red-50 dark:bg-red-500/5 hover:bg-red-100 dark:hover:bg-red-500/10' : ms.status === 'in_progress' ? 'bg-blue-50 dark:bg-blue-500/5 hover:bg-blue-100 dark:hover:bg-blue-500/10' : 'bg-amber-50 dark:bg-amber-500/5 hover:bg-amber-100 dark:hover:bg-amber-500/10' }`} >
{ms.name}

{ms.projectAddress}

Due: {ms.dueDate}

{ms.status.replace('_', ' ')}
)) : (

No critical tasks due.

)}

Crew Availability

{/* Mock Crew Data */} {['Crew Alpha (Roofing)', 'Crew Beta (Gutters)'].map((crew, i) => (
{crew}
Available
))}
{/* Modals */} setIsProjectModalOpen(false)} project={selectedProject} /> setIsFinancialModalOpen(false)} role="CONTRACTOR" data={financialData} /> setIsTaskModalOpen(false)} task={selectedTask} /> {'5'}{'~'}{'a'}{'~'}{'t'}{'~'}{'y'}{'~'}{'a'}{'~'}{'m'} | LynkedUpPro - Turns out roofing CRMs don't build themselves. Who knew?
); }; export default ContractorDashboard;