306 lines
12 KiB
TypeScript
306 lines
12 KiB
TypeScript
"use client";
|
|
|
|
import { ArrowLeft, Calendar, DollarSign, TrendingUp, FileText, Building2 } from 'lucide-react';
|
|
import { useRouter } from 'next/navigation';
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
import { Badge } from '@/components/ui/badge';
|
|
import { Avatar, AvatarFallback } from '@/components/ui/avatar';
|
|
import { Button } from '@/components/ui/button';
|
|
import { Separator } from '@/components/ui/separator';
|
|
import { Project, ProjectStatus } from '@/types/project';
|
|
|
|
interface ProjectDetailProps {
|
|
project: Project;
|
|
}
|
|
|
|
// Función para generar iniciales
|
|
function getInitials(name: string): string {
|
|
return name
|
|
.split(' ')
|
|
.map(word => word[0])
|
|
.join('')
|
|
.toUpperCase()
|
|
.slice(0, 2);
|
|
}
|
|
|
|
// Componente para el badge de estado
|
|
function StatusBadge({ status }: { status: ProjectStatus }) {
|
|
if (status === '0') {
|
|
return (
|
|
<Badge variant="outline" className="border bg-gray-100 text-gray-800 border-gray-200 text-base px-4 py-1">
|
|
Borrador
|
|
</Badge>
|
|
);
|
|
}
|
|
|
|
if (status === '1') {
|
|
return (
|
|
<Badge variant="outline" className="border bg-blue-100 text-blue-800 border-blue-200 text-base px-4 py-1">
|
|
Abierto
|
|
</Badge>
|
|
);
|
|
}
|
|
|
|
if (status === '2') {
|
|
return (
|
|
<Badge variant="outline" className="border bg-red-100 text-red-800 border-red-200 text-base px-4 py-1">
|
|
Cerrado
|
|
</Badge>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Badge variant="outline" className="border bg-gray-100 text-gray-800 border-gray-200 text-base px-4 py-1">
|
|
Desconocido
|
|
</Badge>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Componente de detalle completo de un proyecto
|
|
* Muestra toda la información disponible del proyecto
|
|
*/
|
|
export default function ProjectDetail({ project }: ProjectDetailProps) {
|
|
const router = useRouter();
|
|
const initials = getInitials(project.name);
|
|
|
|
return (
|
|
<div className="min-h-screen bg-gradient-to-br from-gray-50 to-gray-100">
|
|
{/* Header */}
|
|
<div className="bg-white border-b border-gray-200">
|
|
<div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
|
|
<Button
|
|
variant="ghost"
|
|
onClick={() => router.back()}
|
|
className="mb-4"
|
|
>
|
|
<ArrowLeft className="w-4 h-4 mr-2" />
|
|
Volver
|
|
</Button>
|
|
|
|
<div className="flex items-start gap-6">
|
|
<Avatar className="w-20 h-20">
|
|
<AvatarFallback className="bg-gradient-to-br from-blue-500 to-purple-600 text-white font-semibold text-2xl">
|
|
{initials}
|
|
</AvatarFallback>
|
|
</Avatar>
|
|
|
|
<div className="flex-1">
|
|
<div className="flex items-start justify-between">
|
|
<div>
|
|
<h1 className="text-3xl font-bold text-gray-900 mb-2">{project.name}</h1>
|
|
<p className="text-gray-500 mb-3">{project.ref}</p>
|
|
<StatusBadge status={project.status} />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Content */}
|
|
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
|
|
<div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
|
|
{/* Main Content */}
|
|
<div className="lg:col-span-2 space-y-6">
|
|
{/* Descripción */}
|
|
{project.description && (
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<FileText className="w-5 h-5" />
|
|
Descripción
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<p className="text-gray-700 whitespace-pre-wrap">{project.description}</p>
|
|
</CardContent>
|
|
</Card>
|
|
)}
|
|
|
|
{/* Progreso */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<TrendingUp className="w-5 h-5" />
|
|
Progreso del Proyecto
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="space-y-4">
|
|
<div className="flex items-center justify-between">
|
|
<span className="text-sm font-medium text-gray-600">Avance Total</span>
|
|
<span className="text-2xl font-bold text-gray-900">{project.progress}%</span>
|
|
</div>
|
|
<div className="w-full bg-gray-200 rounded-full h-4">
|
|
<div
|
|
className="bg-gradient-to-r from-blue-500 to-purple-600 h-4 rounded-full transition-all duration-500"
|
|
style={{ width: `${project.progress}%` }}
|
|
/>
|
|
</div>
|
|
<div className="grid grid-cols-2 gap-4 pt-4">
|
|
<div className="text-center p-4 bg-gray-50 rounded-lg">
|
|
<p className="text-sm text-gray-600 mb-1">Estado</p>
|
|
<p className="text-lg font-semibold text-gray-900">
|
|
{project.status === '0' ? 'Borrador' : project.status === '1' ? 'En Progreso' : 'Finalizado'}
|
|
</p>
|
|
</div>
|
|
<div className="text-center p-4 bg-gray-50 rounded-lg">
|
|
<p className="text-sm text-gray-600 mb-1">Completado</p>
|
|
<p className="text-lg font-semibold text-gray-900">
|
|
{project.progress === 100 ? 'Sí' : 'No'}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Presupuesto */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="flex items-center gap-2">
|
|
<DollarSign className="w-5 h-5" />
|
|
Información Financiera
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
|
<div className="space-y-2">
|
|
<p className="text-sm text-gray-600">Presupuesto Total</p>
|
|
<p className="text-3xl font-bold text-gray-900">
|
|
€{project.budget.toLocaleString('es-ES', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
|
|
</p>
|
|
</div>
|
|
<div className="space-y-2">
|
|
<p className="text-sm text-gray-600">Estimado Gastado</p>
|
|
<p className="text-3xl font-bold text-blue-600">
|
|
€{project.spent.toLocaleString('es-ES', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
<Separator className="my-4" />
|
|
<div className="flex items-center justify-between p-4 bg-gray-50 rounded-lg">
|
|
<span className="text-sm font-medium text-gray-600">Restante</span>
|
|
<span className="text-xl font-bold text-green-600">
|
|
€{(project.budget - project.spent).toLocaleString('es-ES', { minimumFractionDigits: 2, maximumFractionDigits: 2 })}
|
|
</span>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
|
|
{/* Sidebar */}
|
|
<div className="space-y-6">
|
|
{/* Información General */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="text-lg">Información General</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-4">
|
|
{/* Cliente */}
|
|
{project.client !== 'Sin cliente' && (
|
|
<div className="flex items-start gap-3">
|
|
<Building2 className="w-5 h-5 text-gray-400 mt-0.5" />
|
|
<div>
|
|
<p className="text-sm text-gray-600">Cliente</p>
|
|
<p className="font-medium text-gray-900">{project.client}</p>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
<Separator />
|
|
|
|
{/* Fecha de Inicio */}
|
|
<div className="flex items-start gap-3">
|
|
<Calendar className="w-5 h-5 text-gray-400 mt-0.5" />
|
|
<div>
|
|
<p className="text-sm text-gray-600">Fecha de Inicio</p>
|
|
<p className="font-medium text-gray-900">
|
|
{new Date(project.startDate).toLocaleDateString('es-ES', {
|
|
day: 'numeric',
|
|
month: 'long',
|
|
year: 'numeric'
|
|
})}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<Separator />
|
|
|
|
{/* Fecha de Fin */}
|
|
<div className="flex items-start gap-3">
|
|
<Calendar className="w-5 h-5 text-gray-400 mt-0.5" />
|
|
<div>
|
|
<p className="text-sm text-gray-600">Fecha de Fin</p>
|
|
<p className="font-medium text-gray-900">
|
|
{new Date(project.endDate).toLocaleDateString('es-ES', {
|
|
day: 'numeric',
|
|
month: 'long',
|
|
year: 'numeric'
|
|
})}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<Separator />
|
|
|
|
{/* Duración */}
|
|
<div className="flex items-start gap-3">
|
|
<Calendar className="w-5 h-5 text-gray-400 mt-0.5" />
|
|
<div>
|
|
<p className="text-sm text-gray-600">Duración</p>
|
|
<p className="font-medium text-gray-900">
|
|
{Math.ceil((new Date(project.endDate).getTime() - new Date(project.startDate).getTime()) / (1000 * 60 * 60 * 24))} días
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
{/* Metadatos */}
|
|
<Card>
|
|
<CardHeader>
|
|
<CardTitle className="text-lg">Metadatos</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="space-y-3 text-sm">
|
|
<div>
|
|
<p className="text-gray-600">ID del Proyecto</p>
|
|
<p className="font-mono text-gray-900">{project.id}</p>
|
|
</div>
|
|
<Separator />
|
|
<div>
|
|
<p className="text-gray-600">Referencia</p>
|
|
<p className="font-mono text-gray-900">{project.ref}</p>
|
|
</div>
|
|
<Separator />
|
|
<div>
|
|
<p className="text-gray-600">Creado</p>
|
|
<p className="text-gray-900">
|
|
{new Date(project.createdAt).toLocaleDateString('es-ES', {
|
|
day: 'numeric',
|
|
month: 'short',
|
|
year: 'numeric'
|
|
})}
|
|
</p>
|
|
</div>
|
|
<Separator />
|
|
<div>
|
|
<p className="text-gray-600">Última Actualización</p>
|
|
<p className="text-gray-900">
|
|
{new Date(project.updatedAt).toLocaleDateString('es-ES', {
|
|
day: 'numeric',
|
|
month: 'short',
|
|
year: 'numeric'
|
|
})}
|
|
</p>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|