155 lines
5.6 KiB
TypeScript
155 lines
5.6 KiB
TypeScript
"use client";
|
|
|
|
import Link from 'next/link';
|
|
import { Calendar, DollarSign, TrendingUp, FileText } from 'lucide-react';
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
|
import { Badge } from '@/components/ui/badge';
|
|
import { Avatar, AvatarFallback } from '@/components/ui/avatar';
|
|
import { Project, ProjectStatus } from '@/types/project';
|
|
|
|
interface ProjectCardProps {
|
|
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 con estilos directos
|
|
function StatusBadge({ status }: { status: ProjectStatus }) {
|
|
if (status === '0') {
|
|
return (
|
|
<Badge variant="outline" className="mt-3 border bg-gray-100 text-gray-800 border-gray-200">
|
|
Borrador
|
|
</Badge>
|
|
);
|
|
}
|
|
|
|
if (status === '1') {
|
|
return (
|
|
<Badge variant="outline" className="mt-3 border bg-blue-100 text-blue-800 border-blue-200">
|
|
Abierto
|
|
</Badge>
|
|
);
|
|
}
|
|
|
|
if (status === '2') {
|
|
return (
|
|
<Badge variant="outline" className="mt-3 border bg-red-100 text-red-800 border-red-200">
|
|
Cerrado
|
|
</Badge>
|
|
);
|
|
}
|
|
|
|
// Fallback
|
|
return (
|
|
<Badge variant="outline" className="mt-3 border bg-gray-100 text-gray-800 border-gray-200">
|
|
Desconocido
|
|
</Badge>
|
|
);
|
|
}
|
|
|
|
export default function ProjectCard({ project }: ProjectCardProps) {
|
|
const initials = getInitials(project.name);
|
|
|
|
return (
|
|
<Link href={`/proyectos/${project.id}`}>
|
|
<Card className="border-gray-200 hover:shadow-lg transition-all duration-300 hover:-translate-y-1 cursor-pointer">
|
|
<CardHeader>
|
|
<div className="flex items-start justify-between">
|
|
<div className="flex items-center gap-3">
|
|
<Avatar className="w-12 h-12">
|
|
<AvatarFallback className="bg-gradient-to-br from-blue-500 to-purple-600 text-white font-semibold">
|
|
{initials}
|
|
</AvatarFallback>
|
|
</Avatar>
|
|
<div>
|
|
<CardTitle className="text-lg text-gray-900">{project.name}</CardTitle>
|
|
<CardDescription className="text-sm">{project.ref}</CardDescription>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<StatusBadge status={project.status} />
|
|
</CardHeader>
|
|
<CardContent>
|
|
{/* Description */}
|
|
{project.description && (
|
|
<div className="mb-4">
|
|
<div className="flex items-center gap-1 text-gray-500 mb-1">
|
|
<FileText className="w-3 h-3" />
|
|
<span className="text-xs">Descripción</span>
|
|
</div>
|
|
<p className="text-sm text-gray-700 line-clamp-2">{project.description}</p>
|
|
</div>
|
|
)}
|
|
|
|
{/* Progress Bar */}
|
|
<div className="mb-4">
|
|
<div className="flex items-center justify-between mb-2">
|
|
<span className="text-xs font-medium text-gray-600">Progreso</span>
|
|
<span className="text-xs font-semibold text-gray-900">{project.progress}%</span>
|
|
</div>
|
|
<div className="w-full bg-gray-200 rounded-full h-2">
|
|
<div
|
|
className="bg-gradient-to-r from-blue-500 to-purple-600 h-2 rounded-full transition-all duration-500"
|
|
style={{ width: `${project.progress}%` }}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Budget Info */}
|
|
<div className="grid grid-cols-2 gap-4 mb-4">
|
|
<div>
|
|
<div className="flex items-center gap-1 text-gray-500 mb-1">
|
|
<DollarSign className="w-3 h-3" />
|
|
<span className="text-xs">Presupuesto</span>
|
|
</div>
|
|
<p className="text-sm font-semibold text-gray-900">
|
|
€{project.budget.toLocaleString('es-ES', { minimumFractionDigits: 0, maximumFractionDigits: 0 })}
|
|
</p>
|
|
</div>
|
|
<div>
|
|
<div className="flex items-center gap-1 text-gray-500 mb-1">
|
|
<TrendingUp className="w-3 h-3" />
|
|
<span className="text-xs">Estimado</span>
|
|
</div>
|
|
<p className="text-sm font-semibold text-gray-900">
|
|
€{project.spent.toLocaleString('es-ES', { minimumFractionDigits: 0, maximumFractionDigits: 0 })}
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Footer Info */}
|
|
<div className="pt-4 border-t border-gray-100 flex items-center justify-between">
|
|
<div className="flex items-center gap-1 text-gray-500">
|
|
<Calendar className="w-4 h-4" />
|
|
<span className="text-xs">
|
|
{new Date(project.startDate).toLocaleDateString('es-ES', { day: '2-digit', month: 'short', year: 'numeric' })}
|
|
</span>
|
|
</div>
|
|
<div className="flex items-center gap-1 text-gray-500">
|
|
<Calendar className="w-4 h-4" />
|
|
<span className="text-xs">
|
|
{new Date(project.endDate).toLocaleDateString('es-ES', { day: '2-digit', month: 'short', year: 'numeric' })}
|
|
</span>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Cliente */}
|
|
{project.client !== 'Sin cliente' && (
|
|
<div className="mt-3 pt-3 border-t border-gray-100">
|
|
<span className="text-xs text-gray-500">Cliente: </span>
|
|
<span className="text-xs font-medium text-gray-700">{project.client}</span>
|
|
</div>
|
|
)}
|
|
</CardContent>
|
|
</Card>
|
|
</Link>
|
|
);
|
|
} |