import ProjectLayout from '../../Layouts/ProjectLayout'; import EvmSCurveChart from '../../Components/EvmSCurveChart'; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from '@/Components/ui/card'; import { MapPin, Users, TrendingUp, DollarSign, Calendar, AlertTriangle, ArrowRight, Activity, ShieldCheck, FileText, ClipboardList, Hammer, Truck, Layers, Milestone as MilestoneIcon, Tags } from 'lucide-react'; import { PageProps } from '@/types'; import { Badge } from '@/Components/ui/badge'; import { Link } from '@inertiajs/react'; import { Button } from '@/Components/ui/button'; import { useState } from 'react'; import { cn } from '@/lib/utils'; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/Components/ui/tabs'; interface ChildProject { id: number; ulid: string; name: string; code: string; status: string; contract_value: string; total_capitalization: string; start_date?: string; target_end_date?: string; } interface ProjectData { id: number; ulid: string; name: string; code: string; description?: string; status: string; location?: string; client_name?: string; contract_value: string; total_capitalization: string; completion_percentage: string; capitalization_percentage: number; is_over_budget: boolean; contract_duration?: number; start_date?: string; target_end_date?: string; actual_end_date?: string; created_at: string; is_unprofitable: boolean; project_type: string; classifications?: string[]; extension_projects?: ChildProject[]; rollup_contract_value?: string | number; rollup_capitalization?: string | number; parent_project?: { id: number; ulid: string; name: string; code: string } | null; milestones?: { id: number; ulid: string; name: string; weight_percentage: string | number; }[]; materials_estimates?: { id: number; ulid: string; estimated_qty: string | number; unit_cost: string | number; material: { id: number; ulid: string; name: string; sku?: string; unit: string; category?: string; }; }[]; tasks: { id: number; ulid: string; name: string; description?: string; status: string; estimated_hours: string | number; task_labors?: { id: number; estimated_hours: string | number; labor: { id: number; name: string; category: string; hourly_rate: string | number; }; }[]; task_equipments?: { id: number; estimated_hours: string | number; equipment: { id: number; name: string; owner_name?: string; hourly_rate: string | number; }; }[]; }[]; } interface StatusOption { value: string; label: string } interface Props extends PageProps { project: ProjectData; taskStats: { total: number; pending: number; in_progress: number; completed: number; closed?: number }; allowedTransitions: StatusOption[]; estimationTotals?: { materials: number; labor: number; equipment: number; sum: number; margin: number; margin_pct: number; }; evmData?: any; tab?: string; } const formatCurrency = (v: string | number) => new Intl.NumberFormat('en-PH', { style: 'currency', currency: 'PHP' }).format(Number(v)); function StatCard({ icon: Icon, label, value, sub }: { icon: React.ComponentType<{ className?: string }>; label: string; value: string; sub?: string }) { return (

{label}

{value}

{sub &&

{sub}

}
); } export default function Overview({ project, taskStats, allowedTransitions, estimationTotals, tab, evmData }: Props) { const [activeTab, setActiveTab] = useState(tab || 'overview'); return (
Overview Dashboard Progress Monitoring (EVM) Estimation Review
{/* Unprofitable Warning Banner */} {project.is_unprofitable && (

Special Handling: Unprofitable / Loss-Making Project Flagged

This project has been explicitly flagged as unprofitable. Budget overruns, procurement approvals, and requisition validation will be visually highlighted for special auditing.

)} {/* Parent Project Linked Alert */} {project.project_type === 'extension' && project.parent_project && (

Extension Project / Variation Order

This project is linked to parent project: {project.parent_project.code}. Tasks and capitalization values roll up recursively.

)} {/* Stat Cards */}
= 80 ? 'bg-amber-100' : 'bg-gray-100' }`}> = 80 ? 'text-amber-600' : 'text-gray-600' }`} />

Capitalization

{project.capitalization_percentage.toFixed(1)}%

{formatCurrency(project.total_capitalization)}

= 80 ? 'bg-amber-500' : 'bg-emerald-500' }`} style={{ width: `${Math.min(project.capitalization_percentage, 100)}%` }} />
{project.description &&

Description

{project.description}

}
{project.location &&
{project.location}
} {project.client_name &&
Client: {project.client_name}
}
{project.classifications && project.classifications.length > 0 && (

Project Classifications

{project.classifications.map((tag: string, idx: number) => ( {tag} ))}
)} {/* Capitalization Section */}

Cost Capitalization

= 80 ? 'text-amber-600' : 'text-gray-800' }`}>{project.capitalization_percentage.toFixed(1)}% of contract value

= 80 ? 'bg-amber-500' : 'bg-emerald-500' }`} style={{ width: `${Math.min(project.capitalization_percentage, 100)}%` }} />

{formatCurrency(project.total_capitalization)} / {formatCurrency(project.contract_value)}

{project.is_over_budget && (

⚠ Over budget

)}
{/* Task Progress Section */}

Progress

{(taskStats.completed || 0) + (taskStats.closed || 0)} / {taskStats.total} tasks completed

0 ? `${(((taskStats.completed || 0) + (taskStats.closed || 0)) / taskStats.total) * 100}%` : '0%' }} />

{taskStats.total > 0 ? `${((((taskStats.completed || 0) + (taskStats.closed || 0)) / taskStats.total) * 100).toFixed(0)}%` : '0%'} complete

{taskStats.total > 0 && (

Task Distribution

{(taskStats.closed || 0) > 0 && (
)} {taskStats.completed > 0 && (
)} {taskStats.in_progress > 0 && (
)} {taskStats.pending > 0 && (
)}
)} {taskStats.total > 0 ? (
{[ { label: 'Closed', count: taskStats.closed || 0, color: 'bg-orange-500', textColor: 'text-orange-600' }, { label: 'Completed', count: taskStats.completed, color: 'bg-emerald-500', textColor: 'text-emerald-600' }, { label: 'In Progress', count: taskStats.in_progress, color: 'bg-blue-500', textColor: 'text-blue-600' }, { label: 'Pending', count: taskStats.pending, color: 'bg-gray-300', textColor: 'text-gray-500' }, ].map((item) => { const pct = taskStats.total > 0 ? (item.count / taskStats.total) * 100 : 0; return (
{item.label}
{item.count} task{item.count !== 1 ? 's' : ''} ({pct.toFixed(0)}%)
); })}
) : (

No tasks yet. Add tasks to track progress.

)}
{/* Project Extensions & Financial Rollups */} {project.project_type !== 'extension' && (
Project Extensions & Variation Orders Financial and task progress rollup aggregates for parent project and child variations.
{project.extension_projects?.length || 0} Extension(s)
{/* Rollup Financials Summary */}
Combined Contract Value

{formatCurrency(project.rollup_contract_value || project.contract_value)}

Includes parent & child contracts
Combined Capitalization

{formatCurrency(project.rollup_capitalization || project.total_capitalization)}

Combined actual spent costs
Combined Capitalization %

{((Number(project.rollup_capitalization || project.total_capitalization) / Math.max(Number(project.rollup_contract_value || project.contract_value), 1)) * 100).toFixed(1)}%

Combined budget utilization rate
{/* Combined Capitalization Progress Bar */}
Combined Cost Capitalization Progress {formatCurrency(project.rollup_capitalization || project.total_capitalization)} / {formatCurrency(project.rollup_contract_value || project.contract_value)}
Number(project.rollup_contract_value || project.contract_value) ? 'bg-rose-500' : 'bg-emerald-500' }`} style={{ width: `${Math.min( (Number(project.rollup_capitalization || project.total_capitalization) / Math.max(Number(project.rollup_contract_value || project.contract_value), 1)) * 100, 100 )}%` }} />
{/* Extensions List Table */} {project.extension_projects && project.extension_projects.length > 0 ? (
{project.extension_projects.map((ext) => ( ))}
Code & Project Name Status Contract Value Capitalization Timeline Action
{ext.code} {ext.name} {ext.status.replace(/_/g, ' ')} {formatCurrency(ext.contract_value)} {formatCurrency(ext.total_capitalization)} {ext.start_date ? new Date(ext.start_date).toLocaleDateString() : 'N/A'} - {ext.target_end_date ? new Date(ext.target_end_date).toLocaleDateString() : 'N/A'}
) : (

No extension projects or variation orders registered for this project.

Extensions can be created via the Create Project page and selecting this as the Parent Project.

)} )} {/* Summary Metrics */}

Materials Cost

{formatCurrency(estimationTotals?.materials || 0)}

Labor Cost

{formatCurrency(estimationTotals?.labor || 0)}

Equipment Cost

{formatCurrency(estimationTotals?.equipment || 0)}

Total Estimate

{formatCurrency(estimationTotals?.sum || 0)}

{/* Profitability Margin Bar */} {estimationTotals && ( = 0 ? "border-emerald-200 bg-emerald-50/30" : "border-rose-200 bg-rose-50/30" )}>
= 0 ? "text-emerald-600" : "text-rose-600" )} />

= 0 ? "text-emerald-900" : "text-rose-900" )}> {estimationTotals.margin >= 0 ? "Estimated Project Margins Clear" : "Unprofitable Estimation Warning"}

= 0 ? "text-emerald-700" : "text-rose-700" )}> This estimation consumes {((estimationTotals.sum / Math.max(Number(project.contract_value), 1)) * 100).toFixed(1)}% of the registered contract value ({formatCurrency(project.contract_value)}).

= 0 ? "text-emerald-700" : "text-rose-700" )}> {estimationTotals.margin >= 0 ? "+" : ""}{formatCurrency(estimationTotals.margin)} = 0 ? "bg-emerald-100 text-emerald-800" : "bg-rose-100 text-rose-800" )}> Margin: {estimationTotals.margin_pct.toFixed(1)}%
)} {/* Materials Estimates */} Materials Estimates
{project.materials_estimates && project.materials_estimates.length > 0 ? ( project.materials_estimates.map((est, idx) => ( )) ) : ( )}
# Material Name Category Unit Est. Qty Unit Cost Total Cost
{idx + 1} {est.material?.name || 'Unknown Material'} {est.material?.sku && ({est.material.sku})} {est.material?.category || '—'} {est.material?.unit} {parseFloat(est.estimated_qty as string).toLocaleString()} {formatCurrency(est.unit_cost)} {formatCurrency(parseFloat(est.estimated_qty as string) * parseFloat(est.unit_cost as string))}
No materials estimates configured for this project.
{/* Manpower & Equipment Breakdown */} Tasks & Resource Allocations {project.tasks && project.tasks.length > 0 ? ( project.tasks.map((task) => { const taskLaborSubtotal = task.task_labors?.reduce((sum, tl) => sum + (parseFloat(tl.estimated_hours as string) * parseFloat(tl.labor?.hourly_rate as string || '0')), 0) || 0; const taskEquipmentSubtotal = task.task_equipments?.reduce((sum, te) => sum + (parseFloat(te.estimated_hours as string) * parseFloat(te.equipment?.hourly_rate as string || '0')), 0) || 0; const totalTaskCost = taskLaborSubtotal + taskEquipmentSubtotal; return (

{task.name}

{task.description &&

{task.description}

}
Estimated Task Cost: {formatCurrency(totalTaskCost)}
{/* Manpower Allocations */}
Manpower Allocation
{task.task_labors && task.task_labors.length > 0 ? (
{(() => { const bundles: { [key: string]: any[] } = {}; const singleTrades: any[] = []; task.task_labors.forEach((tl: any) => { if (tl.allocation_type === 'bundle' && tl.bundle_name) { if (!bundles[tl.bundle_name]) { bundles[tl.bundle_name] = []; } bundles[tl.bundle_name].push(tl); } else { singleTrades.push(tl); } }); return ( <> {/* Render Job Crew Bundles */} {Object.entries(bundles).map(([bundleName, items]) => { const first = items[0]; const bundleSubtotal = items.reduce((sum, tl) => sum + (parseFloat(tl.estimated_hours as string) * parseFloat(tl.labor?.hourly_rate as string || '0')), 0 ); const bundleUnit = first?.bundle_unit || 'm²'; const bundleQty = first?.bundle_quantity ? Number(first.bundle_quantity) : null; const bundleRate = first?.bundle_unit_rate ? Number(first.bundle_unit_rate) : null; return (
{bundleName}
Crew Bundle {bundleQty !== null && ( {bundleQty} {bundleUnit} {bundleRate ? `@ ${formatCurrency(bundleRate)}/${bundleUnit}` : ''} )}
{formatCurrency(bundleSubtotal)}
{/* Itemized Trades Breakdown within bundle */}
{items.map((tl: any) => (
{tl.labor?.name} ({tl.labor?.category} • {formatCurrency(tl.labor?.hourly_rate)}/hr) {tl.estimated_hours} hrs ({formatCurrency(parseFloat(tl.estimated_hours as string) * parseFloat(tl.labor?.hourly_rate as string || '0'))})
))}
); })} {/* Render Single Trades */} {singleTrades.map((tl: any) => (
{tl.labor?.name} Trade
{tl.labor?.category} • {formatCurrency(tl.labor?.hourly_rate)}/hr
{tl.estimated_hours} hrs {formatCurrency(parseFloat(tl.estimated_hours as string) * parseFloat(tl.labor?.hourly_rate as string || '0'))}
))}
Labor Subtotal {formatCurrency(taskLaborSubtotal)}
); })()}
) : (

No labor allocated to this task.

)}
{/* Equipment Allocations */}
Equipment Allocation
{task.task_equipments && task.task_equipments.length > 0 ? (
{task.task_equipments.map((te) => (
{te.equipment?.name} {te.equipment?.owner_name || 'Unspecified'} • {formatCurrency(te.equipment?.hourly_rate)}/hr
{te.estimated_hours} hrs {formatCurrency(parseFloat(te.estimated_hours as string) * parseFloat(te.equipment?.hourly_rate as string || '0'))}
))}
Equipment Subtotal {formatCurrency(taskEquipmentSubtotal)}
) : (

No equipment allocated to this task.

)}
); }) ) : (

No tasks found for this project.

)}
{/* Milestone schedule weights */} Milestone Schedule Weights
{project.milestones && project.milestones.length > 0 ? ( project.milestones.map((m, idx) => ( )) ) : ( )}
# Milestone Phase Planned Weight
{idx + 1} {m.name} {parseFloat(m.weight_percentage as string).toFixed(1)}%
No milestones defined.
{evmData ? ( ) : ( Progress Monitoring Baseline No EVM progress data is currently available for this project. )} ); }