diff --git a/calendario.html b/calendario.html index c598eb6..b74443c 100644 --- a/calendario.html +++ b/calendario.html @@ -317,13 +317,19 @@
- +
+ + +
@@ -1343,12 +1349,67 @@ document.getElementById('nsTime').value = ""; document.getElementById('nsDate').value = selectedDateStr; + renderNewServiceAgenda(); // <-- Cargamos la ruta del día + const modal = document.getElementById('newServiceModal'); modal.style.display = 'flex'; setTimeout(() => modal.classList.remove('translate-y-full'), 10); safeLoadIcons(); } + // Dibuja los huecos ocupados para que el operario sepa por dónde se mueve + function renderNewServiceAgenda() { + const dateInput = document.getElementById('nsDate').value; + const agendaContainer = document.getElementById('nsAgendaContainer'); + const agendaList = document.getElementById('nsAgendaList'); + + if (!dateInput) { + agendaContainer.classList.add('hidden'); + return; + } + + const dayServices = localServices.filter(s => String(s.raw_data.scheduled_date || "").trim() === dateInput); + + dayServices.sort((a, b) => { + const tA = String(a.raw_data.scheduled_time || "23:59"); + const tB = String(b.raw_data.scheduled_time || "23:59"); + return tA.localeCompare(tB); + }); + + if (dayServices.length === 0) { + agendaList.innerHTML = `¡Día totalmente libre!`; + } else { + agendaList.innerHTML = dayServices.map(s => { + const time = s.raw_data.scheduled_time || "--:--"; + const dur = parseInt(s.raw_data.duration_minutes || 60); + + let endTime = "--:--"; + if (time.includes(':')) { + let [h, m] = time.split(':').map(Number); + m += dur; + h += Math.floor(m / 60); + m = m % 60; + endTime = `${String(h).padStart(2, '0')}:${String(m).padStart(2, '0')}`; + } + + const city = s.raw_data["Población"] || "Sin Zona"; + + return ` +
+
+
+ ${time} - ${endTime} +
+ ${city} +
+
+ `; + }).join(''); + } + agendaContainer.classList.remove('hidden'); + safeLoadIcons(); + } + function closeNewServiceModal() { const modal = document.getElementById('newServiceModal'); modal.classList.add('translate-y-full'); @@ -1378,8 +1439,101 @@ } catch (e) {} } + async function saveAppointment() { + const id = document.getElementById('detId').value; + const date = document.getElementById('dateInput').value; + const time = document.getElementById('timeInput').value; + const duration = parseInt(document.getElementById('durationInput').value) || 60; + let statusMap = document.getElementById('detStatusMap').value; + + const selectedSt = systemStatuses.find(st => String(st.id) === String(statusMap)); + if (selectedSt && !selectedSt.is_final && !date && !selectedSt.name.toLowerCase().includes('pausa') && !selectedSt.name.toLowerCase().includes('asignar')) { + if(!confirm("No has asignado Fecha para este estado. ¿Deseas continuar?")) return; + } + + // 🛑 RADAR ANTI-SOLAPAMIENTO (PERMISIVO) + if (date && time) { + let [newH, newM] = time.split(':').map(Number); + let newStartMin = newH * 60 + newM; + let newEndMin = newStartMin + duration; + + const solapamiento = localServices.find(s => { + if (String(s.id) === String(id)) return false; + const sDate = String(s.raw_data.scheduled_date || "").trim(); + const sTime = String(s.raw_data.scheduled_time || "").trim(); + if (sDate === date && sTime && sTime.includes(':')) { + let [sH, sM] = sTime.split(':').map(Number); + let sDur = parseInt(s.raw_data.duration_minutes || 60); + let sStartMin = sH * 60 + sM; + let sEndMin = sStartMin + sDur; + return (newStartMin < sEndMin && newEndMin > sStartMin); + } + return false; + }); + + if (solapamiento) { + const horaC = solapamiento.raw_data.scheduled_time; + const zonaC = solapamiento.raw_data["Población"] || "otra zona"; + // AHORA ES UN CONFIRM: Te avisa, pero te deja pasar si quieres + const forzar = confirm(`⚠️ SOLAPAMIENTO DETECTADO\n\nYa tienes una cita a las ${horaC} en ${zonaC}.\n\n¿Deseas FORZAR el guardado y agendarla en el mismo hueco?`); + if (!forzar) return; // Si cancela, abortamos + } + } + + const btn = document.getElementById('btnSaveAppt'); + const originalContent = btn.innerHTML; + btn.innerHTML = ` Guardando...`; + 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({ date, time, duration_minutes: duration, status_operativo: statusMap }) + }); + if (res.ok) { + showToast("Estado actualizado"); + closeModal(); + refreshData(); + } else { alert("Error al guardar"); } + } catch (e) { alert("Error de conexión"); } + finally { btn.innerHTML = originalContent; btn.disabled = false; safeLoadIcons(); } + } + async function saveNewServiceApp(e) { e.preventDefault(); + + const date = document.getElementById('nsDate').value; + const time = document.getElementById('nsTime').value; + const duration = parseInt(document.getElementById('nsDuration').value) || 60; + + // 🛑 RADAR ANTI-SOLAPAMIENTO EN CREACIÓN (PERMISIVO) + if (date && time) { + let [newH, newM] = time.split(':').map(Number); + let newStartMin = newH * 60 + newM; + let newEndMin = newStartMin + duration; + + const solapamiento = localServices.find(s => { + const sDate = String(s.raw_data.scheduled_date || "").trim(); + const sTime = String(s.raw_data.scheduled_time || "").trim(); + if (sDate === date && sTime && sTime.includes(':')) { + let [sH, sM] = sTime.split(':').map(Number); + let sDur = parseInt(s.raw_data.duration_minutes || 60); + let sStartMin = sH * 60 + sM; + let sEndMin = sStartMin + sDur; + return (newStartMin < sEndMin && newEndMin > sStartMin); + } + return false; + }); + + if (solapamiento) { + const horaC = solapamiento.raw_data.scheduled_time; + const zonaC = solapamiento.raw_data["Población"] || "otra zona"; + const forzar = confirm(`⚠️ SOLAPAMIENTO DETECTADO\n\nYa tienes una cita a las ${horaC} en ${zonaC}.\n\n¿Deseas FORZAR la creación de este aviso en el mismo hueco?`); + if (!forzar) return; + } + } + const btn = document.getElementById('btnSaveNewApp'); const originalHTML = btn.innerHTML; btn.innerHTML = ` Procesando...`; @@ -1397,7 +1551,7 @@ description: document.getElementById('nsDesc').value, guild_id: null, assigned_to: myWorkerId, - duration_minutes: document.getElementById('nsDuration').value, + duration_minutes: duration, is_urgent: false, is_company: false, company_name: 'Particular', @@ -1417,10 +1571,6 @@ const citadoSt = systemStatuses.find(st => st.name.toLowerCase().includes('citado')); const statusMapId = citadoSt ? String(citadoSt.id) : null; - const date = document.getElementById('nsDate').value; - const time = document.getElementById('nsTime').value; - const duration = document.getElementById('nsDuration').value; - await fetch(`${API_URL}/services/set-appointment/${dataCreate.id}`, { method: 'PUT', headers: { "Content-Type": "application/json", "Authorization": `Bearer ${token}` },