Actualizar server.js
This commit is contained in:
104
server.js
104
server.js
@@ -502,69 +502,65 @@ async function ensureInstance(instanceName) {
|
|||||||
// 🚀 RUTAS PÚBLICAS (MÓVIL OPERARIO)
|
// 🚀 RUTAS PÚBLICAS (MÓVIL OPERARIO)
|
||||||
// ==========================================
|
// ==========================================
|
||||||
|
|
||||||
// 1. Cargar datos del cliente, logo y empresa
|
// ==========================================
|
||||||
|
// 🔗 PORTAL PÚBLICO DEL CLIENTE (Mejorado con Historial)
|
||||||
|
// ==========================================
|
||||||
app.get("/public/portal/:token", async (req, res) => {
|
app.get("/public/portal/:token", async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { token } = req.params;
|
const { token } = req.params;
|
||||||
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, u.company_logo
|
|
||||||
FROM clients c
|
|
||||||
JOIN users u ON c.owner_id = u.id
|
|
||||||
WHERE c.portal_token = $1
|
|
||||||
`, [token]);
|
|
||||||
|
|
||||||
if (clientQ.rowCount === 0) return res.status(404).json({ ok: false, error: "Enlace no válido o caducado" });
|
// 1. Validar Token y Obtener Cliente
|
||||||
const clientData = clientQ.rows[0];
|
const qClient = await pool.query("SELECT * FROM clients WHERE portal_token = $1", [token]);
|
||||||
|
if (qClient.rowCount === 0) return res.status(404).json({ ok: false, error: "Enlace inválido" });
|
||||||
|
const client = qClient.rows[0];
|
||||||
|
|
||||||
const phoneRaw = clientData.phone.replace('+34', '');
|
// 2. Obtener la Configuración de la Empresa
|
||||||
const scrapedQ = await pool.query(`
|
const qConfig = await pool.query("SELECT * FROM config WHERE account_id = $1", [client.owner_id]);
|
||||||
SELECT id, service_ref as title, raw_data->>'Descripción' as description,
|
let company = { name: "IntegraRepara", logo: "" };
|
||||||
raw_data->>'scheduled_date' as scheduled_date,
|
if (qConfig.rowCount > 0 && qConfig.rows[0].portal_settings) {
|
||||||
raw_data->>'scheduled_time' as scheduled_time,
|
company = qConfig.rows[0].portal_settings;
|
||||||
raw_data->>'appointment_status' as appointment_status,
|
}
|
||||||
created_at,
|
|
||||||
is_urgent,
|
|
||||||
(SELECT full_name FROM users WHERE id = scraped_services.assigned_to) as assigned_worker,
|
|
||||||
(SELECT name FROM service_statuses WHERE id::text = raw_data->>'status_operativo') as real_status_name
|
|
||||||
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}%`]);
|
|
||||||
|
|
||||||
const services = scrapedQ.rows.map(s => {
|
// 3. Buscar TODOS los servicios asociados al teléfono del cliente
|
||||||
// Evaluamos el nombre real de la base de datos
|
// Usamos el teléfono para buscar en el JSONB (cuidando las minúsculas y mayúsculas de las llaves posibles)
|
||||||
let stNameDb = (s.real_status_name || 'Pendiente de Asignar').toLowerCase();
|
const qServices = await pool.query(`
|
||||||
let finalStatusName = s.real_status_name || "Pendiente de Asignar";
|
SELECT
|
||||||
|
s.id, s.service_ref, s.status, s.raw_data, s.is_urgent,
|
||||||
|
st.name as status_name, st.is_final as status_is_final,
|
||||||
|
u.name as assigned_worker
|
||||||
|
FROM scraped_services s
|
||||||
|
LEFT JOIN service_statuses st ON st.id::text = s.raw_data->>'status_operativo'
|
||||||
|
LEFT JOIN users u ON u.id = s.assigned_to
|
||||||
|
WHERE s.owner_id = $1
|
||||||
|
AND s.provider != 'SYSTEM_BLOCK'
|
||||||
|
AND (
|
||||||
|
s.raw_data->>'Teléfono' LIKE '%' || $2 || '%' OR
|
||||||
|
s.raw_data->>'TELEFONOS' LIKE '%' || $2 || '%' OR
|
||||||
|
s.raw_data->>'TELEFONO' LIKE '%' || $2 || '%'
|
||||||
|
)
|
||||||
|
ORDER BY
|
||||||
|
st.is_final ASC, -- Activos primero
|
||||||
|
s.created_at DESC -- Luego ordenados por fecha de creación
|
||||||
|
`, [client.owner_id, client.phone]);
|
||||||
|
|
||||||
if (stNameDb.includes('asignado') || stNameDb.includes('esperando')) { finalStatusName = "Asignado a Técnico"; }
|
// Formatear servicios para enviarlos limpios al cliente
|
||||||
if (stNameDb.includes('citado')) { finalStatusName = "Visita Agendada"; }
|
const formattedServices = qServices.rows.map(s => ({
|
||||||
if (stNameDb.includes('camino')) { finalStatusName = "Técnico de Camino"; }
|
id: s.id,
|
||||||
if (stNameDb.includes('trabajando')) { finalStatusName = "En Reparación"; }
|
title: s.is_urgent ? `🚨 URGENTE: Expediente #${s.service_ref}` : `Expediente #${s.service_ref}`,
|
||||||
if (stNameDb.includes('incidencia')) { finalStatusName = "Pausado / Incidencia"; }
|
description: s.raw_data["Descripción"] || s.raw_data["DESCRIPCION"] || "Revisión técnica solicitada.",
|
||||||
if (stNameDb.includes('terminado') || stNameDb.includes('finalizado') || stNameDb.includes('anulado') || stNameDb.includes('desasignado')) { finalStatusName = "Terminado"; }
|
status_name: s.status_name || 'Pendiente',
|
||||||
|
is_final: s.status_is_final,
|
||||||
|
scheduled_date: s.raw_data.scheduled_date,
|
||||||
|
scheduled_time: s.raw_data.scheduled_time,
|
||||||
|
assigned_worker: s.assigned_worker || 'Pendiente'
|
||||||
|
}));
|
||||||
|
|
||||||
return {
|
res.json({ ok: true, client, company, services: formattedServices });
|
||||||
id: s.id,
|
|
||||||
title: (s.is_urgent ? "🚨 URGENTE: " : "") + "Expediente #" + s.title,
|
|
||||||
description: s.description || "Avería reportada.",
|
|
||||||
scheduled_date: s.scheduled_date,
|
|
||||||
scheduled_time: s.scheduled_time,
|
|
||||||
appointment_status: s.appointment_status,
|
|
||||||
created_at: s.created_at,
|
|
||||||
status_name: finalStatusName,
|
|
||||||
assigned_worker: s.assigned_worker || "Pendiente"
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
res.json({
|
} catch (e) {
|
||||||
ok: true,
|
console.error("Error en Portal Cliente:", e);
|
||||||
client: { name: clientData.full_name, phone: clientData.phone, addresses: clientData.addresses },
|
res.status(500).json({ ok: false, error: "Server error" });
|
||||||
company: { name: clientData.company_name, slug: clientData.company_slug, logo: clientData.company_logo },
|
}
|
||||||
services: services
|
|
||||||
});
|
|
||||||
} catch (e) { res.status(500).json({ ok: false, error: "Error de servidor" }); }
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// 2. Obtener huecos disponibles inteligentes (CON HORARIOS DINÁMICOS Y TRAMOS DE 1 HORA)
|
// 2. Obtener huecos disponibles inteligentes (CON HORARIOS DINÁMICOS Y TRAMOS DE 1 HORA)
|
||||||
|
|||||||
Reference in New Issue
Block a user