Actualizar servicios.html
This commit is contained in:
138
servicios.html
138
servicios.html
@@ -80,9 +80,15 @@
|
|||||||
<input type="week" id="weekFilter" onchange="renderLists()" class="w-full bg-slate-50 border border-slate-200 text-xs font-black px-4 py-3 rounded-xl outline-none focus:ring-2 focus:ring-blue-500 text-slate-600 cursor-pointer" title="Filtrar por semana">
|
<input type="week" id="weekFilter" onchange="renderLists()" class="w-full bg-slate-50 border border-slate-200 text-xs font-black px-4 py-3 rounded-xl outline-none focus:ring-2 focus:ring-blue-500 text-slate-600 cursor-pointer" title="Filtrar por semana">
|
||||||
</div>
|
</div>
|
||||||
<button onclick="document.getElementById('weekFilter').value = ''; renderLists();" class="text-xs font-bold text-slate-400 hover:text-red-500 px-2 py-3">Limpiar Sem.</button>
|
<button onclick="document.getElementById('weekFilter').value = ''; renderLists();" class="text-xs font-bold text-slate-400 hover:text-red-500 px-2 py-3">Limpiar Sem.</button>
|
||||||
<button onclick="refreshData()" class="bg-white border border-slate-200 text-slate-600 hover:text-blue-600 px-4 py-3 rounded-xl shadow-sm transition-all active:scale-95 shrink-0 flex items-center gap-2 ml-auto">
|
<div class="flex items-center gap-2 ml-auto">
|
||||||
<i data-lucide="refresh-cw" class="w-4 h-4"></i> <span class="hidden md:inline text-xs font-black uppercase tracking-widest">Recargar</span>
|
<button onclick="openOldServicesModal()" class="bg-rose-50 border border-rose-200 text-rose-600 hover:bg-rose-600 hover:text-white px-4 py-3 rounded-xl shadow-sm transition-all active:scale-95 shrink-0 flex items-center gap-2 font-black text-xs uppercase tracking-widest relative">
|
||||||
</button>
|
<i data-lucide="alert-octagon" class="w-4 h-4"></i> <span class="hidden md:inline">Atrasados</span>
|
||||||
|
<span id="badgeOldServices" class="absolute -top-2 -right-2 bg-red-500 text-white text-[9px] w-5 h-5 flex items-center justify-center rounded-full hidden shadow-md">0</span>
|
||||||
|
</button>
|
||||||
|
<button onclick="refreshData()" class="bg-white border border-slate-200 text-slate-600 hover:text-blue-600 px-4 py-3 rounded-xl shadow-sm transition-all active:scale-95 shrink-0 flex items-center gap-2">
|
||||||
|
<i data-lucide="refresh-cw" class="w-4 h-4"></i> <span class="hidden md:inline text-xs font-black uppercase tracking-widest">Recargar</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex flex-wrap gap-2 items-center pt-2 border-b border-slate-100 pb-4 mb-4" id="statusPills">
|
<div class="flex flex-wrap gap-2 items-center pt-2 border-b border-slate-100 pb-4 mb-4" id="statusPills">
|
||||||
@@ -475,9 +481,24 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="toastAppt" class="fixed bottom-8 right-8 bg-emerald-600 text-white px-6 py-4 rounded-2xl shadow-2xl hidden z-[200] font-bold text-sm flex items-center gap-3 transition-all duration-300">
|
<div id="oldServicesModal" class="fixed inset-0 bg-slate-900/60 hidden z-[150] flex items-center justify-center backdrop-blur-sm p-4 text-left">
|
||||||
<span id="toastIconContainer"><i data-lucide="check-circle" class="w-5 h-5"></i></span>
|
<div class="bg-white rounded-[2rem] shadow-2xl w-full max-w-4xl max-h-[90vh] flex flex-col fade-in relative overflow-hidden">
|
||||||
<span id="toastMsg">Guardado correctamente</span>
|
<div class="p-6 border-b border-slate-100 flex items-center justify-between bg-white shrink-0 z-10">
|
||||||
|
<div class="flex items-center gap-3">
|
||||||
|
<div class="bg-rose-100 p-2.5 rounded-xl text-rose-600"><i data-lucide="calendar-x" class="w-6 h-6"></i></div>
|
||||||
|
<div>
|
||||||
|
<h3 class="font-black text-slate-800 text-lg uppercase tracking-tight">Servicios Atrasados</h3>
|
||||||
|
<p class="text-xs text-slate-500 font-medium">Citas pasadas que siguen activas (Ayer o anteriores)</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button onclick="closeOldServicesModal()" class="text-slate-400 hover:text-red-500 bg-slate-50 hover:bg-red-50 p-2.5 rounded-full transition-all border border-slate-200"><i data-lucide="x" class="w-5 h-5"></i></button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="p-6 overflow-y-auto no-scrollbar flex-1 bg-slate-50/50">
|
||||||
|
<div id="oldServicesList" class="flex flex-col gap-3">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="js/layout.js"></script>
|
<script src="js/layout.js"></script>
|
||||||
@@ -618,6 +639,7 @@
|
|||||||
localData = data.services.filter(s => s.provider !== 'SYSTEM_BLOCK');
|
localData = data.services.filter(s => s.provider !== 'SYSTEM_BLOCK');
|
||||||
updateOperatorFilter();
|
updateOperatorFilter();
|
||||||
renderLists();
|
renderLists();
|
||||||
|
checkOldServices(); // 👈 Lanzamos el detector de atrasados al recargar
|
||||||
}
|
}
|
||||||
} catch (e) { console.error(e); }
|
} catch (e) { console.error(e); }
|
||||||
}
|
}
|
||||||
@@ -1417,6 +1439,110 @@
|
|||||||
input.focus();
|
input.focus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// =====================================
|
||||||
|
// PANEL DE SERVICIOS ATRASADOS
|
||||||
|
// =====================================
|
||||||
|
function checkOldServices() {
|
||||||
|
const hoy = new Date();
|
||||||
|
hoy.setHours(0, 0, 0, 0);
|
||||||
|
|
||||||
|
const targetStates = ['citado', 'trabajando', 'incidencia', 'compañía', 'perito', 'espera cia'];
|
||||||
|
|
||||||
|
const atrasados = localData.filter(s => {
|
||||||
|
const stateInfo = getServiceStateInfo(s);
|
||||||
|
const stName = (stateInfo.name || "").toLowerCase();
|
||||||
|
|
||||||
|
// Filtramos que estén en los estados problemáticos y que NO estén finalizados/anulados
|
||||||
|
const isTargetState = targetStates.some(ts => stName.includes(ts));
|
||||||
|
if (!isTargetState || stateInfo.is_final) return false;
|
||||||
|
|
||||||
|
const raw = s.raw_data || {};
|
||||||
|
let targetDateStr = raw.scheduled_date;
|
||||||
|
|
||||||
|
// Si no tiene fecha programada pero está en incidencia o perito, usamos la fecha de creación
|
||||||
|
if (!targetDateStr) {
|
||||||
|
targetDateStr = s.created_at ? s.created_at.split('T')[0] : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (targetDateStr) {
|
||||||
|
const svcDate = new Date(targetDateStr);
|
||||||
|
svcDate.setHours(0, 0, 0, 0);
|
||||||
|
return svcDate < hoy; // Es estrictamente menor que hoy (ayer o antes)
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Actualizamos el número rojo en el botón
|
||||||
|
const badge = document.getElementById('badgeOldServices');
|
||||||
|
if (badge) {
|
||||||
|
if (atrasados.length > 0) {
|
||||||
|
badge.innerText = atrasados.length;
|
||||||
|
badge.classList.remove('hidden');
|
||||||
|
} else {
|
||||||
|
badge.classList.add('hidden');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return atrasados.sort((a, b) => {
|
||||||
|
const dA = new Date((a.raw_data && a.raw_data.scheduled_date) || a.created_at);
|
||||||
|
const dB = new Date((b.raw_data && b.raw_data.scheduled_date) || b.created_at);
|
||||||
|
return dA - dB;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function openOldServicesModal() {
|
||||||
|
const list = checkOldServices();
|
||||||
|
const container = document.getElementById('oldServicesList');
|
||||||
|
|
||||||
|
if (list.length === 0) {
|
||||||
|
container.innerHTML = '<div class="text-center py-12 text-slate-400 font-bold uppercase tracking-widest text-xs bg-white rounded-2xl border border-slate-200 shadow-sm"><i data-lucide="check-circle-2" class="w-12 h-12 mx-auto mb-3 text-emerald-400"></i>Todo al día<br><span class="text-[9px] font-medium text-slate-400 normal-case">No hay servicios atrasados pendientes.</span></div>';
|
||||||
|
} else {
|
||||||
|
container.innerHTML = list.map(s => {
|
||||||
|
const raw = s.raw_data || {};
|
||||||
|
const stateInfo = getServiceStateInfo(s);
|
||||||
|
const colorData = colorDict[stateInfo.color] || colorDict['gray'];
|
||||||
|
const clientName = raw["Nombre Cliente"] || raw["CLIENTE"] || "Sin Nombre";
|
||||||
|
const dateStr = raw.scheduled_date ? raw.scheduled_date.split('-').reverse().join('/') : new Date(s.created_at).toLocaleDateString('es-ES');
|
||||||
|
|
||||||
|
return `
|
||||||
|
<div class="bg-white p-5 rounded-2xl border border-rose-200 shadow-sm flex flex-col md:flex-row items-start md:items-center justify-between gap-4 hover:shadow-md transition-all cursor-pointer group" onclick="closeOldServicesModal(); openDetail(${s.id})">
|
||||||
|
<div class="flex items-center gap-4 flex-1 w-full overflow-hidden">
|
||||||
|
<div class="bg-rose-50 text-rose-500 p-3 rounded-xl shrink-0 group-hover:scale-110 transition-transform">
|
||||||
|
<i data-lucide="clock-4" class="w-6 h-6"></i>
|
||||||
|
</div>
|
||||||
|
<div class="min-w-0">
|
||||||
|
<div class="flex items-center gap-2 mb-1.5 flex-wrap">
|
||||||
|
<span class="text-[10px] font-black text-slate-500 bg-slate-100 px-2 py-0.5 rounded border border-slate-200 uppercase tracking-widest">#${s.service_ref}</span>
|
||||||
|
<span class="text-[10px] font-black uppercase tracking-widest text-rose-600 bg-rose-50 px-2 py-0.5 rounded border border-rose-200 shadow-sm"><i data-lucide="calendar" class="w-3 h-3 inline mr-0.5 relative -top-[1px]"></i> Cita: ${dateStr}</span>
|
||||||
|
</div>
|
||||||
|
<h4 class="font-black text-slate-800 text-sm truncate uppercase">${clientName}</h4>
|
||||||
|
<p class="text-[11px] font-bold text-slate-500 truncate mt-1 flex items-center gap-1"><i data-lucide="hard-hat" class="w-3 h-3"></i> ${s.assigned_name || 'Sin operario asignado'}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-3 shrink-0 w-full md:w-auto justify-between md:justify-end mt-2 md:mt-0 pt-3 md:pt-0 border-t md:border-t-0 border-slate-100">
|
||||||
|
<span class="text-[9px] font-black ${colorData.bg} ${colorData.text} px-3 py-1.5 rounded-lg uppercase tracking-widest border ${colorData.border} flex items-center gap-1.5 shadow-sm">
|
||||||
|
<div class="w-2 h-2 rounded-full ${colorData.dot}"></div> ${stateInfo.name}
|
||||||
|
</span>
|
||||||
|
<button class="text-blue-600 bg-blue-50 border border-blue-100 group-hover:bg-blue-600 group-hover:text-white px-4 py-2 rounded-xl transition-all shadow-sm font-black text-[10px] uppercase tracking-widest active:scale-95 flex items-center gap-2">
|
||||||
|
Revisar <i data-lucide="external-link" class="w-3 h-3"></i>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}).join('');
|
||||||
|
}
|
||||||
|
|
||||||
|
document.getElementById('oldServicesModal').classList.remove('hidden');
|
||||||
|
lucide.createIcons();
|
||||||
|
}
|
||||||
|
|
||||||
|
function closeOldServicesModal() {
|
||||||
|
document.getElementById('oldServicesModal').classList.add('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
Reference in New Issue
Block a user