"use client"; import { useState, useEffect } from "react"; import { LayoutGrid, TrendingUp, CheckCircle2, DollarSign, Clock, Calendar, Users, Target, PiggyBank, FileText, AlertCircle, BarChart3, } from "lucide-react"; import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Skeleton } from "@/components/ui/skeleton"; import { Badge } from "@/components/ui/badge"; import { getProjects } from "@/lib/projectsService"; import { Project, ProjectStatus, STATUS_CONFIG } from "@/types/project"; // Componente para una card de estadística individual interface StatCardProps { title: string; value: string | number; subtitle?: string; icon: React.ReactNode; trend?: { value: number; label: string; positive?: boolean; }; highlight?: boolean; } function StatCard({ title, value, subtitle, icon, trend, highlight }: StatCardProps) { return ( {title} {icon}
{value}
{subtitle &&

{subtitle}

} {trend && (
{trend.positive ? "+" : ""}{trend.value}% {trend.label}
)}
); } // Componente para mostrar la distribución por estado interface StatusDistributionProps { projects: Project[]; } function StatusDistribution({ projects }: StatusDistributionProps) { const statusCounts: Record = { "0": projects.filter((p) => p.status === "0").length, "1": projects.filter((p) => p.status === "1").length, "2": projects.filter((p) => p.status === "2").length, }; const total = projects.length; return ( Distribucion por Estado {(Object.keys(statusCounts) as ProjectStatus[]).map((status) => { const count = statusCounts[status]; const percentage = total > 0 ? Math.round((count / total) * 100) : 0; const config = STATUS_CONFIG[status]; return (
{config.label} {count} ({percentage}%)
); })} ); } // Componente para estadísticas de presupuesto interface BudgetStatsProps { projects: Project[]; title?: string; } function BudgetStats({ projects, title = "Resumen de Presupuesto" }: BudgetStatsProps) { const totalBudget = projects.reduce((acc, p) => acc + p.budget, 0); const totalSpent = projects.reduce((acc, p) => acc + p.spent, 0); const remaining = totalBudget - totalSpent; const spentPercentage = totalBudget > 0 ? Math.round((totalSpent / totalBudget) * 100) : 0; return ( {title}

{totalBudget >= 1000 ? `${(totalBudget / 1000).toFixed(0)}k` : totalBudget.toLocaleString("es-ES")}

Presupuesto Total

{totalSpent >= 1000 ? `${(totalSpent / 1000).toFixed(0)}k` : totalSpent.toLocaleString("es-ES")}

Gastado

= 0 ? "text-green-600" : "text-red-600"}`}> {remaining >= 1000 ? `${(remaining / 1000).toFixed(0)}k` : remaining.toLocaleString("es-ES")}

Restante

Consumido {spentPercentage}%
90 ? "bg-red-500" : spentPercentage > 70 ? "bg-yellow-500" : "bg-blue-500" }`} style={{ width: `${Math.min(spentPercentage, 100)}%` }} />
); } // Funciones helper para calcular estadísticas function calculateStats(projects: Project[]) { const total = projects.length; if (total === 0) { return { total: 0, avgProgress: 0, totalBudget: 0, totalSpent: 0, avgBudget: 0, budgetConsumed: 0, minBudget: 0, maxBudget: 0, uniqueClients: 0, oldestProject: null as string | null, newestProject: null as string | null, avgDuration: 0, }; } const totalBudget = projects.reduce((acc, p) => acc + p.budget, 0); const totalSpent = projects.reduce((acc, p) => acc + p.spent, 0); const avgProgress = Math.round(projects.reduce((acc, p) => acc + p.progress, 0) / total); const avgBudget = Math.round(totalBudget / total); const budgetConsumed = totalBudget > 0 ? Math.round((totalSpent / totalBudget) * 100) : 0; const budgets = projects.map((p) => p.budget); const minBudget = Math.min(...budgets); const maxBudget = Math.max(...budgets); const uniqueClients = new Set(projects.map((p) => p.client)).size; const dates = projects.map((p) => new Date(p.startDate).getTime()).filter((d) => !isNaN(d)); const oldestProject = dates.length > 0 ? new Date(Math.min(...dates)).toLocaleDateString("es-ES") : null; const newestProject = dates.length > 0 ? new Date(Math.max(...dates)).toLocaleDateString("es-ES") : null; // Calcular duracion promedio en dias const durations = projects .map((p) => { const start = new Date(p.startDate).getTime(); const end = new Date(p.endDate).getTime(); if (isNaN(start) || isNaN(end)) return null; return Math.ceil((end - start) / (1000 * 60 * 60 * 24)); }) .filter((d): d is number => d !== null && d > 0); const avgDuration = durations.length > 0 ? Math.round(durations.reduce((a, b) => a + b, 0) / durations.length) : 0; return { total, avgProgress, totalBudget, totalSpent, avgBudget, budgetConsumed, minBudget, maxBudget, uniqueClients, oldestProject, newestProject, avgDuration, }; } // Panel de estadísticas para "Todos los proyectos" interface AllProjectsStatsProps { projects: Project[]; } function AllProjectsStats({ projects }: AllProjectsStatsProps) { const stats = calculateStats(projects); const activeProjects = projects.filter((p) => p.status !== "2" && p.progress < 100); const completedProjects = projects.filter((p) => p.status === "2" || p.progress >= 100); return (
{/* Cards principales */}
} highlight /> 0 ? Math.round((activeProjects.length / stats.total) * 100) : 0}% del total`} icon={} /> 0 ? Math.round((completedProjects.length / stats.total) * 100) : 0}% del total`} icon={} /> } />
{/* Segunda fila: Presupuesto */}
} /> } /> } /> } />
{/* Tercera fila: Fechas y distribucion */}
} /> } /> } />
{/* Cuarta fila: Graficos/Distribuciones */}
{/* Info adicional: Rango de presupuestos */}
Rango de Presupuestos

Minimo

{stats.minBudget.toLocaleString("es-ES")}

Maximo

{stats.maxBudget.toLocaleString("es-ES")}

Proyectos por Progreso

{projects.filter((p) => p.progress < 25).length}

0-25%

{projects.filter((p) => p.progress >= 25 && p.progress < 50).length}

25-50%

{projects.filter((p) => p.progress >= 50 && p.progress < 75).length}

50-75%

{projects.filter((p) => p.progress >= 75).length}

75-100%

); } // Panel de estadísticas para "Proyectos Activos" interface ActiveProjectsStatsProps { projects: Project[]; } function ActiveProjectsStats({ projects }: ActiveProjectsStatsProps) { // Filtrar solo proyectos activos (no cerrados y no completados al 100%) const activeProjects = projects.filter((p) => p.status !== "2" && p.progress < 100); const stats = calculateStats(activeProjects); if (activeProjects.length === 0) { return (

No hay proyectos activos

Todos los proyectos estan completados o cerrados

); } // Proyectos que necesitan atencion (bajo progreso o alto consumo de presupuesto) const needsAttention = activeProjects.filter((p) => { const spentPercentage = p.budget > 0 ? (p.spent / p.budget) * 100 : 0; return p.progress < 25 || spentPercentage > 80; }); // Proyectos proximos a terminar const nearCompletion = activeProjects.filter((p) => p.progress >= 75); return (
{/* Cards principales */}
} highlight /> } /> } /> } />
{/* Alertas */}
Requieren Atencion

{needsAttention.length}

Proyectos con bajo progreso o alto consumo de presupuesto

Proximos a Completar

{nearCompletion.length}

Proyectos con 75% o mas de progreso

{/* Estadísticas adicionales */}
} /> } /> } />
{/* Distribucion y Budget */}
{/* Proyectos por progreso */} Distribucion por Progreso

{activeProjects.filter((p) => p.progress < 25).length}

Inicio (0-25%)

{activeProjects.filter((p) => p.progress >= 25 && p.progress < 50).length}

En curso (25-50%)

{activeProjects.filter((p) => p.progress >= 50 && p.progress < 75).length}

Avanzado (50-75%)

{activeProjects.filter((p) => p.progress >= 75).length}

Casi listo (75-99%)

); } // Panel de estadísticas para "Proyectos Completados" interface CompletedProjectsStatsProps { projects: Project[]; } function CompletedProjectsStats({ projects }: CompletedProjectsStatsProps) { // Filtrar proyectos completados (cerrados o 100% progreso) const completedProjects = projects.filter((p) => p.status === "2" || p.progress >= 100); const stats = calculateStats(completedProjects); if (completedProjects.length === 0) { return (

No hay proyectos completados

Aun no se ha completado ningun proyecto

); } // Proyectos que terminaron dentro del presupuesto const withinBudget = completedProjects.filter((p) => p.spent <= p.budget); const overBudget = completedProjects.filter((p) => p.spent > p.budget); // Eficiencia promedio (presupuesto restante / presupuesto total) const avgEfficiency = completedProjects.length > 0 ? Math.round( completedProjects.reduce((acc, p) => { if (p.budget === 0) return acc; return acc + ((p.budget - p.spent) / p.budget) * 100; }, 0) / completedProjects.length ) : 0; return (
{/* Cards principales */}
} highlight /> } /> } /> } />
{/* Cumplimiento de presupuesto */}
Dentro del Presupuesto

{withinBudget.length}

{stats.total > 0 ? Math.round((withinBudget.length / stats.total) * 100) : 0}% de los proyectos completados

Excedieron Presupuesto

{overBudget.length}

{stats.total > 0 ? Math.round((overBudget.length / stats.total) * 100) : 0}% de los proyectos completados

{/* Estadísticas adicionales */}
} /> } /> } />
{/* Distribucion y Budget */}
Rango de Presupuestos Completados

Minimo

{stats.minBudget.toLocaleString("es-ES")}

Maximo

{stats.maxBudget.toLocaleString("es-ES")}

{/* Timeline */} Rango de Fechas

Primer Proyecto

{stats.oldestProject || "N/A"}

Ultimo Proyecto

{stats.newestProject || "N/A"}

); } // Componente de skeleton para carga function StatisticsSkeleton() { return (
{Array.from({ length: 4 }).map((_, i) => ( ))}
{Array.from({ length: 4 }).map((_, i) => ( ))}
); } // Componente principal de la página export default function StatisticsPage() { const [projects, setProjects] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); useEffect(() => { async function loadProjects() { try { setLoading(true); const data = await getProjects(); setProjects(data); setError(null); } catch (err) { console.error("Error loading projects:", err); setError("Error al cargar los proyectos"); } finally { setLoading(false); } } loadProjects(); }, []); if (error) { return (

{error}

); } const activeCount = projects.filter((p) => p.status !== "2" && p.progress < 100).length; const completedCount = projects.filter((p) => p.status === "2" || p.progress >= 100).length; return (
{/* Header */}

Estadisticas

Analisis detallado de {projects.length} proyectos

{/* Main content */}
{loading ? ( ) : ( Todos {projects.length} Activos {activeCount} Completados {completedCount} )}
); }