257 lines
9.6 KiB
TypeScript
257 lines
9.6 KiB
TypeScript
import {
|
|
FileText,
|
|
Building2,
|
|
Calendar,
|
|
Clock,
|
|
Hash,
|
|
} from "lucide-react";
|
|
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
|
import { Separator } from "@/components/ui/separator";
|
|
import { Progress } from "@/components/ui/progress";
|
|
import { Project } from "@/types/project";
|
|
|
|
interface ProjectInfoProps {
|
|
project: Project;
|
|
}
|
|
|
|
// Formatear moneda
|
|
function formatCurrency(amount: number): string {
|
|
return new Intl.NumberFormat("es-ES", {
|
|
style: "currency",
|
|
currency: "EUR",
|
|
minimumFractionDigits: 2,
|
|
}).format(amount);
|
|
}
|
|
|
|
// Formatear fecha
|
|
function formatDate(dateString: string): string {
|
|
return new Date(dateString).toLocaleDateString("es-ES", {
|
|
day: "numeric",
|
|
month: "long",
|
|
year: "numeric",
|
|
});
|
|
}
|
|
|
|
// Calcular duración en días
|
|
function getDuration(startDate: string, endDate: string): number {
|
|
const start = new Date(startDate);
|
|
const end = new Date(endDate);
|
|
const diff = end.getTime() - start.getTime();
|
|
return Math.ceil(diff / (1000 * 60 * 60 * 24));
|
|
}
|
|
|
|
export function ProjectInfo({ project }: ProjectInfoProps) {
|
|
const duration = getDuration(project.startDate, project.endDate);
|
|
const budgetUsed = project.budget > 0 ? (project.spent / project.budget) * 100 : 0;
|
|
const budgetRemaining = project.budget - project.spent;
|
|
|
|
return (
|
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
{/* Columna principal - 2/3 */}
|
|
<div className="lg:col-span-2 space-y-6">
|
|
{/* Descripción */}
|
|
<Card>
|
|
<CardHeader className="pb-3">
|
|
<CardTitle className="flex items-center gap-2 text-base">
|
|
<FileText className="h-4 w-4 text-muted-foreground" />
|
|
Descripción
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
{project.description ? (
|
|
<p className="text-sm text-muted-foreground whitespace-pre-wrap leading-relaxed">
|
|
{project.description}
|
|
</p>
|
|
) : (
|
|
<p className="text-sm text-muted-foreground italic">
|
|
Sin descripción disponible
|
|
</p>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Información financiera */}
|
|
<Card>
|
|
<CardHeader className="pb-3">
|
|
<CardTitle className="text-base">Información Financiera</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-6">
|
|
{/* Barra de progreso del presupuesto */}
|
|
<div>
|
|
<div className="flex items-center justify-between mb-2">
|
|
<span className="text-sm font-medium">Uso del presupuesto</span>
|
|
<span className={`text-sm font-medium ${budgetUsed > 90 ? "text-red-500" : ""}`}>
|
|
{budgetUsed.toFixed(1)}%
|
|
</span>
|
|
</div>
|
|
<Progress
|
|
value={Math.min(budgetUsed, 100)}
|
|
className={`h-2 ${budgetUsed > 100 ? "[&>div]:bg-red-500" : ""}`}
|
|
/>
|
|
</div>
|
|
|
|
{/* Desglose */}
|
|
<div className="grid grid-cols-3 gap-4">
|
|
<div className="text-center p-4 rounded-lg bg-muted/50">
|
|
<p className="text-sm text-muted-foreground mb-1">Presupuesto</p>
|
|
<p className="text-xl font-bold">{formatCurrency(project.budget)}</p>
|
|
</div>
|
|
<div className="text-center p-4 rounded-lg bg-muted/50">
|
|
<p className="text-sm text-muted-foreground mb-1">Gastado</p>
|
|
<p className="text-xl font-bold text-blue-600 dark:text-blue-400">
|
|
{formatCurrency(project.spent)}
|
|
</p>
|
|
</div>
|
|
<div className="text-center p-4 rounded-lg bg-muted/50">
|
|
<p className="text-sm text-muted-foreground mb-1">Restante</p>
|
|
<p className={`text-xl font-bold ${budgetRemaining < 0 ? "text-red-500" : "text-green-600 dark:text-green-400"}`}>
|
|
{formatCurrency(budgetRemaining)}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Timeline */}
|
|
<Card>
|
|
<CardHeader className="pb-3">
|
|
<CardTitle className="flex items-center gap-2 text-base">
|
|
<Calendar className="h-4 w-4 text-muted-foreground" />
|
|
Timeline del Proyecto
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="relative">
|
|
{/* Línea de progreso */}
|
|
<div className="absolute top-4 left-0 right-0 h-1 bg-muted rounded-full">
|
|
<div
|
|
className="h-full bg-gradient-to-r from-blue-500 to-purple-600 rounded-full transition-all"
|
|
style={{ width: `${project.progress}%` }}
|
|
/>
|
|
</div>
|
|
|
|
{/* Puntos */}
|
|
<div className="relative flex justify-between pt-8">
|
|
<div className="text-center">
|
|
<div className="w-3 h-3 rounded-full bg-blue-500 mx-auto -mt-5 relative z-10" />
|
|
<p className="text-xs text-muted-foreground mt-2">Inicio</p>
|
|
<p className="text-sm font-medium">{formatDate(project.startDate)}</p>
|
|
</div>
|
|
<div className="text-center">
|
|
<div className={`w-3 h-3 rounded-full mx-auto -mt-5 relative z-10 ${project.progress >= 100 ? "bg-green-500" : "bg-gray-300 dark:bg-gray-600"}`} />
|
|
<p className="text-xs text-muted-foreground mt-2">Fin previsto</p>
|
|
<p className="text-sm font-medium">{formatDate(project.endDate)}</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="mt-6 flex items-center justify-center gap-6 text-sm">
|
|
<div className="flex items-center gap-2">
|
|
<Clock className="h-4 w-4 text-muted-foreground" />
|
|
<span className="text-muted-foreground">Duración:</span>
|
|
<span className="font-medium">{duration} días</span>
|
|
</div>
|
|
<div className="flex items-center gap-2">
|
|
<span className="text-muted-foreground">Progreso:</span>
|
|
<span className="font-medium">{project.progress}%</span>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
|
|
{/* Sidebar - 1/3 */}
|
|
<div className="space-y-6">
|
|
{/* Detalles generales */}
|
|
<Card>
|
|
<CardHeader className="pb-3">
|
|
<CardTitle className="text-base">Detalles</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
{/* Cliente */}
|
|
{project.client !== "Sin cliente" && (
|
|
<>
|
|
<div className="flex items-start gap-3">
|
|
<Building2 className="h-4 w-4 text-muted-foreground mt-0.5" />
|
|
<div>
|
|
<p className="text-xs text-muted-foreground">Cliente</p>
|
|
<p className="text-sm font-medium">{project.client}</p>
|
|
</div>
|
|
</div>
|
|
<Separator />
|
|
</>
|
|
)}
|
|
|
|
{/* Referencia */}
|
|
<div className="flex items-start gap-3">
|
|
<Hash className="h-4 w-4 text-muted-foreground mt-0.5" />
|
|
<div>
|
|
<p className="text-xs text-muted-foreground">Referencia</p>
|
|
<p className="text-sm font-medium font-mono">{project.ref}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<Separator />
|
|
|
|
{/* ID */}
|
|
<div className="flex items-start gap-3">
|
|
<Hash className="h-4 w-4 text-muted-foreground mt-0.5" />
|
|
<div>
|
|
<p className="text-xs text-muted-foreground">ID del proyecto</p>
|
|
<p className="text-sm font-medium font-mono">{project.id}</p>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Fechas */}
|
|
<Card>
|
|
<CardHeader className="pb-3">
|
|
<CardTitle className="text-base">Fechas</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
<div className="flex items-start gap-3">
|
|
<Calendar className="h-4 w-4 text-muted-foreground mt-0.5" />
|
|
<div>
|
|
<p className="text-xs text-muted-foreground">Fecha de inicio</p>
|
|
<p className="text-sm font-medium">{formatDate(project.startDate)}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<Separator />
|
|
|
|
<div className="flex items-start gap-3">
|
|
<Calendar className="h-4 w-4 text-muted-foreground mt-0.5" />
|
|
<div>
|
|
<p className="text-xs text-muted-foreground">Fecha de fin</p>
|
|
<p className="text-sm font-medium">{formatDate(project.endDate)}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<Separator />
|
|
|
|
<div className="flex items-start gap-3">
|
|
<Clock className="h-4 w-4 text-muted-foreground mt-0.5" />
|
|
<div>
|
|
<p className="text-xs text-muted-foreground">Creado</p>
|
|
<p className="text-sm font-medium">{formatDate(project.createdAt)}</p>
|
|
</div>
|
|
</div>
|
|
|
|
<Separator />
|
|
|
|
<div className="flex items-start gap-3">
|
|
<Clock className="h-4 w-4 text-muted-foreground mt-0.5" />
|
|
<div>
|
|
<p className="text-xs text-muted-foreground">Última actualización</p>
|
|
<p className="text-sm font-medium">{formatDate(project.updatedAt)}</p>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|