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();