trello_fake/components/task-detail/task-info.tsx

304 lines
8.6 KiB
TypeScript
Raw Permalink Normal View History

"use client";
import {
FileText,
Calendar,
User,
FolderOpen,
Tag,
Clock,
AlertTriangle,
CheckCircle2,
Info,
Hash
} from "lucide-react";
import Link from "next/link";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
import { Separator } from "@/components/ui/separator";
import {
Task,
TASK_STATUS_CONFIG,
TASK_PRIORITY_CONFIG
} from "@/types/task";
import { Project } from "@/types/project";
interface TaskInfoProps {
task: Task;
project: Project | null;
}
// Formatear fecha completa
function formatFullDate(dateString: string | null): string {
if (!dateString) return 'No definida';
return new Date(dateString).toLocaleDateString('es-ES', {
weekday: 'long',
day: '2-digit',
month: 'long',
year: 'numeric',
});
}
// Verificar si la tarea está vencida
function isOverdue(task: Task): boolean {
const endDate = task.endDate || task.plannedEndDate;
if (!endDate) return false;
return new Date(endDate) < new Date() && task.status !== '2';
}
// Item de información
function InfoItem({
icon: Icon,
label,
value,
className = "",
valueClassName = ""
}: {
icon: React.ElementType;
label: string;
value: React.ReactNode;
className?: string;
valueClassName?: string;
}) {
return (
<div className={`flex items-start gap-3 ${className}`}>
<div className="flex items-center justify-center w-8 h-8 rounded-lg bg-muted/50 shrink-0">
<Icon className="h-4 w-4 text-muted-foreground" />
</div>
<div className="min-w-0 flex-1">
<p className="text-xs text-muted-foreground uppercase tracking-wide mb-0.5">{label}</p>
<div className={`text-sm font-medium ${valueClassName}`}>{value}</div>
</div>
</div>
);
}
export function TaskInfo({ task, project }: TaskInfoProps) {
const overdue = isOverdue(task);
const statusConfig = TASK_STATUS_CONFIG[task.status];
const priorityConfig = TASK_PRIORITY_CONFIG[task.priority];
return (
<div className="grid gap-6 lg:grid-cols-2">
{/* Descripción */}
<Card className="lg:col-span-2">
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg">
<FileText className="h-5 w-5" />
Descripción
</CardTitle>
</CardHeader>
<CardContent>
{task.description ? (
<p className="text-sm text-muted-foreground whitespace-pre-wrap leading-relaxed">
{task.description}
</p>
) : (
<p className="text-sm text-muted-foreground/60 italic">
Esta tarea no tiene descripción.
</p>
)}
</CardContent>
</Card>
{/* Información General */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg">
<Info className="h-5 w-5" />
Información General
</CardTitle>
<CardDescription>Detalles básicos de la tarea</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<InfoItem
icon={Hash}
label="Referencia"
value={<span className="font-mono">{task.ref}</span>}
/>
<Separator />
<InfoItem
icon={Tag}
label="Estado"
value={
<Badge variant="outline" className={statusConfig.bgClass}>
{task.status === '2' && <CheckCircle2 className="h-3 w-3 mr-1" />}
{statusConfig.label}
</Badge>
}
/>
<InfoItem
icon={AlertTriangle}
label="Prioridad"
value={
task.priority !== '0' ? (
<Badge variant="outline" className={priorityConfig.bgClass}>
{priorityConfig.label}
</Badge>
) : (
<span className="text-muted-foreground">Sin prioridad asignada</span>
)
}
/>
<Separator />
<InfoItem
icon={FolderOpen}
label="Proyecto"
value={
project ? (
<Link
href={`/proyectos/${project.id}`}
className="text-blue-600 hover:text-blue-700 hover:underline transition-colors"
>
{project.name}
</Link>
) : (
<span className="text-muted-foreground">Proyecto #{task.projectId}</span>
)
}
/>
{task.parentTaskId && task.parentTaskId > 0 && (
<InfoItem
icon={FolderOpen}
label="Tarea padre"
value={
<Link
href={`/tareas/${task.parentTaskId}`}
className="text-blue-600 hover:text-blue-700 hover:underline transition-colors"
>
Ver tarea padre #{task.parentTaskId}
</Link>
}
/>
)}
</CardContent>
</Card>
{/* Fechas y Tiempos */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg">
<Calendar className="h-5 w-5" />
Fechas y Tiempos
</CardTitle>
<CardDescription>Cronograma y dedicación</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<InfoItem
icon={Calendar}
label="Fecha de creación"
value={formatFullDate(task.createdAt)}
/>
<Separator />
<InfoItem
icon={Calendar}
label="Inicio planificado"
value={formatFullDate(task.plannedStartDate)}
/>
<InfoItem
icon={Calendar}
label="Fin planificado"
value={formatFullDate(task.plannedEndDate)}
valueClassName={overdue ? 'text-red-500' : ''}
/>
{(task.startDate || task.endDate) && (
<>
<Separator />
{task.startDate && (
<InfoItem
icon={Calendar}
label="Inicio real"
value={formatFullDate(task.startDate)}
/>
)}
{task.endDate && (
<InfoItem
icon={Calendar}
label="Fin real"
value={formatFullDate(task.endDate)}
/>
)}
</>
)}
<Separator />
<InfoItem
icon={Clock}
label="Tiempo planificado"
value={task.plannedHours > 0 ? `${task.plannedHours} horas` : 'No estimado'}
/>
<InfoItem
icon={Clock}
label="Tiempo trabajado"
value={task.workedHours > 0 ? `${task.workedHours} horas` : 'Sin registrar'}
/>
</CardContent>
</Card>
{/* Metadatos */}
<Card className="lg:col-span-2">
<CardHeader>
<CardTitle className="flex items-center gap-2 text-lg">
<User className="h-5 w-5" />
Metadatos
</CardTitle>
<CardDescription>Información de seguimiento y auditoría</CardDescription>
</CardHeader>
<CardContent>
<div className="grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
<InfoItem
icon={User}
label="Creado por"
value={task.createdBy > 0 ? `Usuario #${task.createdBy}` : 'Sistema'}
/>
<InfoItem
icon={Calendar}
label="Creado"
value={formatFullDate(task.createdAt)}
/>
<InfoItem
icon={Calendar}
label="Última actualización"
value={formatFullDate(task.updatedAt)}
/>
<InfoItem
icon={Hash}
label="Orden"
value={task.order > 0 ? `Posición ${task.order}` : 'Sin ordenar'}
/>
</div>
{task.budget > 0 && (
<>
<Separator className="my-4" />
<InfoItem
icon={Tag}
label="Presupuesto asignado"
value={`${task.budget.toLocaleString('es-ES')} €`}
/>
</>
)}
</CardContent>
</Card>
</div>
);
}