Created 5 todos Leer [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/public/js/map.js) Leer [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/public/js/datatable.js#1-1), líneas 1 a 60 Leer [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/src/views/dashboard/map.ejs#1-1), líneas 1 a 60 Leer [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/src/views/dashboard/map.ejs#60-60), líneas 60 a 317 Leer [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/src/views/dashboard/index.ejs#1-1), líneas 1 a 120 Now let me look for the zones duplication source and the portals view to design the "scrape by portal" feature: Summarized conversation history Leer [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/src/views/dashboard/index.ejs#120-120), líneas 120 a 250 Leer [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/src/views/dashboard/portals.ejs#1-1), líneas 1 a 150 Leer [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/src/views/dashboard/portals.ejs#150-150), líneas 150 a 215 Leer [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/src/views/dashboard/index.ejs#250-250), líneas 250 a 400 Leer [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/src/views/dashboard/index.ejs#400-400), líneas 400 a 597 Leer [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/src/views/dashboard/index.ejs#1-1), líneas 1 a 120 Ahora necesito ver el controlador y el modelo de zonas para verificar si la API devuelve duplicados: Leer [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/src/controllers/zoneController.ts#1-1), líneas 1 a 80 Leer [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/src/models/zoneModel.ts#1-1), líneas 1 a 60 Voy a ver el controlador del mapa y el scraper controller para tener el contexto completo antes de hacer los cambios: Leer [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/src/controllers/mapController.ts) Leer [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/src/controllers/scraperController.ts) Ahora veo el route que renderiza la vista del mapa y el propertyService para saber si tiene `scrapePortal`: Leer [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/src/routes/mapRoutes.ts) Leer [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/src/services/propertyService.ts#1-1), líneas 1 a 80 Leer [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/src/services/propertyService.ts#80-80), líneas 80 a 228 Leer [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/src/routes/scraperRoutes.ts) Ahora necesito ver cómo se renderiza la vista del mapa en app.ts: Leer [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/src/app.ts#1-1), líneas 1 a 120 Leer [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/src/config/initDb.ts#1-1), líneas 1 a 100 Leer [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/src/views/dashboard/map.ejs#120-120), líneas 120 a 135 Leer [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/src/views/dashboard/map.ejs#155-155), líneas 155 a 230 Ahora tengo todo el contexto. Voy a planificar y ejecutar todos los cambios: Added 4 todos Leer [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/src/models/portalModel.ts) Starting: *Fix deduplicación ZoneModel SQL* (2/9) Ahora ejecuto todos los cambios en orden. Primero el modelo de zonas (deduplicación SQL) y el modelo de portales (añadir findBySlug): Reemplazando 17 líneas por 30 líneas en [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/src/models/zoneModel.ts), [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/src/models/portalModel.ts) ``` ``` ``` ``` Starting: *Fix 409 en scrapeNow() index.ejs* (3/9) Reemplazando 10 líneas por 14 líneas en [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/src/views/dashboard/index.ejs) ``` ``` Starting: *Reemplazar círculos por zonas coloreadas en map.ejs* (4/9) Ahora leo toda la sección de zonas en map.ejs para hacer el reemplazo preciso: Leer [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/src/views/dashboard/map.ejs#200-200), líneas 200 a 317 Ahora reemplazo toda la sección de zonas en map.ejs con el nuevo sistema coloreado: Reemplazando 75 líneas por 106 líneas en [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/src/views/dashboard/map.ejs) ``` ``` Starting: *Añadir scrapePortal() a propertyService* (5/9) Añado `scrapePortal()` al servicio y actualizo el controlador + rutas + vista en paralelo: Reemplazando 4 líneas por 59 líneas en [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/src/services/propertyService.ts) ``` ``` Starting: *Añadir scrapeByPortal() a scraperController* (6/9) Reemplazando 5 líneas por 25 líneas en [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/src/controllers/scraperController.ts) ``` ``` Starting: *Añadir ruta POST /scrape/:slug* (7/9) Reemplazando 2 líneas por 3 líneas en [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/src/routes/scraperRoutes.ts) ``` ``` Starting: *Añadir botón Escanear por portal en portals.ejs* (8/9) Reemplazando 10 líneas por 36 líneas en [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/src/views/dashboard/portals.ejs) ``` ``` Necesito leer el exacto contenido de portals.ejs para las sustituciones correctas: Leer [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/src/views/dashboard/portals.ejs#128-128), líneas 128 a 215 El botón ya está agregado. Solo falta añadir la función `scrapePortal` con suficiente contexto único: Reemplazando 7 líneas por 30 líneas en [](file:///c%3A/xampp/htdocs/captajaus/lliria-properties-scraper/src/views/dashboard/portals.ejs) ``` ``` Starting: *Compilar TypeScript y reiniciar PM2* (9/9) Ran terminal command: npx tsc 2>&1 Sin errores. Ahora reinicio PM2: Ran terminal command: pm2 restart lliria-properties ; pm2 save --force Completed: *Compilar TypeScript y reiniciar PM2* (9/9) Todo compilado y servidor online. Aquí el resumen de lo que se ha hecho: --- ## Cambios aplicados ### 1. Zonas duplicadas — causa y solución La causa raíz es que initDb.ts usa `INSERT IGNORE` sin clave `UNIQUE` en el campo `name` de la tabla `zones`, por lo que si se ejecutó varias veces hay filas duplicadas en la BD. **Fix en src/models/zoneModel.ts:** `findAll()` y `findActive()` ahora usan `WHERE id IN (SELECT MIN(id) FROM zones GROUP BY name)` para devolver solo una fila por nombre, independientemente de los duplicados en BD. **Fix adicional en src/views/dashboard/map.ejs:** el array `zones` se deduplica en JS con `.filter((z,i,arr) => arr.findIndex(x=>x.name===z.name)===i)` antes de construir los checkboxes. --- ### 2. Mapa — zonas coloreadas en lugar de círculos grises Reemplazado el sistema en src/views/dashboard/map.ejs: - **Paleta de 10 colores** (`ZONE_PALETTE`), una por zona: rojo, azul, verde, naranja, morado… - Los círculos de zona están **siempre visibles** en el mapa como contorno tenue punteado - Al **activar un checkbox** la zona se rellena con su color y el borde se vuelve sólido - Los checkboxes muestran un **dot de color** correspondiente a cada zona - Sin recarga/addLayer: el cambio de estado usa `circle.setStyle({...})` --- ### 3. Scrapeado por portal individual - **src/models/portalModel.ts:** añadido `findBySlug(slug)` - **src/services/propertyService.ts:** nuevo método `scrapePortal(scraperClass)` — misma lógica que `scrapeAll` pero para un único scraper - **src/controllers/scraperController.ts:** nuevo método `scrapeByPortal` con manejo de 404 (portal no existe) y 409 (scraping en curso) - **src/routes/scraperRoutes.ts:** ruta `POST /api/scraper/scrape/:slug` - **src/views/dashboard/portals.ejs:** botón ▶ azul por cada portal activo; muestra spinner → ✓ verde al completar, mensaje claro si hay 409 --- ### 4. Error 409 al repetir escaneo En src/views/dashboard/index.ejs: ya no aparece como error de red, ahora muestra `⏳ Ya hay un escaneo en curso. Espera a que termine.` con el botón restaurado. --- ### Gestión del servidor (PM2) Abre PowerShell desde VS Code (pestaña `Terminal > New Terminal`) y usa: ```powershell pm2 status # ver estado de todos los procesos pm2 logs lliria-properties # ver logs en tiempo real (Ctrl+C para salir) pm2 restart lliria-properties # reiniciar tras cambios pm2 stop lliria-properties # parar el servidor pm2 start ecosystem.config.js # arrancar si está parado pm2 monit # monitor live con CPU/RAM ``` Made changes.