import { jsPDF } from "jspdf"; import autoTable from "jspdf-autotable"; import { Project, STATUS_CONFIG } from "@/types/project"; import { Task, TASK_STATUS_CONFIG, TASK_PRIORITY_CONFIG, TaskStatus, TaskPriority } from "@/types/task"; // Formateadores function formatCurrency(amount: number): string { return new Intl.NumberFormat("es-ES", { style: "currency", currency: "EUR", minimumFractionDigits: 0, maximumFractionDigits: 0, }).format(amount); } function formatDate(dateString: string | null | undefined): string { if (!dateString) return "-"; try { return new Date(dateString).toLocaleDateString("es-ES", { day: "2-digit", month: "short", year: "numeric", }); } catch { return "-"; } } function getStatusLabel(status: string): string { return STATUS_CONFIG[status as keyof typeof STATUS_CONFIG]?.label || status; } function getTaskStatusLabel(status: TaskStatus): string { return TASK_STATUS_CONFIG[status]?.label || status; } function getTaskPriorityLabel(priority: TaskPriority): string { return TASK_PRIORITY_CONFIG[priority]?.label || priority; } /** * Exporta una lista de proyectos a PDF (vista resumen) */ export function exportProjectsToPDF(projects: Project[]): void { const doc = new jsPDF(); const pageWidth = doc.internal.pageSize.getWidth(); // Título doc.setFontSize(20); doc.setTextColor(59, 130, 246); // blue-500 doc.text("Listado de Proyectos", pageWidth / 2, 20, { align: "center" }); // Fecha de generación doc.setFontSize(10); doc.setTextColor(100); doc.text( `Generado el ${new Date().toLocaleDateString("es-ES", { day: "numeric", month: "long", year: "numeric", hour: "2-digit", minute: "2-digit", })}`, pageWidth / 2, 28, { align: "center" } ); // Resumen doc.setFontSize(11); doc.setTextColor(60); doc.text(`Total de proyectos: ${projects.length}`, 14, 40); // Tabla de proyectos const tableData = projects.map((project) => [ project.ref, project.name, project.client, getStatusLabel(project.status), `${project.progress.toFixed(0)}%`, formatCurrency(project.budget), formatDate(project.startDate), formatDate(project.endDate), ]); autoTable(doc, { startY: 48, head: [ ["Ref", "Nombre", "Cliente", "Estado", "Progreso", "Presupuesto", "Inicio", "Fin"], ], body: tableData, styles: { fontSize: 9, cellPadding: 3, }, headStyles: { fillColor: [59, 130, 246], // blue-500 textColor: 255, fontStyle: "bold", }, alternateRowStyles: { fillColor: [248, 250, 252], // slate-50 }, columnStyles: { 0: { cellWidth: 20 }, // Ref 1: { cellWidth: 40 }, // Nombre 2: { cellWidth: 30 }, // Cliente 3: { cellWidth: 22 }, // Estado 4: { cellWidth: 18 }, // Progreso 5: { cellWidth: 25 }, // Presupuesto 6: { cellWidth: 22 }, // Inicio 7: { cellWidth: 22 }, // Fin }, }); // Guardar const fileName = `proyectos_${new Date().toISOString().split("T")[0]}.pdf`; doc.save(fileName); } /** * Exporta un proyecto con sus tareas a PDF (vista detallada) */ export function exportProjectDetailToPDF(project: Project, tasks: Task[]): void { const doc = new jsPDF(); const pageWidth = doc.internal.pageSize.getWidth(); // === HEADER === doc.setFontSize(22); doc.setTextColor(59, 130, 246); // blue-500 doc.text(project.name, pageWidth / 2, 20, { align: "center" }); doc.setFontSize(12); doc.setTextColor(100); doc.text(`Referencia: ${project.ref}`, pageWidth / 2, 28, { align: "center" }); // Fecha de generación doc.setFontSize(9); doc.text( `Generado el ${new Date().toLocaleDateString("es-ES", { day: "numeric", month: "long", year: "numeric", hour: "2-digit", minute: "2-digit", })}`, pageWidth / 2, 35, { align: "center" } ); // === INFORMACIÓN DEL PROYECTO === let yPos = 48; doc.setFontSize(14); doc.setTextColor(30); doc.text("Información del Proyecto", 14, yPos); yPos += 8; // Tabla de información const projectInfo = [ ["Cliente", project.client || "-"], ["Estado", getStatusLabel(project.status)], ["Progreso", `${project.progress.toFixed(0)}%`], ["Presupuesto", formatCurrency(project.budget)], ["Fecha de inicio", formatDate(project.startDate)], ["Fecha de fin", formatDate(project.endDate)], ]; autoTable(doc, { startY: yPos, body: projectInfo, styles: { fontSize: 10, cellPadding: 4, }, columnStyles: { 0: { fontStyle: "bold", cellWidth: 45, textColor: [100, 100, 100] }, 1: { cellWidth: 60 }, }, theme: "plain", margin: { left: 14 }, }); // Obtener posición después de la tabla yPos = (doc as jsPDF & { lastAutoTable?: { finalY: number } }).lastAutoTable?.finalY || yPos + 50; // Descripción si existe if (project.description) { yPos += 10; doc.setFontSize(14); doc.setTextColor(30); doc.text("Descripción", 14, yPos); yPos += 6; doc.setFontSize(10); doc.setTextColor(60); const descriptionLines = doc.splitTextToSize(project.description, pageWidth - 28); doc.text(descriptionLines, 14, yPos); yPos += descriptionLines.length * 5 + 5; } // === TAREAS === if (tasks.length > 0) { yPos += 10; // Verificar si necesitamos nueva página if (yPos > 250) { doc.addPage(); yPos = 20; } doc.setFontSize(14); doc.setTextColor(30); doc.text(`Tareas (${tasks.length})`, 14, yPos); yPos += 8; // Tabla de tareas const taskData = tasks.map((task) => [ task.ref, task.title, getTaskStatusLabel(task.status), getTaskPriorityLabel(task.priority), `${task.progress}%`, formatDate(task.startDate), formatDate(task.endDate), ]); autoTable(doc, { startY: yPos, head: [["Ref", "Tarea", "Estado", "Prioridad", "Progreso", "Inicio", "Fin"]], body: taskData, styles: { fontSize: 8, cellPadding: 3, }, headStyles: { fillColor: [147, 51, 234], // purple-600 textColor: 255, fontStyle: "bold", }, alternateRowStyles: { fillColor: [248, 250, 252], // slate-50 }, columnStyles: { 0: { cellWidth: 18 }, // Ref 1: { cellWidth: 50 }, // Tarea 2: { cellWidth: 25 }, // Estado 3: { cellWidth: 22 }, // Prioridad 4: { cellWidth: 18 }, // Progreso 5: { cellWidth: 22 }, // Inicio 6: { cellWidth: 22 }, // Fin }, }); } else { yPos += 15; doc.setFontSize(11); doc.setTextColor(100); doc.text("Este proyecto no tiene tareas asignadas.", 14, yPos); } // === FOOTER === const pageCount = doc.getNumberOfPages(); for (let i = 1; i <= pageCount; i++) { doc.setPage(i); doc.setFontSize(8); doc.setTextColor(150); doc.text( `Página ${i} de ${pageCount}`, pageWidth / 2, doc.internal.pageSize.getHeight() - 10, { align: "center" } ); } // Guardar const fileName = `proyecto_${project.ref}_${new Date().toISOString().split("T")[0]}.pdf`; doc.save(fileName); }