diff --git a/server.js b/server.js index a36964a..3cc8682 100644 --- a/server.js +++ b/server.js @@ -1082,8 +1082,6 @@ app.post("/config/company", authMiddleware, async (req, res) => { const client = app.get("/guilds", authMiddleware, async (req, res) => { try { let q = await pool.query("SELECT id, name, ia_keywords FROM guilds WHERE owner_id=$1 ORDER BY name ASC", [req.user.accountId]); - - // 🚀 MAGIA SAAS: Si el usuario es nuevo y no tiene gremios, le inyectamos los predeterminados con la IA ya entrenada if (q.rowCount === 0) { const defaults = [ { n: "ELECTRICISTA", kw: '["electric", "cortocircuito", "cuadro electrico", "salto de plomos", "apagon", "diferencial", "icp", "magnetotermico", "chispazo", "sin luz", "cableado", "derivacion", "no hay luz", "salta el termico"]' }, @@ -1097,67 +1095,26 @@ app.get("/guilds", authMiddleware, async (req, res) => { { n: "MANITAS PERSIANAS", kw: '["manitas persian", "cambiar cinta", "cuerda persiana", "recogedor", "atasco persiana", "lamas rotas", "persiana descolgada"]' }, { n: "MANITAS GENERAL", kw: '["bombin", "colgar cuadro", "soporte tv", "estanteria", "montar mueble", "ikea", "cortina", "riel", "estor", "agujero", "taladro", "picaporte", "colgar espejo"]' } ]; - - // Insertamos la copia personal para este cliente - for (const g of defaults) { - await pool.query("INSERT INTO guilds (owner_id, name, ia_keywords) VALUES ($1, $2, $3::jsonb)", [req.user.accountId, g.n, g.kw]); - } - - // Volvemos a consultar ahora que ya los tiene + for (const g of defaults) { await pool.query("INSERT INTO guilds (owner_id, name, ia_keywords) VALUES ($1, $2, $3::jsonb)", [req.user.accountId, g.n, g.kw]); } q = await pool.query("SELECT id, name, ia_keywords FROM guilds WHERE owner_id=$1 ORDER BY name ASC", [req.user.accountId]); } - res.json({ ok: true, guilds: q.rows }); - } catch (e) { - res.status(500).json({ ok: false }); - } -}); - -app.post("/guilds", authMiddleware, async (req, res) => { - try { - const { name } = req.body; - await pool.query("INSERT INTO guilds (name, owner_id) VALUES ($1, $2)", [name, req.user.accountId]); - res.json({ ok: true }); } catch (e) { res.status(500).json({ ok: false }); } }); -app.delete("/guilds/:id", authMiddleware, async (req, res) => { - try { - await pool.query("DELETE FROM guilds WHERE id=$1 AND owner_id=$2", [req.params.id, req.user.accountId]); - res.json({ ok: true }); - } catch (e) { res.status(500).json({ ok: false }); } -}); +app.post("/guilds", authMiddleware, async (req, res) => { try { const { name } = req.body; await pool.query("INSERT INTO guilds (name, owner_id) VALUES ($1, $2)", [name, req.user.accountId]); res.json({ ok: true }); } catch (e) { res.status(500).json({ ok: false }); } }); +app.delete("/guilds/:id", authMiddleware, async (req, res) => { try { await pool.query("DELETE FROM guilds WHERE id=$1 AND owner_id=$2", [req.params.id, req.user.accountId]); res.json({ ok: true }); } catch (e) { res.status(500).json({ ok: false }); } }); -// NUEVA RUTA: PARA GUARDAR LAS PALABRAS CLAVE DE LA IA app.put("/guilds/:id/ia-rules", authMiddleware, async (req, res) => { try { - const { keywords } = req.body; // Viene un array ["grifo", "fuga"] + const { keywords } = req.body; const guildId = req.params.id; - - // Asegurarnos de que el array es válido const safeKeywords = Array.isArray(keywords) ? keywords : []; - - await pool.query( - "UPDATE guilds SET ia_keywords = $1 WHERE id = $2 AND owner_id = $3", - [JSON.stringify(safeKeywords), guildId, req.user.accountId] - ); - + await pool.query("UPDATE guilds SET ia_keywords = $1 WHERE id = $2 AND owner_id = $3", [JSON.stringify(safeKeywords), guildId, req.user.accountId]); res.json({ ok: true }); - } catch (e) { - console.error("Error guardando IA:", e); - res.status(500).json({ ok: false, error: e.message }); - } + } catch (e) { res.status(500).json({ ok: false, error: e.message }); } }); - -app.get("/services", authMiddleware, async (req, res) => { try { const q = await pool.query(`SELECT s.*, st.name as status_name, st.color as status_color, c.name as company_name, g.name as guild_name, u.full_name as assigned_name FROM services s LEFT JOIN service_statuses st ON s.status_id = st.id LEFT JOIN companies c ON s.company_id = c.id LEFT JOIN guilds g ON s.guild_id = g.id LEFT JOIN users u ON s.assigned_to = u.id WHERE s.owner_id=$1 ORDER BY s.created_at DESC`, [req.user.accountId]); res.json({ ok: true, services: q.rows }); } catch (e) { res.status(500).json({ ok: false }); } }); -app.get("/services/:id", authMiddleware, async (req, res) => { try { const q = await pool.query(`SELECT * FROM services WHERE id=$1 AND owner_id=$2`, [req.params.id, req.user.accountId]); res.json({ ok: true, service: q.rows[0] }); } catch (e) { res.status(500).json({ ok: false }); } }); -app.get("/services/:id/logs", authMiddleware, async (req, res) => { try { const q = await pool.query(`SELECT l.*, u.full_name as user_name, s2.name as new_status, s2.color as new_color FROM service_logs l LEFT JOIN users u ON l.user_id=u.id LEFT JOIN service_statuses s2 ON l.new_status_id=s2.id WHERE l.service_id=$1 ORDER BY l.created_at DESC`, [req.params.id]); res.json({ ok: true, logs: q.rows }); } catch (e) { res.status(500).json({ ok: false }); } }); -app.put("/services/:id/status", authMiddleware, async (req, res) => { const client = await pool.connect(); try { const { status_id, comment } = req.body; await client.query('BEGIN'); const curr = await client.query("SELECT status_id FROM services WHERE id=$1", [req.params.id]); const old = curr.rows[0].status_id; await client.query("UPDATE services SET status_id=$1 WHERE id=$2", [status_id, req.params.id]); await client.query("INSERT INTO service_logs (service_id, user_id, old_status_id, new_status_id, comment) VALUES ($1, $2, $3, $4, $5)", [req.params.id, req.user.sub, old, status_id, comment]); await client.query('COMMIT'); res.json({ ok: true }); } catch (e) { await client.query('ROLLBACK'); res.status(500).json({ ok: false }); } finally { client.release(); } }); -app.post("/services", authMiddleware, async (req, res) => { const client = await pool.connect(); try { const { phone, name, address, email, description, scheduled_date, scheduled_time, duration, is_urgent, is_company, company_id, company_ref, internal_notes, client_notes, status_id, guild_id, assigned_to } = req.body; const p = normalizePhone(phone); await client.query('BEGIN'); let finalStatus = status_id; if (!finalStatus) { const def = await client.query("SELECT id FROM service_statuses WHERE owner_id=$1 AND is_default=TRUE LIMIT 1", [req.user.accountId]); finalStatus = def.rows[0]?.id; } let clientId; const cCheck = await client.query("SELECT id, addresses FROM clients WHERE phone=$1 AND owner_id=$2", [p, req.user.accountId]); if (cCheck.rowCount > 0) { clientId = cCheck.rows[0].id; let addrs = cCheck.rows[0].addresses || []; if(!addrs.includes(address)) { addrs.push(address); await client.query("UPDATE clients SET addresses=$1 WHERE id=$2", [JSON.stringify(addrs), clientId]); } } else { const newC = await client.query("INSERT INTO clients (owner_id, full_name, phone, email, addresses) VALUES ($1, $2, $3, $4, $5) RETURNING id", [req.user.accountId, name, p, email, JSON.stringify([address])]); clientId = newC.rows[0].id; } const insert = await client.query(`INSERT INTO services (owner_id, client_id, status_id, contact_phone, contact_name, address, email, description, scheduled_date, scheduled_time, duration_minutes, is_urgent, is_company, company_id, company_ref, internal_notes, client_notes, title, guild_id, assigned_to) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16, $17, $18, $19, $20) RETURNING id`, [req.user.accountId, clientId, finalStatus, p, name, address, email, description, scheduled_date || 'NOW()', scheduled_time || 'NOW()', (duration || 30), (is_urgent || false), (is_company || false), (company_id || null), company_ref, internal_notes, client_notes, name + " - Svc", (guild_id || null), (assigned_to || null)]); await client.query("INSERT INTO service_logs (service_id, user_id, new_status_id, comment) VALUES ($1, $2, $3, 'Servicio Creado')", [insert.rows[0].id, req.user.sub, finalStatus]); await client.query('COMMIT'); res.json({ ok: true }); } catch (e) { await client.query('ROLLBACK'); console.error(e); res.status(500).json({ ok: false, error: e.message }); } finally { client.release(); } }); -app.put("/services/:id", authMiddleware, async (req, res) => { const client = await pool.connect(); try { const { name, address, email, description, scheduled_date, scheduled_time, duration, is_urgent, is_company, company_id, company_ref, internal_notes, client_notes, guild_id, assigned_to } = req.body; await client.query('BEGIN'); await client.query(`UPDATE services SET contact_name=$1, address=$2, email=$3, description=$4, scheduled_date=$5, scheduled_time=$6, duration_minutes=$7, is_urgent=$8, is_company=$9, company_id=$10, company_ref=$11, internal_notes=$12, client_notes=$13, guild_id=$14, assigned_to=$15 WHERE id=$16 AND owner_id=$17`, [name, address, email, description, scheduled_date, scheduled_time, (duration || 30), (is_urgent || false), (is_company || false), (company_id || null), company_ref, internal_notes, client_notes, (guild_id || null), (assigned_to || null), req.params.id, req.user.accountId]); await client.query("INSERT INTO service_logs (service_id, user_id, new_status_id, comment) VALUES ($1, $2, (SELECT status_id FROM services WHERE id=$1), 'Datos editados')", [req.params.id, req.user.sub]); await client.query('COMMIT'); res.json({ ok: true }); } catch (e) { await client.query('ROLLBACK'); res.status(500).json({ ok: false }); } finally { client.release(); } }); -app.delete("/services/:id", authMiddleware, async (req, res) => { try { await pool.query("DELETE FROM services WHERE id=$1 AND owner_id=$2", [req.params.id, req.user.accountId]); res.json({ ok: true }); } catch (e) { res.status(500).json({ ok: false }); } }); - // ========================================== // 🕒 EL RELOJ DEL SISTEMA (Ejecutar cada minuto) // ==========================================