import React from 'react'; import { useQuery } from '@tanstack/react-query'; import { Link } from 'react-router-dom'; import { Flag, Briefcase, Target, ShieldAlert, FileCheck, Edit2, Calendar, Clock, Coins, ChevronRight, TrendingUp, } from 'lucide-react'; import { getActivities, getMilestones, getProjectIndicators, getProjectRisks, getBudgets, } from '../../services/api'; import { Project, ApprovalStatus, Budget } from '../../types'; import { formatDate, formatZAR, getApprovalStatusBadge, getPriorityBadge, getRiskScoreColor, getUserEmail, } from '../../lib/formatters'; import { ProjectTimelineGantt } from './ProjectTimelineGantt'; interface ProjectExpandedRowProps { project: Project; onOpenMilestonesModal: () => void; onOpenActivitiesModal: () => void; onOpenIndicatorsModal: () => void; onOpenRisksModal: () => void; onOpenEditProject: () => void; } export const ProjectExpandedRow: React.FC = ({ project, onOpenMilestonesModal, onOpenActivitiesModal, onOpenIndicatorsModal, onOpenRisksModal, onOpenEditProject, }) => { const projectId = project.id; const { data: activities = [] } = useQuery({ queryKey: ['activities', projectId], queryFn: () => getActivities(projectId), }); const { data: milestones = [] } = useQuery({ queryKey: ['milestones', projectId], queryFn: () => getMilestones(projectId), }); const { data: indicators = [] } = useQuery({ queryKey: ['indicators', projectId], queryFn: () => getProjectIndicators(projectId), }); const { data: risks = [] } = useQuery({ queryKey: ['risks', projectId], queryFn: () => getProjectRisks(projectId), }); const { data: allBudgets = [] } = useQuery({ queryKey: ['budgets'], queryFn: () => getBudgets(1, 100), }); const projectBudgets = allBudgets.filter((b) => String(b.projectId) === String(projectId)); const approvedBudgets = projectBudgets.filter( (b) => b.status === undefined || b.status === null || Number(b.status) === ApprovalStatus.Approved || Number(b.status) === 3 ); const approvedBudgetTotal = approvedBudgets.reduce((sum, b) => sum + (Number(b.amount) || 0), 0); const verifiedMilestonesCount = milestones.filter((m) => m.status === ApprovalStatus.Approved).length; const milestoneProgress = milestones.length > 0 ? Math.round((verifiedMilestonesCount / milestones.length) * 100) : 0; const totalActivityBudget = activities.reduce((sum, a) => sum + (Number(a.amount) || 0), 0); return (
{/* Project Meta Bar */}
Project #{project.id} Summary Owner / Created by: {getUserEmail(project.createdBy)} {approvedBudgetTotal > 0 && ( <> • Approved Budget: {formatZAR(approvedBudgetTotal)} )}

{project.description}

Review Evidence Queue
{/* Gantt-style Timeline Component */} {/* 4 Vital Resource Summary Cards Grid */}
{/* Card 1: Milestones */}
Milestones ({milestones.length})
{verifiedMilestonesCount}/{milestones.length} Verified
{milestones.length === 0 ? (

No contractual milestones defined.

) : (
{milestones.slice(0, 3).map((m) => { const sBadge = getApprovalStatusBadge(m.status); return (
{m.name} {sBadge.label}
Due: {formatDate(m.dueAt)}
); })} {milestones.length > 3 && (

+{milestones.length - 3} more deliverables

)}
)}
{/* Card 2: Activities */}
Activities ({activities.length})
{formatZAR(totalActivityBudget)}
{activities.length === 0 ? (

No work activities recorded.

) : (
{activities.slice(0, 3).map((act) => (
{act.title} {formatZAR(act.amount)}
{act.category || 'Operations'} {formatDate(act.performedAt)}
))} {activities.length > 3 && (

+{activities.length - 3} more tasks

)}
)}
{/* Card 3: Indicators */}
Indicators ({indicators.length})
M&E Targets
{indicators.length === 0 ? (

No M&E indicators defined.

) : (
{indicators.slice(0, 3).map((ind) => { const target = Number(ind.targetAmount) || 1; const actual = Number(ind.actualAmount) || 0; const pct = Math.round((actual / target) * 100); return (
{ind.name} = 100 ? 'text-emerald-400 font-bold' : 'text-indigo-400 font-bold'}> {pct}%
Actual: {actual} Target: {target}
); })} {indicators.length > 3 && (

+{indicators.length - 3} more indicators

)}
)}
{/* Card 4: Risks */}
Risks ({risks.length})
ISO 31000
{risks.length === 0 ? (

No project hazards logged.

) : (
{risks.slice(0, 3).map((rk) => { const score = (rk.likelihood || 1) * (rk.impact || 1); const riskInfo = getRiskScoreColor(rk.likelihood || 1, rk.impact || 1); return (
{rk.name} {score}/25 {riskInfo.level}

{rk.description}

); })} {risks.length > 3 && (

+{risks.length - 3} more risks

)}
)}
); };