From 806fa00103b1d5a07c45369a7e135e84eec7cb1d Mon Sep 17 00:00:00 2001 From: marsalva Date: Mon, 7 Sep 2026 07:26:24 +0000 Subject: [PATCH] =?UTF-8?q?Permite=20anular=20servicios=20sin=20tener=20qu?= =?UTF-8?q?e=20asign=C3=A1rtelos=20primero?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit solicitar.html: botón "Anular Aviso" en la Bolsa de Trabajo. buscar.html: botón de anular en la pestaña "Sin Asignar" y en "Mis Asignados". asignados.html: botón "Anular Servicio" en la ficha de un servicio ya asignado pero sin cita. En los tres casos se cambia el estado a Anulado directamente, sin tocar assigned_to, para no disparar el WhatsApp de "servicio asignado". --- asignados.html | 40 ++++++++++++++++++++++++++++++++++++++++ buscar.html | 41 ++++++++++++++++++++++++++++++++++++++--- solicitar.html | 41 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 119 insertions(+), 3 deletions(-) diff --git a/asignados.html b/asignados.html index 0feffbf..9949ce4 100644 --- a/asignados.html +++ b/asignados.html @@ -193,6 +193,10 @@ + + @@ -1029,6 +1033,42 @@ function renderServices(servicesToRender = localServices) { lucide.createIcons(); } } + + // 🆕 Anula un servicio ya asignado pero todavía sin cita, sin obligar a agendarlo antes. + async function anularServicio() { + const id = document.getElementById('detId').value; + if (!confirm("¿Seguro que quieres anular este servicio? No podrás deshacerlo desde aquí.")) return; + + const stAnulado = systemStatuses.find(st => st.name.toLowerCase().includes('anula')); + if (!stAnulado) return alert("El sistema no tiene un estado 'Anulado' configurado. Avisa a la oficina."); + + const btn = document.getElementById('btnAnularAsig'); + const originalContent = btn.innerHTML; + btn.innerHTML = ` Anulando...`; + btn.disabled = true; + + try { + const res = await fetch(`${API_URL}/services/set-appointment/${id}`, { + method: 'PUT', + headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }, + body: JSON.stringify({ status_operativo: String(stAnulado.id) }) + }); + + if (res.ok) { + showToast("Servicio anulado"); + closeModal('actionModal', 'modalContent'); + refreshData(); + } else { + alert("Error al anular."); + } + } catch (e) { + alert("Error de conexión"); + } finally { + btn.innerHTML = originalContent; + btn.disabled = false; + lucide.createIcons(); + } + } function showToast(msg) { const t = document.getElementById('toast'); document.getElementById('toastMsg').innerText = msg; diff --git a/buscar.html b/buscar.html index c02741a..31f65c7 100644 --- a/buscar.html +++ b/buscar.html @@ -444,6 +444,14 @@ `; + // 🆕 Botón Anular: disponible en ambas pestañas, para no tener que asignarse + // el servicio primero solo para poder cancelarlo (y evitar el WhatsApp de "asignado"). + const anularBtn = ` + + `; + if (currentTab === 'mine') { if (!isScheduled) { actionButtons = ` @@ -459,19 +467,20 @@ ` + actionButtons; } + actionButtons = actionButtons + anularBtn; } else if (currentTab === 'unassigned') { // GOAL 2: BOTÓN ASIGNÁRMELO actionButtons = ` - ` + actionButtons; + ` + actionButtons + anularBtn; } return `
${isUrgent ? '
Urgente
' : ''} - +
@@ -481,7 +490,7 @@ #${s.service_ref}
- +

${company}

${name}

@@ -553,6 +562,32 @@ } } + // 🆕 Anula un servicio directamente, esté o no asignado, sin pasar por "Asignármelo" primero + // (así no se le manda al cliente el WhatsApp de "servicio asignado" para algo que se va a cancelar). + async function anularServicio(id) { + if (!confirm("¿Seguro que quieres anular este servicio? No podrás deshacerlo desde aquí.")) return; + + const stAnulado = systemStatuses.find(st => st.name.toLowerCase().includes('anula')); + if (!stAnulado) return alert("El sistema no tiene un estado 'Anulado' configurado. Avisa a la oficina."); + + try { + const res = await fetch(`${API_URL}/providers/scraped/${id}`, { + method: 'PUT', + headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }, + body: JSON.stringify({ status_operativo: String(stAnulado.id) }) + }); + + if (res.ok) { + showToast("Servicio anulado"); + loadAllData(); + } else { + alert("Error al anular."); + } + } catch (e) { + alert("Error de conexión"); + } + } + async function quickCopyPortalLink(serviceId, phone, name, addr, btnElement) { if (!phone) return alert("El cliente no tiene teléfono guardado"); diff --git a/solicitar.html b/solicitar.html index 74460d9..967294f 100644 --- a/solicitar.html +++ b/solicitar.html @@ -117,6 +117,11 @@

Al quedártelo, el servicio pasará a tu lista de Asignados.

+ + +
@@ -440,6 +445,42 @@ lucide.createIcons(); } } + // 🆕 Anula el aviso directamente desde la bolsa, sin tener que asignártelo primero + // (así el cliente NO recibe el WhatsApp de "servicio asignado" por algo que ni siquiera vas a coger). + async function anularServicio() { + const id = document.getElementById('detId').value; + if (!confirm("¿Seguro que quieres anular este aviso? No podrás deshacerlo desde aquí.")) return; + + const stAnulado = systemStatuses.find(st => st.name.toLowerCase().includes('anula')); + if (!stAnulado) return alert("El sistema no tiene un estado 'Anulado' configurado. Avisa a la oficina."); + + const btn = document.getElementById('btnAnularBolsa'); + const originalContent = btn.innerHTML; + btn.innerHTML = ` Anulando...`; + btn.disabled = true; + + try { + const res = await fetch(`${API_URL}/providers/scraped/${id}`, { + method: 'PUT', + headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }, + body: JSON.stringify({ status_operativo: String(stAnulado.id) }) + }); + + if (res.ok) { + showToast("Aviso anulado"); + closeModal(); + loadAvailableServices(); + } else { + alert("Error al anular."); + } + } catch (e) { + alert("Error de conexión"); + } finally { + btn.innerHTML = originalContent; + btn.disabled = false; + lucide.createIcons(); + } + } // 🔥 FUNCIÓN DE FILTRADO Y ORDENACIÓN 🔥 function filterServices() { const query = document.getElementById('searchInput').value.toLowerCase().trim();