feat: Enhance Dolibarr project management dashboard with new features and scripts

- Updated README.md to include detailed project description, features, installation steps, and troubleshooting tips.
- Added new scripts for seeding test projects via API and direct connection to Dolibarr.
- Introduced a diagnostic script to check the status of Dolibarr and Next.js services.
- Implemented project seeding scripts with realistic data and validation processes.
- Updated project TypeScript definitions to allow nullable date fields for better compatibility.
This commit is contained in:
Levi Planelles 2026-01-28 13:00:17 +01:00
parent ea708254e3
commit 997ff4d64d
8 changed files with 1220 additions and 23 deletions

216
QUICK_START.md Normal file
View File

@ -0,0 +1,216 @@
# 🚀 Guía Rápida de Uso
## Paso 1: Iniciar Dolibarr (Docker)
Si tienes Dolibarr en Docker:
```bash
docker start <nombre-contenedor-dolibarr>
# o
docker-compose up -d
```
Verifica que Dolibarr esté accesible en `http://localhost:8200`
---
## Paso 2: Iniciar la aplicación
```bash
# Asegúrate de estar en el directorio del proyecto
cd trello_fake
# Inicia el servidor de desarrollo
npm run dev
```
Espera a ver:
```
✓ Ready in 2.3s
○ Local: http://localhost:3000
```
---
## Paso 3: Crear proyectos de prueba
**Opción A: Usando el script (Recomendado)**
En una **nueva terminal** (dejando `npm run dev` corriendo):
```bash
npm run seed
```
Verás algo como:
```
🚀 SEED DE PROYECTOS DE PRUEBA PARA DOLIBARR
[1/12] Desarrollo de App Móvil
📈 Progreso: [▓▓▓▓▓▓░░░░] 65%
💰 Presupuesto: €45,000
✅ Creado con ID: 1
✓ Validado (estado: abierto)
...
✨ PROCESO COMPLETADO
✅ Proyectos creados exitosamente: 12
```
**Opción B: Crear proyectos manualmente en Dolibarr**
1. Accede a Dolibarr: `http://localhost:8200`
2. Login: admin / p4ssw0rd
3. Ve a Proyectos > Nuevo Proyecto
4. Completa el formulario y guarda
---
## Paso 4: Ver los proyectos
Abre tu navegador en: `http://localhost:3000`
Deberías ver:
- 📊 Dashboard con estadísticas
- 🎴 Proyectos en vista de grid (por defecto)
- 🔍 Barra de búsqueda funcional
- 🌓 Toggle de modo oscuro
---
## Paso 5: Explorar funcionalidades
### Vista de Lista
- Haz clic en el botón **☰** (lista) en el header
### Detalle de Proyecto
- Haz clic en cualquier tarjeta de proyecto
- Verás información completa: descripción, progreso, presupuesto, fechas
### Búsqueda
- Escribe en la barra de búsqueda
- Filtra por nombre, cliente o referencia
### Modo Oscuro
- Haz clic en el icono **☀️/🌙** en el header
- Elige: Claro, Oscuro o Sistema
---
## 🎯 Casos de Uso
### Ver proyectos completados
1. Busca proyectos con badge verde "Cerrado"
2. O con progreso del 100%
3. Ejemplos: "Dashboard Analítico BI", "Portal de Empleados"
### Ver proyectos en progreso
1. Busca proyectos con badge azul "Abierto"
2. Progreso entre 1% y 99%
3. Ejemplo: "App de Gestión de Proyectos" (75%)
### Ver borradores
1. Busca proyectos con badge gris "Borrador"
2. Progreso del 0%
3. Ejemplo: "Sistema CRM Personalizado"
### Comparar presupuestos
1. Cambia a vista de lista (☰)
2. Compara la columna "Presupuesto"
3. Proyecto más caro: "E-commerce con Marketplace" (€120k)
---
## 🐛 Solución de Problemas
### No veo proyectos
```bash
# 1. Verifica que Dolibarr esté corriendo
curl http://localhost:8200
# 2. Ejecuta el seed
npm run seed
# 3. Recarga la página (F5)
```
### Error 500 en la consola
- Revisa `.env.local`
- Verifica las credenciales de Dolibarr
- Comprueba que la API esté habilitada en Dolibarr
### Página en blanco
- Abre la consola del navegador (F12)
- Busca errores en rojo
- Verifica que `npm run dev` esté corriendo
---
## 🎨 Personalización
### Cambiar el número de proyectos de prueba
Edita `scripts/seed-projects-via-api.js`:
```javascript
// Comenta los proyectos que no quieras
const projectsData = [
// ... solo deja los que quieras
];
```
### Modificar datos de un proyecto
Edita el objeto correspondiente:
```javascript
{
title: "Tu Proyecto Personalizado",
ref: "PROJ-2024-XXX",
description: "Tu descripción...",
opp_amount: 75000, // Tu presupuesto
opp_percent: 80, // Tu progreso
// ...
}
```
Luego ejecuta:
```bash
npm run seed
```
---
## 🔄 Resetear todo
### Limpiar proyectos en Dolibarr
1. Accede a Dolibarr
2. Ve a cada proyecto
3. Elimina uno por uno
### Limpiar caché de Next.js
```bash
rm -rf .next
npm run dev
```
### Empezar de cero
```bash
# Detén el servidor (Ctrl+C)
rm -rf .next
npm run dev
npm run seed
```
---
## 📚 Referencias Rápidas
| Acción | Comando |
|--------|---------|
| Iniciar desarrollo | `npm run dev` |
| Crear proyectos | `npm run seed` |
| Build producción | `npm run build` |
| Ver logs | Ver terminal donde corre `npm run dev` |
| Ver API calls | F12 > Network > Fetch/XHR |
---
¡Listo! Ya puedes explorar tu dashboard de proyectos Dolibarr 🎉

199
README.md
View File

@ -2,27 +2,198 @@ This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-
![Preview](image.png)
## Dashboard de Proyectos Dolibarr
Aplicación web moderna para visualizar y gestionar proyectos de Dolibarr con una interfaz intuitiva y profesional.
### ✨ Características
- 📊 Vista de proyectos en Grid y Lista
- 🎨 Modo oscuro completo
- 📱 Diseño responsive
- 🔍 Búsqueda en tiempo real
- 📈 Estadísticas y KPIs
- 🔐 API segura (API key protegida en el backend)
- ⚡ Skeletons loading para mejor UX
- 📄 Página de detalle completa para cada proyecto
---
## 🚀 Getting Started
### Requisitos previos
- Node.js 18+
- Dolibarr con API habilitada
- Docker (opcional, para Dolibarr)
### Instalación
1. **Clonar el repositorio**
```bash
git clone <tu-repo>
cd trello_fake
```
2. **Instalar dependencias**
```bash
npm install
```
3. **Configurar variables de entorno**
Crea un archivo `.env.local` en la raíz:
```bash
# API de Dolibarr (segura - solo servidor)
DOLIBARR_API_URL=http://localhost:8200/api/index.php
DOLIBARR_API_KEY=tu-api-key-aqui
# Variables públicas
NEXT_PUBLIC_DOLIBARR_USER_ID=2
```
4. **Iniciar el servidor de desarrollo**
```bash
npm run dev
```
Abre [http://localhost:3000](http://localhost:3000) en tu navegador.
---
## 📊 Datos de Prueba
Si no tienes proyectos en tu Dolibarr, puedes generar datos de prueba:
```bash
# Con el servidor corriendo (npm run dev)
npm run seed
```
Este comando creará 12 proyectos de ejemplo con:
- Diferentes estados de progreso (0% - 100%)
- Presupuestos variados (€28k - €120k)
- Descripciones detalladas
- Fechas realistas
Ver más detalles en [scripts/README.md](scripts/README.md)
---
## 🏗️ Estructura del Proyecto
```
/
├── app/ # Next.js App Router
│ ├── api/dolibarr/[...path]/ # API Route proxy (protege la API key)
│ ├── proyectos/[id]/ # Página de detalle de proyecto
│ ├── layout.tsx # Layout principal
│ └── page.tsx # Página de inicio
├── components/
│ ├── dashboard/ # Componentes del dashboard
│ ├── ui/ # Componentes shadcn/ui
│ ├── theme-provider.tsx # Provider de tema
│ └── theme-toggle.tsx # Toggle de modo oscuro
├── lib/ # Utilidades y servicios
│ ├── dolibarrClient.ts # Cliente de API
│ ├── projectsService.ts # Servicio de proyectos
│ └── usersService.ts # Servicio de usuarios
├── types/ # Definiciones TypeScript
└── scripts/ # Scripts de utilidad
```
---
## 🛠️ Scripts Disponibles
```bash
npm run dev # Servidor de desarrollo
npm run build # Build de producción
npm run start # Servidor de producción
npm run lint # Ejecutar ESLint
npm run seed # Crear proyectos de prueba
```
---
## 🎨 Tecnologías
- **Framework:** Next.js 16 (App Router)
- **Lenguaje:** TypeScript 5
- **UI:** shadcn/ui + Radix UI
- **Estilos:** Tailwind CSS
- **Iconos:** Lucide React
- **Tema:** next-themes
- **API:** Dolibarr REST API
---
## 🔐 Seguridad
La aplicación implementa las siguientes medidas de seguridad:
- ✅ API key protegida en el servidor (NO expuesta al cliente)
- ✅ API Route proxy para todas las llamadas a Dolibarr
- ✅ Variables de entorno seguras
- ✅ Validación de datos de entrada
---
## 📖 Documentación Adicional
- [AGENTS.md](AGENTS.md) - Guías para agentes de IA y arquitectura del proyecto
- [scripts/README.md](scripts/README.md) - Documentación de scripts de utilidad
---
## 🐛 Troubleshooting
### Error: "Cannot connect to Dolibarr"
- Verifica que Dolibarr esté corriendo
- Revisa la URL en `.env.local`
- Comprueba que la API esté habilitada en Dolibarr
### No se muestran proyectos
- Ejecuta `npm run seed` para crear datos de prueba
- Verifica las credenciales de la API
- Revisa la consola del navegador para errores
### Modo oscuro no funciona
- Limpia la caché del navegador
- Verifica que ThemeProvider esté envolviendo la app
---
## 📝 Credenciales de Dolibarr (Demo)
username: admin
password: p4ssw0rd
## Getting Started
---
First, run the development server:
## 🤝 Contribuir
```bash
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev
```
Las contribuciones son bienvenidas. Por favor:
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
1. Fork el proyecto
2. Crea una rama para tu feature (`git checkout -b feature/AmazingFeature`)
3. Commit tus cambios (`git commit -m 'Add: AmazingFeature'`)
4. Push a la rama (`git push origin feature/AmazingFeature`)
5. Abre un Pull Request
You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.
---
This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel.
## 📄 Licencia
Este proyecto es privado y de uso interno.
---
## Learn More
To learn more about Next.js, take a look at the following resources:
- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.
- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.
- [Dolibarr API Documentation](https://wiki.dolibarr.org/index.php/Module_Web_Services_API_REST_(v1)) - Dolibarr REST API reference.

View File

@ -6,7 +6,10 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "eslint"
"lint": "eslint",
"seed": "node scripts/seed-projects-via-api.js",
"seed:direct": "node scripts/seed-projects.js",
"diagnose": "node scripts/diagnose.js"
},
"dependencies": {
"@radix-ui/react-avatar": "^1.1.11",

114
scripts/README.md Normal file
View File

@ -0,0 +1,114 @@
# Scripts de Utilidad
Este directorio contiene scripts para facilitar el desarrollo y testing de la aplicación.
## 📊 Seed de Proyectos de Prueba
### Opción 1: Via API Route de Next.js (Recomendado) ✅
Este método es más seguro ya que usa la API Route protegida.
**Requisitos:**
- El servidor de Next.js debe estar corriendo (`npm run dev`)
- Dolibarr debe estar accesible
**Uso:**
```bash
npm run seed
```
**Lo que hace:**
- Crea 12 proyectos de prueba con datos realistas
- Diferentes estados de progreso (0%, 15%, 35%, 50%, 65%, 75%, 90%, 100%)
- Variedad de presupuestos (€28k - €120k)
- Fechas de inicio y fin variadas
- Valida automáticamente los proyectos con progreso > 0%
**Proyectos incluidos:**
1. Desarrollo de App Móvil (65%)
2. Rediseño Web Corporativo (90%)
3. Sistema de Facturación Automática (35%)
4. Migración a Cloud AWS (20%)
5. Dashboard Analítico BI (100% - completado)
6. API REST Microservicios (50%)
7. App de Gestión de Proyectos (75%)
8. E-commerce con Marketplace (15%)
9. Sistema CRM Personalizado (0% - borrador)
10. Portal de Empleados (100% - completado)
11. Sistema de Reservas Online (45%)
12. App de Formación Online (30%)
---
### Opción 2: Conexión Directa a Dolibarr
Este método se conecta directamente a la API de Dolibarr (sin pasar por Next.js).
**Requisitos:**
- Dolibarr debe estar accesible
- Variables de entorno configuradas en `.env.local`
**Uso:**
```bash
npm run seed:direct
```
⚠️ **Nota:** Este método usa las credenciales directamente desde `.env.local`.
---
## 🔧 Troubleshooting
### Error: "No se puede conectar al servidor Next.js"
**Solución:** Asegúrate de que el servidor esté corriendo:
```bash
npm run dev
```
### Error: "Dolibarr API error"
**Soluciones posibles:**
1. Verifica que Dolibarr esté corriendo (Docker)
2. Revisa las credenciales en `.env.local`
3. Verifica que la URL de la API sea correcta
### Error: "No se pudo validar"
**No te preocupes:** Es normal. Algunos proyectos se crean correctamente pero no se pueden validar automáticamente. Puedes validarlos manualmente desde Dolibarr si es necesario.
---
## 🧹 Limpiar Proyectos de Prueba
Si quieres eliminar los proyectos de prueba creados, puedes hacerlo desde la interfaz de Dolibarr o usando la API:
```bash
# TODO: Crear script de limpieza
# npm run clean:projects
```
---
## 📝 Notas
- Los scripts pausan 500ms entre cada creación para no saturar la API
- Los proyectos con progreso 0% se dejan como "borrador"
- Los proyectos con progreso > 0% se intentan validar automáticamente
- Todos los proyectos tienen descripciones detalladas
- Las fechas están distribuidas entre 2023 y 2025
---
## 🎨 Personalización
Para modificar los proyectos de prueba, edita el array `projectsData` en cualquiera de los scripts:
```javascript
{
title: "Tu Proyecto",
ref: "PROJ-2024-XXX",
description: "Descripción detallada...",
opp_amount: 50000, // Presupuesto en euros
opp_percent: 50, // Progreso (0-100)
date_start: timestamp, // Unix timestamp en segundos
date_end: timestamp, // Unix timestamp en segundos
}
```

175
scripts/diagnose.js Normal file
View File

@ -0,0 +1,175 @@
#!/usr/bin/env node
/**
* Script de diagnóstico para verificar que la aplicación esté funcionando correctamente
*/
const COLORS = {
reset: '\x1b[0m',
green: '\x1b[32m',
red: '\x1b[31m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
cyan: '\x1b[36m',
};
function log(color, symbol, message) {
console.log(`${color}${symbol}${COLORS.reset} ${message}`);
}
async function checkDolibarr() {
try {
const response = await fetch('http://localhost:8200');
if (response.ok || response.status === 404) {
log(COLORS.green, '✓', 'Dolibarr está accesible en http://localhost:8200');
return true;
}
log(COLORS.red, '✗', `Dolibarr respondió con status ${response.status}`);
return false;
} catch (error) {
log(COLORS.red, '✗', 'Dolibarr NO está accesible en http://localhost:8200');
log(COLORS.yellow, '⚠', 'Asegúrate de que Docker esté corriendo: docker ps');
return false;
}
}
async function checkNextJS() {
try {
const response = await fetch('http://localhost:3000');
if (response.ok || response.status === 404) {
log(COLORS.green, '✓', 'Next.js está corriendo en http://localhost:3000');
return true;
}
log(COLORS.red, '✗', `Next.js respondió con status ${response.status}`);
return false;
} catch (error) {
log(COLORS.red, '✗', 'Next.js NO está corriendo en http://localhost:3000');
log(COLORS.yellow, '⚠', 'Ejecuta: npm run dev');
return false;
}
}
async function checkAPIRoute() {
try {
const response = await fetch('http://localhost:3000/api/dolibarr/projects');
if (response.ok) {
const data = await response.json();
log(COLORS.green, '✓', `API Route funciona correctamente`);
log(COLORS.cyan, 'ℹ', `Proyectos encontrados: ${data.length}`);
return data.length;
}
const errorData = await response.json();
log(COLORS.red, '✗', `API Route falló: ${errorData.error}`);
return 0;
} catch (error) {
log(COLORS.red, '✗', `Error en API Route: ${error.message}`);
return 0;
}
}
async function checkDolibarrAPI() {
try {
const API_URL = 'http://localhost:8200/api/index.php';
const API_KEY = process.env.NEXT_PUBLIC_DOLIBARR_API_KEY || 'Xzulu2Jbv1Ge4V1IVtXKe45db9W64WN7';
const response = await fetch(`${API_URL}/projects?DOLAPIKEY=${API_KEY}`);
if (response.ok) {
const data = await response.json();
log(COLORS.green, '✓', 'API de Dolibarr funciona correctamente');
log(COLORS.cyan, 'ℹ', `Proyectos en Dolibarr: ${data.length}`);
if (data.length > 0) {
const sample = data[0];
log(COLORS.blue, '→', `Ejemplo: ${sample.title} (${sample.ref})`);
}
return data.length;
}
log(COLORS.red, '✗', `API de Dolibarr falló con status ${response.status}`);
return 0;
} catch (error) {
log(COLORS.red, '✗', `Error conectando a API de Dolibarr: ${error.message}`);
return 0;
}
}
async function main() {
console.log('\n' + '═'.repeat(60));
console.log('🔍 DIAGNÓSTICO DEL SISTEMA');
console.log('═'.repeat(60) + '\n');
// 1. Verificar Dolibarr
console.log('1️⃣ Verificando Dolibarr...');
const dolibarrOk = await checkDolibarr();
console.log('');
if (!dolibarrOk) {
console.log('💡 Solución:');
console.log(' docker ps # Ver contenedores');
console.log(' docker start <container> # Iniciar Dolibarr\n');
return;
}
// 2. Verificar Next.js
console.log('2️⃣ Verificando Next.js...');
const nextOk = await checkNextJS();
console.log('');
if (!nextOk) {
console.log('💡 Solución:');
console.log(' npm run dev\n');
return;
}
// 3. Verificar API de Dolibarr directa
console.log('3️⃣ Verificando API de Dolibarr...');
const dolibarrProjectCount = await checkDolibarrAPI();
console.log('');
// 4. Verificar API Route
console.log('4️⃣ Verificando API Route de Next.js...');
const apiRouteProjectCount = await checkAPIRoute();
console.log('');
// Resumen
console.log('═'.repeat(60));
console.log('📊 RESUMEN');
console.log('═'.repeat(60));
if (dolibarrProjectCount === 0) {
log(COLORS.yellow, '⚠', 'No hay proyectos en Dolibarr');
console.log('\n💡 Solución:');
console.log(' npm run seed # Crear proyectos de prueba\n');
} else if (apiRouteProjectCount === 0) {
log(COLORS.red, '✗', 'Hay proyectos en Dolibarr pero no se pueden obtener via API Route');
console.log('\n💡 Posibles causas:');
console.log(' 1. Revisa las credenciales en .env.local');
console.log(' 2. Verifica que la API key sea correcta');
console.log(' 3. Mira los logs del servidor Next.js (terminal donde corre npm run dev)\n');
} else if (dolibarrProjectCount !== apiRouteProjectCount) {
log(COLORS.yellow, '⚠', 'Discrepancia en el número de proyectos');
log(COLORS.cyan, 'ℹ', `Dolibarr: ${dolibarrProjectCount}, API Route: ${apiRouteProjectCount}`);
} else {
log(COLORS.green, '✓', 'Todo está funcionando correctamente!');
console.log('');
log(COLORS.cyan, '🎉', 'Abre http://localhost:3000 para ver tus proyectos');
console.log('');
}
// Consejos adicionales
if (apiRouteProjectCount > 0) {
console.log('═'.repeat(60));
console.log('💡 CONSEJOS');
console.log('═'.repeat(60));
console.log('• Abre las DevTools del navegador (F12)');
console.log('• Ve a la pestaña Console');
console.log('• Busca errores en rojo');
console.log('• Ve a la pestaña Network > Fetch/XHR');
console.log('• Verifica que la petición a /api/dolibarr/projects retorne 200\n');
}
}
main().catch(error => {
console.error('\n❌ Error fatal:', error.message);
process.exit(1);
});

View File

@ -0,0 +1,281 @@
#!/usr/bin/env node
/**
* Script para crear proyectos de prueba en Dolibarr usando la API Route
* Este script usa la API Route segura de Next.js
*
* REQUISITO: El servidor de Next.js debe estar corriendo (npm run dev)
* Uso: node scripts/seed-projects-via-api.js
*/
const NEXT_API_URL = 'http://localhost:3000/api/dolibarr';
// Datos de proyectos de ejemplo con variedad de estados y presupuestos
const projectsData = [
{
title: "Desarrollo de App Móvil",
ref: "PROJ-2024-001",
description: "Aplicación móvil para gestión de inventario con funcionalidades de escaneo QR y sincronización en tiempo real.",
opp_amount: 45000,
opp_percent: 65,
date_start: Math.floor(new Date('2024-01-15').getTime() / 1000),
date_end: Math.floor(new Date('2024-06-30').getTime() / 1000),
},
{
title: "Rediseño Web Corporativo",
ref: "PROJ-2024-002",
description: "Renovación completa del sitio web corporativo con nuevo diseño responsive y optimización SEO.",
opp_amount: 28000,
opp_percent: 90,
date_start: Math.floor(new Date('2023-11-01').getTime() / 1000),
date_end: Math.floor(new Date('2024-02-28').getTime() / 1000),
},
{
title: "Sistema de Facturación Automática",
ref: "PROJ-2024-003",
description: "Desarrollo de sistema automatizado para generación y envío de facturas con integración a contabilidad.",
opp_amount: 62000,
opp_percent: 35,
date_start: Math.floor(new Date('2024-02-01').getTime() / 1000),
date_end: Math.floor(new Date('2024-08-31').getTime() / 1000),
},
{
title: "Migración a Cloud AWS",
ref: "PROJ-2024-004",
description: "Migración completa de infraestructura on-premise a AWS con configuración de alta disponibilidad.",
opp_amount: 85000,
opp_percent: 20,
date_start: Math.floor(new Date('2024-03-01').getTime() / 1000),
date_end: Math.floor(new Date('2024-12-31').getTime() / 1000),
},
{
title: "Dashboard Analítico BI",
ref: "PROJ-2024-005",
description: "Creación de dashboard de Business Intelligence para análisis de ventas y KPIs en tiempo real.",
opp_amount: 38000,
opp_percent: 100,
date_start: Math.floor(new Date('2023-09-01').getTime() / 1000),
date_end: Math.floor(new Date('2023-12-15').getTime() / 1000),
},
{
title: "API REST Microservicios",
ref: "PROJ-2024-006",
description: "Desarrollo de arquitectura de microservicios con API REST para integración de sistemas legacy.",
opp_amount: 72000,
opp_percent: 50,
date_start: Math.floor(new Date('2024-01-20').getTime() / 1000),
date_end: Math.floor(new Date('2024-07-31').getTime() / 1000),
},
{
title: "App de Gestión de Proyectos",
ref: "PROJ-2024-007",
description: "Plataforma web para gestión ágil de proyectos con tableros Kanban, Gantt y reporting automático.",
opp_amount: 55000,
opp_percent: 75,
date_start: Math.floor(new Date('2023-12-01').getTime() / 1000),
date_end: Math.floor(new Date('2024-05-31').getTime() / 1000),
},
{
title: "E-commerce con Marketplace",
ref: "PROJ-2024-008",
description: "Plataforma de e-commerce completa con funcionalidad de marketplace multivendedor y pasarela de pagos.",
opp_amount: 120000,
opp_percent: 15,
date_start: Math.floor(new Date('2024-04-01').getTime() / 1000),
date_end: Math.floor(new Date('2025-03-31').getTime() / 1000),
},
{
title: "Sistema CRM Personalizado",
ref: "PROJ-2024-009",
description: "CRM a medida con automatización de marketing, gestión de leads y pipeline de ventas.",
opp_amount: 48000,
opp_percent: 0,
date_start: Math.floor(new Date('2024-05-01').getTime() / 1000),
date_end: Math.floor(new Date('2024-11-30').getTime() / 1000),
},
{
title: "Portal de Empleados",
ref: "PROJ-2024-010",
description: "Portal interno para empleados con gestión de vacaciones, nóminas, formación y comunicación interna.",
opp_amount: 35000,
opp_percent: 100,
date_start: Math.floor(new Date('2023-10-01').getTime() / 1000),
date_end: Math.floor(new Date('2023-12-31').getTime() / 1000),
},
{
title: "Sistema de Reservas Online",
ref: "PROJ-2024-011",
description: "Plataforma de reservas con calendario interactivo, confirmación automática y pasarela de pago.",
opp_amount: 42000,
opp_percent: 45,
date_start: Math.floor(new Date('2024-02-15').getTime() / 1000),
date_end: Math.floor(new Date('2024-07-15').getTime() / 1000),
},
{
title: "App de Formación Online",
ref: "PROJ-2024-012",
description: "Plataforma LMS para formación online con seguimiento de progreso, certificados y evaluaciones.",
opp_amount: 68000,
opp_percent: 30,
date_start: Math.floor(new Date('2024-03-10').getTime() / 1000),
date_end: Math.floor(new Date('2024-10-31').getTime() / 1000),
},
];
/**
* Crear un proyecto usando la API Route de Next.js
*/
async function createProject(projectData) {
const url = `${NEXT_API_URL}/projects`;
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(projectData),
});
if (!response.ok) {
const errorData = await response.json();
throw new Error(`Error ${response.status}: ${errorData.error || 'Unknown error'}`);
}
const result = await response.json();
return result;
} catch (error) {
if (error.code === 'ECONNREFUSED') {
throw new Error('No se puede conectar al servidor Next.js. ¿Está corriendo "npm run dev"?');
}
throw error;
}
}
/**
* Validar proyecto (cambiar estado a validado/abierto)
*/
async function validateProject(projectId) {
const url = `${NEXT_API_URL}/projects/${projectId}/validate`;
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ notrigger: 0 }),
});
if (!response.ok) {
// Es normal que algunos proyectos no se puedan validar
return false;
}
return true;
} catch (error) {
return false;
}
}
/**
* Script principal
*/
async function main() {
console.log('\n🚀 SEED DE PROYECTOS DE PRUEBA PARA DOLIBARR\n');
console.log('═'.repeat(60));
console.log(`📡 API Next.js: ${NEXT_API_URL}`);
console.log(`📊 Proyectos a crear: ${projectsData.length}`);
console.log('═'.repeat(60));
console.log('');
// Verificar que Next.js esté corriendo
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);
}
let successCount = 0;
let errorCount = 0;
const errors = [];
for (let i = 0; i < projectsData.length; i++) {
const project = projectsData[i];
const num = i + 1;
const percentage = project.opp_percent;
// Indicador visual del progreso
const progressBar = '▓'.repeat(Math.floor(percentage / 10)) + '░'.repeat(10 - Math.floor(percentage / 10));
console.log(`[${num}/${projectsData.length}] ${project.title}`);
console.log(` 📈 Progreso: [${progressBar}] ${percentage}%`);
console.log(` 💰 Presupuesto: €${project.opp_amount.toLocaleString()}`);
try {
// Crear proyecto
const result = await createProject(project);
console.log(` ✅ Creado con ID: ${result}`);
// Validar proyecto si tiene progreso > 0
if (percentage > 0) {
const validated = await validateProject(result);
if (validated) {
console.log(` ✓ Validado (estado: abierto)`);
} else {
console.log(` ⚠ Creado pero no validado (normal para algunos proyectos)`);
}
} else {
console.log(` 📋 Dejado como borrador`);
}
successCount++;
// Pequeña pausa para no saturar la API
await new Promise(resolve => setTimeout(resolve, 500));
} catch (error) {
console.log(` ❌ Error: ${error.message}`);
errors.push({ project: project.title, error: error.message });
errorCount++;
}
console.log('');
}
// Resumen final
console.log('═'.repeat(60));
console.log('✨ PROCESO COMPLETADO\n');
console.log(`✅ Proyectos creados exitosamente: ${successCount}`);
console.log(`❌ Proyectos con error: ${errorCount}`);
if (errors.length > 0) {
console.log('\n📋 Errores encontrados:');
errors.forEach(({ project, error }) => {
console.log(` • ${project}: ${error}`);
});
}
console.log('═'.repeat(60));
if (successCount > 0) {
console.log('\n💡 ¡Recarga tu aplicación para ver los proyectos!');
console.log(' Abre http://localhost:3000 en tu navegador\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. Las credenciales en .env.local son correctas\n');
process.exit(1);
});

229
scripts/seed-projects.js Normal file
View File

@ -0,0 +1,229 @@
#!/usr/bin/env node
/**
* Script para crear proyectos de prueba en Dolibarr
* Uso: node scripts/seed-projects.js
*/
const API_URL = process.env.NEXT_PUBLIC_API_URL || 'http://localhost:8200/api/index.php';
const API_KEY = process.env.NEXT_PUBLIC_DOLIBARR_API_KEY || 'Xzulu2Jbv1Ge4V1IVtXKe45db9W64WN7';
// Datos de proyectos de ejemplo
const projectsData = [
{
title: "Desarrollo de App Móvil",
ref: "PROJ-2024-001",
description: "Aplicación móvil para gestión de inventario con funcionalidades de escaneo QR y sincronización en tiempo real.",
socid: null, // Sin cliente asignado
opp_amount: 45000,
opp_percent: 65,
date_start: Math.floor(new Date('2024-01-15').getTime() / 1000),
date_end: Math.floor(new Date('2024-06-30').getTime() / 1000),
},
{
title: "Rediseño Web Corporativo",
ref: "PROJ-2024-002",
description: "Renovación completa del sitio web corporativo con nuevo diseño responsive y optimización SEO.",
socid: null,
opp_amount: 28000,
opp_percent: 90,
date_start: Math.floor(new Date('2023-11-01').getTime() / 1000),
date_end: Math.floor(new Date('2024-02-28').getTime() / 1000),
},
{
title: "Sistema de Facturación Automática",
ref: "PROJ-2024-003",
description: "Desarrollo de sistema automatizado para generación y envío de facturas con integración a contabilidad.",
socid: null,
opp_amount: 62000,
opp_percent: 35,
date_start: Math.floor(new Date('2024-02-01').getTime() / 1000),
date_end: Math.floor(new Date('2024-08-31').getTime() / 1000),
},
{
title: "Migración a Cloud AWS",
ref: "PROJ-2024-004",
description: "Migración completa de infraestructura on-premise a AWS con configuración de alta disponibilidad.",
socid: null,
opp_amount: 85000,
opp_percent: 20,
date_start: Math.floor(new Date('2024-03-01').getTime() / 1000),
date_end: Math.floor(new Date('2024-12-31').getTime() / 1000),
},
{
title: "Dashboard Analítico BI",
ref: "PROJ-2024-005",
description: "Creación de dashboard de Business Intelligence para análisis de ventas y KPIs en tiempo real.",
socid: null,
opp_amount: 38000,
opp_percent: 100,
date_start: Math.floor(new Date('2023-09-01').getTime() / 1000),
date_end: Math.floor(new Date('2023-12-15').getTime() / 1000),
},
{
title: "API REST Microservicios",
ref: "PROJ-2024-006",
description: "Desarrollo de arquitectura de microservicios con API REST para integración de sistemas legacy.",
socid: null,
opp_amount: 72000,
opp_percent: 50,
date_start: Math.floor(new Date('2024-01-20').getTime() / 1000),
date_end: Math.floor(new Date('2024-07-31').getTime() / 1000),
},
{
title: "App de Gestión de Proyectos",
ref: "PROJ-2024-007",
description: "Plataforma web para gestión ágil de proyectos con tableros Kanban, Gantt y reporting automático.",
socid: null,
opp_amount: 55000,
opp_percent: 75,
date_start: Math.floor(new Date('2023-12-01').getTime() / 1000),
date_end: Math.floor(new Date('2024-05-31').getTime() / 1000),
},
{
title: "E-commerce con Marketplace",
ref: "PROJ-2024-008",
description: "Plataforma de e-commerce completa con funcionalidad de marketplace multivendedor y pasarela de pagos.",
socid: null,
opp_amount: 120000,
opp_percent: 15,
date_start: Math.floor(new Date('2024-04-01').getTime() / 1000),
date_end: Math.floor(new Date('2025-03-31').getTime() / 1000),
},
{
title: "Sistema CRM Personalizado",
ref: "PROJ-2024-009",
description: "CRM a medida con automatización de marketing, gestión de leads y pipeline de ventas.",
socid: null,
opp_amount: 48000,
opp_percent: 0,
date_start: Math.floor(new Date('2024-05-01').getTime() / 1000),
date_end: Math.floor(new Date('2024-11-30').getTime() / 1000),
},
{
title: "Portal de Empleados",
ref: "PROJ-2024-010",
description: "Portal interno para empleados con gestión de vacaciones, nóminas, formación y comunicación interna.",
socid: null,
opp_amount: 35000,
opp_percent: 100,
date_start: Math.floor(new Date('2023-10-01').getTime() / 1000),
date_end: Math.floor(new Date('2023-12-31').getTime() / 1000),
},
];
/**
* Crear un proyecto en Dolibarr
*/
async function createProject(projectData) {
const url = `${API_URL}/projects?DOLAPIKEY=${API_KEY}`;
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify(projectData),
});
if (!response.ok) {
const errorText = await response.text();
throw new Error(`Error ${response.status}: ${errorText}`);
}
const result = await response.json();
return result;
} catch (error) {
throw error;
}
}
/**
* Validar proyecto (cambiar estado a validado/abierto)
*/
async function validateProject(projectId) {
const url = `${API_URL}/projects/${projectId}/validate?DOLAPIKEY=${API_KEY}`;
try {
const response = await fetch(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({ notrigger: 0 }),
});
if (!response.ok) {
const errorText = await response.text();
console.warn(` ⚠️ No se pudo validar (puede ser normal): ${errorText.substring(0, 100)}`);
return false;
}
return true;
} catch (error) {
console.warn(` ⚠️ Error validando proyecto: ${error.message}`);
return false;
}
}
/**
* Script principal
*/
async function main() {
console.log('🚀 Iniciando creación de proyectos de prueba en Dolibarr...\n');
console.log(`📡 API URL: ${API_URL}`);
console.log(`🔑 API Key: ${API_KEY.substring(0, 10)}...`);
console.log(`📊 Proyectos a crear: ${projectsData.length}\n`);
let successCount = 0;
let errorCount = 0;
for (let i = 0; i < projectsData.length; i++) {
const project = projectsData[i];
const num = i + 1;
console.log(`[${num}/${projectsData.length}] Creando: "${project.title}"...`);
try {
// Crear proyecto
const result = await createProject(project);
console.log(` ✅ Proyecto creado con ID: ${result}`);
// Validar proyecto (cambiar a estado abierto)
// Solo validar proyectos que no estén al 0% (borradores)
if (project.opp_percent > 0) {
console.log(` 📝 Validando proyecto...`);
await validateProject(result);
}
successCount++;
// Pequeña pausa entre creaciones
await new Promise(resolve => setTimeout(resolve, 300));
} catch (error) {
console.error(` ❌ Error: ${error.message}`);
errorCount++;
}
console.log('');
}
console.log('═'.repeat(60));
console.log(`✨ Proceso completado!`);
console.log(`✅ Exitosos: ${successCount}`);
console.log(`❌ Errores: ${errorCount}`);
console.log('═'.repeat(60));
if (successCount > 0) {
console.log('\n💡 Ahora puedes recargar tu aplicación para ver los proyectos creados!');
}
}
// Ejecutar
main().catch(error => {
console.error('❌ Error fatal:', error);
process.exit(1);
});

View File

@ -7,8 +7,8 @@ export interface DolibarrProject {
ref: string;
title: string;
description: string;
date_start: number; // timestamp
date_end: number; // timestamp
date_start: number | null; // timestamp (puede ser null)
date_end: number | null; // timestamp (puede ser null)
status: string;
statut: string;
budget_amount: string;
@ -20,8 +20,8 @@ export interface DolibarrProject {
public: string | number;
usage_opportunity: string | number;
usage_task: string | number;
date_c: number; // fecha creación
date_m: number; // fecha modificación
date_c: number | null; // fecha creación (puede ser null)
date_m: number | null; // fecha modificación (puede ser null)
}
// Interface para usar en la UI (formato normalizado)
@ -74,6 +74,14 @@ export function mapDolibarrProject(dolibarr: DolibarrProject): Project {
const oppAmount = parseFloat(dolibarr.opp_amount || '0');
const progress = parseFloat(dolibarr.opp_percent || '0');
// Helper para convertir timestamp de Dolibarr (segundos) a fecha ISO
const timestampToDate = (timestamp: number | null | undefined): string => {
if (!timestamp || timestamp === 0) {
return new Date().toISOString().split('T')[0]; // Fecha actual por defecto
}
return new Date(timestamp * 1000).toISOString().split('T')[0];
};
return {
id: typeof dolibarr.id === 'string' ? parseInt(dolibarr.id) : dolibarr.id,
ref: dolibarr.ref,
@ -83,10 +91,10 @@ export function mapDolibarrProject(dolibarr: DolibarrProject): Project {
progress: progress,
budget: oppAmount || budget,
spent: (oppAmount || budget) * (progress / 100), // Calcular gastado según progreso
startDate: new Date(dolibarr.date_start * 1000).toISOString().split('T')[0],
endDate: new Date(dolibarr.date_end * 1000).toISOString().split('T')[0],
startDate: timestampToDate(dolibarr.date_start),
endDate: timestampToDate(dolibarr.date_end),
description: dolibarr.description || '',
createdAt: new Date(dolibarr.date_c * 1000).toISOString().split('T')[0],
updatedAt: new Date(dolibarr.date_m * 1000).toISOString().split('T')[0],
createdAt: timestampToDate(dolibarr.date_c),
updatedAt: timestampToDate(dolibarr.date_m),
};
}