diff --git a/calendario.html b/calendario.html index 7ce09f3..24cf651 100644 --- a/calendario.html +++ b/calendario.html @@ -180,16 +180,28 @@ let calendarEvents = []; let calendarInstance = null; - // Diccionario de colores. AÑADIMOS EL BLOQUEO (Rojo vivo) + // DICCIONARIO DE COLORES E ICONOS const statusConfig = { - 'citado': { bg: '#3b82f6', border: '#2563eb', label: 'Citado / Agendado' }, // Azul - 'de_camino': { bg: '#8b5cf6', border: '#7c3aed', label: 'De Camino' }, // Morado - 'trabajando': { bg: '#f59e0b', border: '#d97706', label: 'Trabajando en lugar' }, // Naranja - 'incidencia': { bg: '#ef4444', border: '#dc2626', label: 'Pausado / Incidencia' }, // Rojo - 'terminado': { bg: '#10b981', border: '#059669', label: 'Trabajo Terminado' }, // Verde - 'SYSTEM_BLOCK': { bg: '#f43f5e', border: '#e11d48', label: 'BLOQUEO DE AGENDA' } // Rosa fuerte + 'citado': { bg: '#3b82f6', border: '#2563eb', label: 'Citado / Agendado', icon: 'calendar' }, + 'de_camino': { bg: '#8b5cf6', border: '#7c3aed', label: 'Técnico de Camino', icon: 'truck' }, // COCHE/CAMIÓN + 'trabajando': { bg: '#f59e0b', border: '#d97706', label: 'En Reparación', icon: 'hammer' }, // MARTILLO + 'incidencia': { bg: '#ef4444', border: '#dc2626', label: 'Pausado / Incidencia', icon: 'alert-triangle' }, + 'terminado': { bg: '#10b981', border: '#059669', label: 'Trabajo Terminado', icon: 'check-circle' }, + 'SYSTEM_BLOCK': { bg: '#f43f5e', border: '#e11d48', label: 'BLOQUEO DE AGENDA', icon: 'shield-alert' } }; + // Función para detectar la clave correcta aunque venga el ID o un texto variable + function getStatusKey(status) { + if (!status) return 'citado'; + const s = String(status).toLowerCase(); + if (s.includes('camino')) return 'de_camino'; + if (s.includes('trabajando') || s.includes('reparacion')) return 'trabajando'; + if (s.includes('incidencia') || s.includes('pausado')) return 'incidencia'; + if (s.includes('terminado') || s.includes('finalizado')) return 'terminado'; + if (s === 'system_block') return 'SYSTEM_BLOCK'; + return 'citado'; + } + document.addEventListener("DOMContentLoaded", async () => { if (!localStorage.getItem("token")) window.location.href = "index.html"; lucide.createIcons(); @@ -210,17 +222,16 @@ async function loadServices() { try { - // Traemos los servicios activos const res = await fetch(`${API_URL}/services/active`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); const data = await res.json(); if (data.ok) { - // Filtramos solo los que tienen fecha de inicio programada rawServices = data.services.filter(s => s.raw_data && s.raw_data.scheduled_date && s.raw_data.scheduled_date !== ""); - // Extraemos compañías (ignorando los bloqueos) const compSelect = document.getElementById('compFilter'); const uniqueComps = [...new Set(rawServices.filter(s => s.provider !== 'SYSTEM_BLOCK').map(s => (s.raw_data['Compañía'] || s.raw_data['COMPAÑIA'] || "Particular").toString().toUpperCase()))].sort(); + + compSelect.innerHTML = ''; uniqueComps.forEach(c => compSelect.innerHTML += ``); processEventsAndRender(); @@ -235,24 +246,19 @@ const name = r['Nombre Cliente'] || r['CLIENTE'] || "Sin Nombre"; const pop = r['Población'] || r['POBLACION-PROVINCIA'] || ""; - const title = isBlock ? `BLOQUEADO: ${r['Descripción'] || 'Motivo no especificado'}` : `${name} - ${pop}`; + const title = isBlock ? `BLOQUEO: ${r['Descripción'] || 'Motivo'}` : `${name} - ${pop}`; - // Formato ISO: YYYY-MM-DDTHH:mm:00 const dateIso = r.scheduled_date; const timeIso = r.scheduled_time ? r.scheduled_time + ':00' : '09:00:00'; const startStr = `${dateIso}T${timeIso}`; - // Calcular la fecha/hora de fin sumando los minutos const startObj = new Date(startStr); const duration = parseInt(r.duration_minutes || 60); const endObj = new Date(startObj.getTime() + duration * 60000); - - // Ajuste de offset horario local para la ISO String final const endStr = new Date(endObj.getTime() - (endObj.getTimezoneOffset() * 60000)).toISOString().slice(0, 19); - // Color según estado (o si es bloqueo) - const status = isBlock ? 'SYSTEM_BLOCK' : (r.status_operativo || 'citado'); - const sConf = statusConfig[status] || statusConfig['citado']; + const statusKey = isBlock ? 'SYSTEM_BLOCK' : getStatusKey(r.status_operativo); + const sConf = statusConfig[statusKey] || statusConfig['citado']; return { id: svc.id, @@ -271,7 +277,12 @@ }; }); - initCalendar(); + if (calendarInstance) { + calendarInstance.removeAllEventSources(); + calendarInstance.addEventSource(calendarEvents); + } else { + initCalendar(); + } } function initCalendar() { @@ -279,22 +290,16 @@ calendarInstance = new FullCalendar.Calendar(calendarEl, { initialView: 'timeGridWeek', - locale: 'es', // FORZAMOS IDIOMA ESPAÑOL PARA LA VENTANITA + locale: 'es', firstDay: 1, slotMinTime: '08:00:00', slotMaxTime: '22:00:00', expandRows: true, allDaySlot: false, nowIndicator: true, - dayMaxEvents: 3, // Cuántas caben antes de mostrar "X más" - moreLinkText: "más", // TRADUCCIÓN MANUAL POR SI ACASO - buttonText: { - today: 'Hoy', - month: 'Mes', - week: 'Semana', - day: 'Día', - list: 'Agenda' - }, + dayMaxEvents: 3, + moreLinkText: "más", + buttonText: { today: 'Hoy', month: 'Mes', week: 'Semana', day: 'Día', list: 'Agenda' }, headerToolbar: { left: 'prev,next today', center: 'title', @@ -302,9 +307,7 @@ }, events: calendarEvents, - // INYECCIÓN DE HTML Y COLOR DENTRO DEL EVENTO eventContent: function(arg) { - // Extraemos la hora para la tarjeta let timeText = arg.timeText; if (!timeText && arg.event.start) { timeText = arg.event.start.toLocaleTimeString('es-ES', {hour: '2-digit', minute:'2-digit'}); @@ -313,29 +316,35 @@ const title = arg.event.title; const op = arg.event.extendedProps.operatorName; const isBlock = arg.event.extendedProps.isBlock; - const bgColor = arg.event.backgroundColor; // Recuperamos el color oficial + const conf = arg.event.extendedProps.statusConf; + const bgColor = arg.event.backgroundColor; return { html: ` -
- ${op} -
+${title}
+