#!/usr/bin/env node /** * Script para crear tareas de prueba en proyectos de Dolibarr * * REQUISITO: El servidor de Next.js debe estar corriendo (npm run dev) * Uso: node scripts/seed-tasks.js */ const NEXT_API_URL = 'http://localhost:3000/api/dolibarr'; // Plantillas de tareas variadas para cada proyecto const taskTemplates = [ // Tareas de análisis/planificación { label: "Análisis de requisitos", description: "Documentar todos los requisitos funcionales y no funcionales del proyecto con el cliente.", planned_workload: 28800, // 8 horas en segundos progress: 100, priority: 2, }, { label: "Diseño de arquitectura", description: "Definir la arquitectura técnica, selección de tecnologías y patrones de diseño a utilizar.", planned_workload: 36000, // 10 horas progress: 100, priority: 3, }, { label: "Creación de wireframes", description: "Diseñar los wireframes y mockups de las principales pantallas de la aplicación.", planned_workload: 21600, // 6 horas progress: 80, priority: 2, }, // Tareas de desarrollo { label: "Configuración del entorno de desarrollo", description: "Preparar repositorio, CI/CD, entornos de desarrollo y staging.", planned_workload: 14400, // 4 horas progress: 100, priority: 3, }, { label: "Desarrollo del backend", description: "Implementar la API REST, modelos de datos y lógica de negocio del servidor.", planned_workload: 72000, // 20 horas progress: 60, priority: 3, }, { label: "Desarrollo del frontend", description: "Implementar la interfaz de usuario con componentes, estados y conexión con API.", planned_workload: 64800, // 18 horas progress: 45, priority: 3, }, { label: "Integración con servicios externos", description: "Conectar con APIs de terceros, pasarelas de pago y servicios cloud.", planned_workload: 28800, // 8 horas progress: 30, priority: 2, }, // Tareas de testing { label: "Pruebas unitarias", description: "Escribir y ejecutar tests unitarios para componentes críticos del sistema.", planned_workload: 21600, // 6 horas progress: 25, priority: 2, }, { label: "Pruebas de integración", description: "Realizar pruebas de integración entre módulos y con servicios externos.", planned_workload: 18000, // 5 horas progress: 10, priority: 2, }, { label: "QA y corrección de bugs", description: "Ejecutar plan de QA, documentar bugs encontrados y corregirlos.", planned_workload: 36000, // 10 horas progress: 0, priority: 1, }, // Tareas de documentación { label: "Documentación técnica", description: "Crear documentación de API, guías de instalación y arquitectura del sistema.", planned_workload: 14400, // 4 horas progress: 15, priority: 1, }, { label: "Manual de usuario", description: "Redactar el manual de usuario con capturas y tutoriales paso a paso.", planned_workload: 10800, // 3 horas progress: 0, priority: 1, }, // Tareas de despliegue { label: "Configuración de producción", description: "Preparar servidores, certificados SSL, dominios y configuraciones de producción.", planned_workload: 18000, // 5 horas progress: 0, priority: 3, }, { label: "Despliegue inicial", description: "Realizar el despliegue a producción y verificar el correcto funcionamiento.", planned_workload: 7200, // 2 horas progress: 0, priority: 3, }, { label: "Formación al cliente", description: "Sesión de formación al equipo del cliente sobre el uso del sistema.", planned_workload: 10800, // 3 horas progress: 0, priority: 2, }, ]; /** * Obtener todos los proyectos existentes */ async function getProjects() { const url = `${NEXT_API_URL}/projects`; const response = await fetch(url, { method: 'GET', headers: { 'Content-Type': 'application/json', }, }); if (!response.ok) { throw new Error(`Error obteniendo proyectos: ${response.status}`); } return response.json(); } // Contador global para generar refs únicos let taskCounter = 0; /** * Generar referencia única para una tarea */ function generateTaskRef(projectId) { taskCounter++; const timestamp = Date.now().toString(36).toUpperCase(); return `TASK-P${projectId}-${String(taskCounter).padStart(3, '0')}`; } /** * Crear una tarea en un proyecto */ async function createTask(projectId, taskData) { const url = `${NEXT_API_URL}/tasks`; // Calcular fechas basadas en la fecha actual const now = new Date(); const startDate = new Date(now); startDate.setDate(startDate.getDate() - Math.floor(Math.random() * 30)); // Hace 0-30 días const endDate = new Date(startDate); endDate.setDate(endDate.getDate() + Math.floor(Math.random() * 30) + 7); // 7-37 días después const payload = { ref: generateTaskRef(projectId), // Campo requerido por Dolibarr fk_project: String(projectId), // Dolibarr espera string label: taskData.label, description: taskData.description, planned_workload: taskData.planned_workload, progress: taskData.progress, priority: taskData.priority, dateo: Math.floor(startDate.getTime() / 1000), // Fecha inicio planificada datee: Math.floor(endDate.getTime() / 1000), // Fecha fin planificada date_start: taskData.progress > 0 ? Math.floor(startDate.getTime() / 1000) : null, date_end: taskData.progress >= 100 ? Math.floor(new Date().getTime() / 1000) : null, }; const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(payload), }); if (!response.ok) { const errorData = await response.json().catch(() => ({})); throw new Error(`Error ${response.status}: ${errorData.error || 'Unknown error'}`); } return response.json(); } /** * Seleccionar tareas aleatorias para un proyecto */ function selectTasksForProject(projectProgress, count = 6) { // Mezclar las tareas aleatoriamente const shuffled = [...taskTemplates].sort(() => Math.random() - 0.5); // Seleccionar las primeras 'count' tareas const selected = shuffled.slice(0, count); // Ajustar el progreso de las tareas según el progreso del proyecto return selected.map((task, index) => { let adjustedProgress = task.progress; // Si el proyecto tiene poco progreso, reducir el progreso de las tareas if (projectProgress < 30) { adjustedProgress = Math.min(task.progress, 30 + Math.random() * 20); } else if (projectProgress >= 100) { // Si el proyecto está completo, completar más tareas adjustedProgress = index < count - 1 ? 100 : Math.max(task.progress, 80); } else { // Ajustar proporcionalmente const factor = projectProgress / 50; adjustedProgress = Math.min(100, Math.round(task.progress * factor)); } return { ...task, progress: Math.round(adjustedProgress), }; }); } /** * Script principal */ async function main() { console.log('\n📋 SEED DE TAREAS PARA PROYECTOS DE DOLIBARR\n'); console.log('═'.repeat(60)); console.log(`📡 API Next.js: ${NEXT_API_URL}`); console.log(`📝 Plantillas de tareas: ${taskTemplates.length}`); console.log('═'.repeat(60)); console.log(''); // Verificar conexión con Next.js console.log('🔍 Verificando conexión con Next.js...'); try { const testResponse = await fetch('http://localhost:3000'); if (!testResponse.ok && testResponse.status !== 404) { throw new Error('Server not responding'); } console.log('✅ Servidor Next.js está corriendo\n'); } catch (error) { console.error('❌ ERROR: No se puede conectar al servidor Next.js'); console.error(' Por favor, ejecuta "npm run dev" en otra terminal primero.\n'); process.exit(1); } // Obtener proyectos existentes console.log('📂 Obteniendo proyectos existentes...'); let projects; try { projects = await getProjects(); console.log(`✅ Encontrados ${projects.length} proyectos\n`); } catch (error) { console.error('❌ Error obteniendo proyectos:', error.message); process.exit(1); } if (projects.length === 0) { console.log('⚠️ No hay proyectos. Ejecuta primero: npm run seed\n'); process.exit(0); } let totalTasks = 0; let totalErrors = 0; const TASKS_PER_PROJECT = 6; for (let i = 0; i < projects.length; i++) { const project = projects[i]; const projectId = project.id; const projectTitle = project.title || project.ref || `Proyecto ${projectId}`; const projectProgress = parseFloat(project.opp_percent || '0'); console.log(`\n[${i + 1}/${projects.length}] 📁 ${projectTitle}`); console.log(` ID: ${projectId} | Progreso: ${projectProgress}%`); console.log(' Creando tareas:'); // Seleccionar tareas para este proyecto const tasksToCreate = selectTasksForProject(projectProgress, TASKS_PER_PROJECT); for (let j = 0; j < tasksToCreate.length; j++) { const task = tasksToCreate[j]; try { const taskId = await createTask(projectId, task); const progressBar = '█'.repeat(Math.floor(task.progress / 10)) + '░'.repeat(10 - Math.floor(task.progress / 10)); console.log(` ✅ [${progressBar}] ${task.progress}% - ${task.label}`); totalTasks++; // Pequeña pausa para no saturar la API await new Promise(resolve => setTimeout(resolve, 200)); } catch (error) { console.log(` ❌ Error: ${task.label} - ${error.message}`); totalErrors++; } } } // Resumen final console.log('\n'); console.log('═'.repeat(60)); console.log('✨ PROCESO COMPLETADO\n'); console.log(`📊 Proyectos procesados: ${projects.length}`); console.log(`✅ Tareas creadas: ${totalTasks}`); console.log(`❌ Errores: ${totalErrors}`); console.log('═'.repeat(60)); if (totalTasks > 0) { console.log('\n💡 ¡Recarga tu aplicación para ver las tareas!'); console.log(' Haz clic en cualquier proyecto para ver sus tareas.\n'); } } // Ejecutar main().catch(error => { console.error('\n❌ ERROR FATAL:', error.message); console.error('\nAsegúrate de que:'); console.error(' 1. El servidor Next.js está corriendo (npm run dev)'); console.error(' 2. Dolibarr está accesible'); console.error(' 3. Existen proyectos (ejecuta "npm run seed" primero)\n'); process.exit(1); });