From c7d119e619778d0b3080f09df43476ad5733f5d8 Mon Sep 17 00:00:00 2001 From: marsalva Date: Wed, 18 Feb 2026 22:19:03 +0000 Subject: [PATCH] Actualizar server.js --- server.js | 74 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 61 insertions(+), 13 deletions(-) diff --git a/server.js b/server.js index 90afa74..5c5885e 100644 --- a/server.js +++ b/server.js @@ -471,6 +471,7 @@ app.post("/public/assignment/respond", async (req, res) => { app.get("/public/portal/:token", async (req, res) => { try { const { token } = req.params; + // 1. Identificamos al cliente const clientQ = await pool.query(` SELECT c.id, c.full_name, c.phone, c.addresses, c.owner_id, u.company_slug, u.full_name as company_name @@ -482,22 +483,49 @@ app.get("/public/portal/:token", async (req, res) => { if (clientQ.rowCount === 0) return res.status(404).json({ ok: false, error: "Enlace no válido o caducado" }); const clientData = clientQ.rows[0]; - const servicesQ = await pool.query(` - SELECT s.id, s.title, s.description, s.scheduled_date, s.scheduled_time, s.created_at, - st.name as status_name, st.color as status_color, st.is_final, - u.full_name as assigned_worker - FROM services s - LEFT JOIN service_statuses st ON s.status_id = st.id - LEFT JOIN users u ON s.assigned_to = u.id - WHERE s.client_id = $1 - ORDER BY s.created_at DESC - `, [clientData.id]); + // 2. Buscamos los expedientes en el Panel Operativo (scraped_services) + const phoneRaw = clientData.phone.replace('+34', ''); + const scrapedQ = await pool.query(` + SELECT id, service_ref as title, raw_data->>'Descripción' as description, + raw_data->>'scheduled_date' as scheduled_date, + raw_data->>'scheduled_time' as scheduled_time, + created_at, + raw_data->>'status_operativo' as estado_operativo, + (SELECT full_name FROM users WHERE id = scraped_services.assigned_to) as assigned_worker + FROM scraped_services + WHERE owner_id = $1 + AND (raw_data->>'Teléfono' ILIKE $2 OR raw_data->>'TELEFONO' ILIKE $2 OR raw_data->>'TELEFONOS' ILIKE $2) + ORDER BY created_at DESC + `, [clientData.owner_id, `%${phoneRaw}%`]); + + // Adaptamos el formato visual de los estados + const services = scrapedQ.rows.map(s => { + let statusName = "Pendiente de Asignar"; let color = "gray"; + if (s.estado_operativo === 'asignado_operario') { statusName = "Asignado a Técnico"; color = "blue"; } + if (s.estado_operativo === 'citado') { statusName = "Visita Agendada"; color = "emerald"; } + if (s.estado_operativo === 'de_camino') { statusName = "Técnico de Camino"; color = "indigo"; } + if (s.estado_operativo === 'trabajando') { statusName = "En Reparación"; color = "amber"; } + if (s.estado_operativo === 'incidencia') { statusName = "Pausado / Incidencia"; color = "red"; } + if (s.estado_operativo === 'terminado') { statusName = "Terminado"; color = "purple"; } + + return { + id: s.id, + title: "Expediente #" + s.title, + description: s.description || "Avería reportada.", + scheduled_date: s.scheduled_date, + scheduled_time: s.scheduled_time, + created_at: s.created_at, + status_name: statusName, + status_color: color, + assigned_worker: s.assigned_worker || "Pendiente" + }; + }); res.json({ ok: true, client: { name: clientData.full_name, phone: clientData.phone, addresses: clientData.addresses }, company: { name: clientData.company_name, slug: clientData.company_slug }, - services: servicesQ.rows + services: services }); } catch (e) { res.status(500).json({ ok: false, error: "Error de servidor" }); } }); @@ -873,10 +901,30 @@ app.post("/discovery/save", authMiddleware, async (req, res) => { } catch (e) { await client.query('ROLLBACK'); res.status(500).json({ ok: false }); } finally { client.release(); } }); +// AÑADIDO: Asegura que el cliente exista. Si no existe, lo crea y le asigna un token. +app.post("/clients/ensure", authMiddleware, async (req, res) => { + try { + const { phone, name, address } = req.body; + const p = normalizePhone(phone); + if(!p) return res.status(400).json({ok: false, error: "Sin teléfono"}); + + // 1. Buscamos si ya existe + const q = await pool.query("SELECT * FROM clients WHERE phone=$1 AND owner_id=$2 LIMIT 1", [p, req.user.accountId]); + if (q.rowCount > 0) return res.json({ ok: true, client: q.rows[0] }); + + // 2. Si no existe, lo creamos al vuelo en la agenda para que se genere su portal_token + const insert = await pool.query( + "INSERT INTO clients (owner_id, full_name, phone, addresses) VALUES ($1, $2, $3, $4) RETURNING *", + [req.user.accountId, name || 'Asegurado', p, JSON.stringify(address ? [address] : [])] + ); + res.json({ ok: true, client: insert.rows[0] }); + } catch (e) { res.status(500).json({ ok: false }); } +}); + app.get("/clients", authMiddleware, async (req, res) => { try { const { search } = req.query; - let query = `SELECT c.*, (SELECT COUNT(*) FROM services s WHERE s.client_id = c.id) as service_count FROM clients c WHERE c.owner_id = $1`; + let query = `SELECT c.*, c.portal_token, (SELECT COUNT(*) FROM services s WHERE s.client_id = c.id) as service_count FROM clients c WHERE c.owner_id = $1`; const params = [req.user.accountId]; if (search) { query += ` AND (c.full_name ILIKE $2 OR c.phone ILIKE $2)`; params.push(`%${search}%`); } query += ` ORDER BY c.created_at DESC LIMIT 50`; @@ -888,7 +936,7 @@ app.get("/clients", authMiddleware, async (req, res) => { app.get("/clients/:id/details", authMiddleware, async (req, res) => { try { const clientId = req.params.id; - const clientQ = await pool.query("SELECT * FROM clients WHERE id=$1 AND owner_id=$2", [clientId, req.user.accountId]); + const clientQ = await pool.query("SELECT id, full_name, phone, addresses, email, notes, portal_token, created_at FROM clients WHERE id=$1 AND owner_id=$2", [clientId, req.user.accountId]); if (clientQ.rowCount === 0) return res.status(404).json({ ok: false }); const servicesQ = await pool.query(`SELECT s.*, st.name as status_name, st.color as status_color, u.full_name as assigned_name FROM services s LEFT JOIN service_statuses st ON s.status_id = st.id LEFT JOIN users u ON s.assigned_to = u.id WHERE s.client_id = $1 ORDER BY s.created_at DESC`, [clientId]); res.json({ ok: true, client: clientQ.rows[0], services: servicesQ.rows });