From 99bd74148b695daf05aee363838a4028bc005cc0 Mon Sep 17 00:00:00 2001 From: marsalva Date: Mon, 16 Feb 2026 21:57:08 +0000 Subject: [PATCH] Actualizar servicios.html --- servicios.html | 286 +++++++++++++++++++++++++++++++------------------ 1 file changed, 180 insertions(+), 106 deletions(-) diff --git a/servicios.html b/servicios.html index 4eb4c9a..60c656b 100644 --- a/servicios.html +++ b/servicios.html @@ -99,7 +99,7 @@
-
@@ -142,6 +142,7 @@
+
@@ -168,51 +169,79 @@
-
-
+
+ + + +
@@ -234,7 +263,7 @@ const data = await res.json(); if (data.ok) { localData = data.services; - updateOperatorFilter(); // Actualiza el select de operarios + updateOperatorFilter(); renderLists(); } } catch (e) { console.error(e); } @@ -243,27 +272,17 @@ function updateOperatorFilter() { const opSelect = document.getElementById('opFilter'); const currentValue = opSelect.value; - - // Extraer nombres únicos de operarios asignados en este panel const uniqueOps = [...new Set(localData.map(s => s.assigned_name).filter(Boolean))].sort(); - let html = ''; - uniqueOps.forEach(op => { - html += ``; - }); - + uniqueOps.forEach(op => { html += ``; }); opSelect.innerHTML = html; - // Restaurar la selección previa si sigue existiendo - if (html.includes(`value="${currentValue}"`)) { - opSelect.value = currentValue; - } + if (html.includes(`value="${currentValue}"`)) opSelect.value = currentValue; } function renderLists() { const searchTerm = document.getElementById('searchFilter').value.toLowerCase(); const selectedOp = document.getElementById('opFilter').value; - // FILTRO DE DATOS const filteredData = localData.filter(s => { const raw = s.raw_data; const name = (raw["Nombre Cliente"] || raw["CLIENTE"] || "").toLowerCase(); @@ -273,21 +292,12 @@ const ref = (s.service_ref || "").toLowerCase(); const assigned = s.assigned_name || ""; - // Buscar texto (Nombre, Ref, Dirección, Población, Teléfono) - const matchesSearch = searchTerm === "" || - name.includes(searchTerm) || - ref.includes(searchTerm) || - addr.includes(searchTerm) || - pop.includes(searchTerm) || - phone.includes(searchTerm); - - // Buscar Operario + const matchesSearch = searchTerm === "" || name.includes(searchTerm) || ref.includes(searchTerm) || addr.includes(searchTerm) || pop.includes(searchTerm) || phone.includes(searchTerm); const matchesOp = selectedOp === "ALL" || assigned === selectedOp; return matchesSearch && matchesOp; }); - // Lógica inteligente de 3 columnas apoyada en el raw_data const unassigned = filteredData.filter(s => !s.assigned_name || s.raw_data.status_operativo === 'sin_asignar'); const pending = filteredData.filter(s => s.assigned_name && (!s.raw_data.scheduled_date || s.raw_data.scheduled_date === "") && s.raw_data.status_operativo !== 'sin_asignar'); const assigned = filteredData.filter(s => s.raw_data.scheduled_date && s.raw_data.scheduled_date !== "" && s.raw_data.status_operativo !== 'sin_asignar'); @@ -350,31 +360,43 @@ const raw = s.raw_data; document.getElementById('detId').value = s.id; document.getElementById('detRef').innerText = s.service_ref; + document.getElementById('detCp').value = raw["Código Postal"] || "00000"; const companyName = raw['Compañía'] || raw['COMPAÑIA'] || raw['Procedencia'] || "Particular"; document.getElementById('detCompany').innerText = companyName; - document.getElementById('detName').innerText = raw["Nombre Cliente"] || raw["CLIENTE"] || "Asegurado Sin Nombre"; const phone = raw["Teléfono"] || raw["TELEFONO"] || "Sin Teléfono"; document.getElementById('detPhone').innerText = phone; - const cleanPhone = phone.replace(/\D/g,''); - document.getElementById('detPhoneLink').href = cleanPhone ? `tel:${cleanPhone}` : "#"; - + document.getElementById('detPhoneLink').href = phone.replace(/\D/g,'') ? `tel:${phone.replace(/\D/g,'')}` : "#"; document.getElementById('detAddrText').innerText = `${raw["Dirección"] || "Dirección no especificada"} ${raw["Población"] || ""}`; - document.getElementById('detWorker').innerText = s.assigned_name || "Pendiente Asignar"; - document.getElementById('detDesc').innerHTML = (raw["Descripción"] || raw["DESCRIPCION"] || "No hay notas adicionales del siniestro.") - .replace(/\n/g, '
'); + document.getElementById('detDesc').innerHTML = (raw["Descripción"] || raw["DESCRIPCION"] || "Sin notas.").replace(/\n/g, '
'); - document.getElementById('dateInput').value = raw.scheduled_date || ""; - document.getElementById('timeInput').value = raw.scheduled_time || ""; - - let defaultStatus = raw.status_operativo; - if (!defaultStatus) { - defaultStatus = (!s.assigned_name) ? 'sin_asignar' : 'citado'; + // LÓGICA DE PANELES LATERALES + if (s.assigned_name && raw.status_operativo !== 'sin_asignar') { + // TIENE OPERARIO: Mostrar panel de fechas + document.getElementById('panelAsignado').classList.remove('hidden'); + document.getElementById('panelSinAsignar').classList.add('hidden'); + + document.getElementById('detWorker').innerText = s.assigned_name; + document.getElementById('dateInput').value = raw.scheduled_date || ""; + document.getElementById('timeInput').value = raw.scheduled_time || ""; + document.getElementById('detStatusMap').value = raw.status_operativo || 'citado'; + } else { + // NO TIENE OPERARIO (O FUE PAUSADO): Mostrar panel de re-asignación + document.getElementById('panelAsignado').classList.add('hidden'); + document.getElementById('panelSinAsignar').classList.remove('hidden'); + + // Pre-cargar el gremio si el robot lo detectó previamente + if(raw.guild_id) { + document.getElementById('reGremio').value = raw.guild_id; + loadOps(raw.guild_id, 'reOperario'); + } else { + document.getElementById('reGremio').value = ""; + document.getElementById('reOperario').innerHTML = ''; + } } - document.getElementById('detStatusMap').value = defaultStatus; document.getElementById('detailModal').classList.remove('hidden'); lucide.createIcons(); @@ -386,57 +408,105 @@ const time = document.getElementById('timeInput').value; const statusMap = document.getElementById('detStatusMap').value; - if(!date && statusMap !== 'incidencia' && statusMap !== 'terminado' && statusMap !== 'sin_asignar') { - if(!confirm("No has asignado una Fecha de Visita.\n\nEl estado se guardará, pero el expediente seguirá en la columna izquierda hasta que le pongas fecha. ¿Deseas continuar?")) return; + // Si lo mandan a "sin_asignar", borrar operario en BD + let assigned_to = undefined; + if (statusMap === 'sin_asignar') { + assigned_to = null; + } else if(!date && statusMap !== 'incidencia' && statusMap !== 'terminado') { + if(!confirm("No has asignado Fecha. ¿Deseas continuar?")) return; } const btn = document.getElementById('btnSaveAppt'); const originalContent = btn.innerHTML; - btn.innerHTML = ` Guardando...`; btn.disabled = true; - lucide.createIcons(); try { - const res = await fetch(`${API_URL}/services/set-appointment/${id}`, { + // Si cambiamos a sin_asignar, usamos la ruta de scraped para borrar el assigned_to + if(statusMap === 'sin_asignar') { + await fetch(`${API_URL}/providers/scraped/${id}`, { + method: 'PUT', + headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }, + body: JSON.stringify({ status_operativo: statusMap, assigned_to: null, assigned_to_name: null }) + }); + } else { + await fetch(`${API_URL}/services/set-appointment/${id}`, { + method: 'PUT', + headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }, + body: JSON.stringify({ date, time, status_operativo: statusMap }) + }); + } + closeDetailModal(); showToast("Estado actualizado"); refreshPanel(); + } catch (e) { alert("Error"); } + finally { btn.innerHTML = originalContent; btn.disabled = false; } + } + + async function sendToAutomate() { + const id = document.getElementById('detId').value; + const guild_id = document.getElementById('reGremio').value; + const cp = document.getElementById('detCp').value; + + if(!guild_id) return alert("Selecciona un gremio primero."); + + const btn = document.getElementById('btnAuto'); + btn.innerHTML = ''; + + try { + const res = await fetch(`${API_URL}/providers/automate/${id}`, { + method: 'POST', + headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }, + body: JSON.stringify({ guild_id, cp }) + }); + const data = await res.json(); + if(data.ok) { closeDetailModal(); showToast("Enviado a la rueda de WhatsApp"); refreshPanel(); } + else { alert(data.error || "No hay operarios en esa zona para ese gremio"); btn.innerHTML = 'Mandar a la Cola'; } + } catch (e) { alert("Error"); btn.innerHTML = 'Mandar a la Cola'; } + } + + async function assignManualFromPanel() { + const id = document.getElementById('detId').value; + const guild_id = document.getElementById('reGremio').value; + const assigned_to = document.getElementById('reOperario').value; + + if(!guild_id || !assigned_to) return alert("Selecciona gremio y operario."); + + try { + const select = document.getElementById('reOperario'); + const name = select.options[select.selectedIndex].text; + + await fetch(`${API_URL}/providers/scraped/${id}`, { method: 'PUT', headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }, - body: JSON.stringify({ date, time, status_operativo: statusMap }) + body: JSON.stringify({ + automation_status: 'completed', + assigned_to: assigned_to, + assigned_name: name, + guild_id: guild_id, + status_operativo: 'asignado_operario' + }) }); - - if(res.ok) { - closeDetailModal(); - const toast = document.getElementById('toastAppt'); - toast.classList.remove('hidden'); - setTimeout(() => { toast.classList.add('hidden'); }, 3000); - refreshPanel(); - } else { - alert("Error en el servidor al guardar."); - } - } catch (e) { - alert("Error de conexión"); - } finally { - btn.innerHTML = originalContent; - btn.disabled = false; - lucide.createIcons(); - } + closeDetailModal(); showToast("Asignado correctamente"); refreshPanel(); + } catch (e) { alert("Error"); } + } + + function showToast(msg) { + document.getElementById('toastMsg').innerText = msg; + const toast = document.getElementById('toastAppt'); + toast.classList.remove('hidden'); + setTimeout(() => { toast.classList.add('hidden'); }, 3000); } async function saveNewService(e) { e.preventDefault(); const action = e.submitter.value; const data = { - phone: document.getElementById('nPhone').value, - name: document.getElementById('nName').value, - address: document.getElementById('nAddr').value, - description: document.getElementById('nDesc').value, - guild_id: document.getElementById('nGuild').value, - assigned_to: document.getElementById('nWorker').value || null + phone: document.getElementById('nPhone').value, name: document.getElementById('nName').value, + address: document.getElementById('nAddr').value, description: document.getElementById('nDesc').value, + guild_id: document.getElementById('nGuild').value, assigned_to: document.getElementById('nWorker').value || null }; try { const res = await fetch(`${API_URL}/services/manual-high`, { - method: 'POST', - headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }, + method: 'POST', headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }, body: JSON.stringify({ ...data, mode: action }) }); if (res.ok) { closeCreateModal(); refreshPanel(); } @@ -450,11 +520,15 @@ async function loadGuilds() { const res = await fetch(`${API_URL}/guilds`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); const data = await res.json(); - if(data.ok) document.getElementById('nGuild').innerHTML = '' + data.guilds.map(g => ``).join(''); + if(data.ok) { + const opts = '' + data.guilds.map(g => ``).join(''); + document.getElementById('nGuild').innerHTML = opts; + document.getElementById('reGremio').innerHTML = opts; + } } - async function loadOps(gid) { - const sel = document.getElementById('nWorker'); + async function loadOps(gid, targetSelectId) { + const sel = document.getElementById(targetSelectId); if(!gid) { sel.innerHTML = ''; return; } const res = await fetch(`${API_URL}/operators?guild_id=${gid}`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); const data = await res.json();