Actualizar servicios2.html

This commit is contained in:
2026-03-22 11:46:26 +00:00
parent c91134793e
commit cec595f4cf

View File

@@ -27,6 +27,7 @@
.border-col-2 { border-left-color: #f59e0b; } /* Ambar: Sin Cita */
.border-col-3 { border-left-color: #3b82f6; } /* Azul: Pendiente Inicio */
.border-col-4 { border-left-color: #10b981; } /* Verde: Trabajando */
.border-col-5 { border-left-color: #a855f7; } /* Morado: Incidencias */
/* Estilos base para formularios y modales */
.input-modern { @apply w-full bg-slate-50 border border-slate-200 px-4 py-3 rounded-xl text-sm font-semibold text-slate-700 outline-none transition-all focus:border-blue-500 focus:bg-white focus:ring-2 focus:ring-blue-100; }
@@ -89,46 +90,46 @@
</div>
</div>
<div class="flex gap-6 overflow-x-auto no-scrollbar flex-1 pb-6 w-full fade-in min-w-max">
<div class="flex gap-6 overflow-x-auto no-scrollbar flex-1 pb-6 w-full fade-in min-w-max items-start">
<div class="kanban-col bg-slate-100/70 rounded-[2rem] p-4 border border-slate-200/60 shadow-inner">
<div class="flex justify-between items-center mb-4 px-2">
<h3 class="font-black text-slate-700 uppercase tracking-widest text-xs flex items-center gap-2">
<span class="w-2.5 h-2.5 rounded-full bg-red-500"></span> Sin Asignar
</h3>
<span id="count-unassigned" class="bg-white border border-slate-200 text-slate-600 text-[10px] font-black px-2.5 py-1 rounded-lg shadow-sm">0</span>
<h3 class="font-black text-slate-700 uppercase tracking-widest text-xs flex items-center gap-2"><span class="w-2.5 h-2.5 rounded-full bg-red-500"></span> Sin Asignar</h3>
<span id="count-c1" class="bg-white border border-slate-200 text-slate-600 text-[10px] font-black px-2.5 py-1 rounded-lg shadow-sm">0</span>
</div>
<div id="col-unassigned" class="kanban-cards space-y-3 no-scrollbar"></div>
<div id="col-1" class="kanban-cards space-y-3 no-scrollbar"></div>
</div>
<div class="kanban-col bg-slate-100/70 rounded-[2rem] p-4 border border-slate-200/60 shadow-inner">
<div class="flex justify-between items-center mb-4 px-2">
<h3 class="font-black text-slate-700 uppercase tracking-widest text-xs flex items-center gap-2">
<span class="w-2.5 h-2.5 rounded-full bg-amber-500 animate-pulse"></span> Sin Cita
</h3>
<span id="count-unscheduled" class="bg-white border border-slate-200 text-slate-600 text-[10px] font-black px-2.5 py-1 rounded-lg shadow-sm">0</span>
<h3 class="font-black text-slate-700 uppercase tracking-widest text-xs flex items-center gap-2"><span class="w-2.5 h-2.5 rounded-full bg-amber-500 animate-pulse"></span> Sin Cita</h3>
<span id="count-c2" class="bg-white border border-slate-200 text-slate-600 text-[10px] font-black px-2.5 py-1 rounded-lg shadow-sm">0</span>
</div>
<div id="col-unscheduled" class="kanban-cards space-y-3 no-scrollbar"></div>
<div id="col-2" class="kanban-cards space-y-3 no-scrollbar"></div>
</div>
<div class="kanban-col bg-slate-100/70 rounded-[2rem] p-4 border border-slate-200/60 shadow-inner">
<div class="flex justify-between items-center mb-4 px-2">
<h3 class="font-black text-slate-700 uppercase tracking-widest text-xs flex items-center gap-2">
<span class="w-2.5 h-2.5 rounded-full bg-blue-500"></span> Pte. Inicio
</h3>
<span id="count-pending-start" class="bg-white border border-slate-200 text-slate-600 text-[10px] font-black px-2.5 py-1 rounded-lg shadow-sm">0</span>
<h3 class="font-black text-slate-700 uppercase tracking-widest text-xs flex items-center gap-2"><span class="w-2.5 h-2.5 rounded-full bg-blue-500"></span> Pte. Inicio</h3>
<span id="count-c3" class="bg-white border border-slate-200 text-slate-600 text-[10px] font-black px-2.5 py-1 rounded-lg shadow-sm">0</span>
</div>
<div id="col-pending-start" class="kanban-cards space-y-3 no-scrollbar"></div>
<div id="col-3" class="kanban-cards space-y-3 no-scrollbar"></div>
</div>
<div class="kanban-col bg-slate-100/70 rounded-[2rem] p-4 border border-slate-200/60 shadow-inner">
<div class="flex justify-between items-center mb-4 px-2">
<h3 class="font-black text-slate-700 uppercase tracking-widest text-xs flex items-center gap-2">
<span class="w-2.5 h-2.5 rounded-full bg-emerald-500"></span> Trabajando
</h3>
<span id="count-working" class="bg-white border border-slate-200 text-slate-600 text-[10px] font-black px-2.5 py-1 rounded-lg shadow-sm">0</span>
<h3 class="font-black text-slate-700 uppercase tracking-widest text-xs flex items-center gap-2"><span class="w-2.5 h-2.5 rounded-full bg-emerald-500"></span> Trabajando</h3>
<span id="count-c4" class="bg-white border border-slate-200 text-slate-600 text-[10px] font-black px-2.5 py-1 rounded-lg shadow-sm">0</span>
</div>
<div id="col-working" class="kanban-cards space-y-3 no-scrollbar"></div>
<div id="col-4" class="kanban-cards space-y-3 no-scrollbar"></div>
</div>
<div class="kanban-col bg-slate-100/70 rounded-[2rem] p-4 border border-slate-200/60 shadow-inner">
<div class="flex justify-between items-center mb-4 px-2">
<h3 class="font-black text-slate-700 uppercase tracking-widest text-xs flex items-center gap-2"><span class="w-2.5 h-2.5 rounded-full bg-purple-500"></span> Incidencias</h3>
<span id="count-c5" class="bg-white border border-slate-200 text-slate-600 text-[10px] font-black px-2.5 py-1 rounded-lg shadow-sm">0</span>
</div>
<div id="col-5" class="kanban-cards space-y-3 no-scrollbar"></div>
</div>
</div>
@@ -589,14 +590,49 @@
}
// =====================================
// MOTOR KANBAN
// MOTOR KANBAN E INTELIGENCIA DE ESTADOS
// =====================================
function getServiceStateInfo(s) {
const raw = s.raw_data || {};
const dbStat = raw.status_operativo;
if (!s.assigned_name && (s.automation_status === 'in_progress' || s.automation_status === 'failed')) {
return { id: 'bolsa', name: s.automation_status === 'in_progress' ? 'Buscando Operario' : 'Fallo en Bolsa', color: s.automation_status === 'in_progress' ? 'amber' : 'red', is_final: false };
}
if (!s.assigned_name && (!dbStat || dbStat === 'sin_asignar')) {
return systemStatuses.find(st => st.name.toLowerCase().includes('pendiente de asignar')) || systemStatuses[0] || {id: 'sin_asignar', name: 'Sin Asignar', color: 'gray'};
}
const foundById = systemStatuses.find(st => String(st.id) === String(dbStat));
if (foundById) return foundById;
if (s.assigned_name && (!raw.scheduled_date || raw.scheduled_date === "")) {
const asignado = systemStatuses.find(st => st.name.toLowerCase() === 'asignado');
const esperando = systemStatuses.find(st => st.name.toLowerCase().includes('esperando'));
if (dbStat === 'esperando_cliente' && esperando) return esperando;
if (asignado) return asignado;
}
const stLower = String(dbStat).toLowerCase();
if (stLower === 'citado' || (s.assigned_name && raw.scheduled_date && !dbStat)) {
const citado = systemStatuses.find(st => st.name.toLowerCase().includes('citado'));
if(citado) return citado;
}
if (stLower === 'de_camino') return systemStatuses.find(st => st.name.toLowerCase().includes('camino')) || {name: 'De Camino', color: 'blue'};
if (stLower === 'trabajando') return systemStatuses.find(st => st.name.toLowerCase().includes('trabajando')) || {name: 'Trabajando', color: 'emerald'};
if (stLower === 'incidencia') return systemStatuses.find(st => st.name.toLowerCase().includes('incidencia')) || {name: 'Incidencia', color: 'purple'};
return { id: 'unknown', name: 'Desconocido', color: 'gray', is_final: false };
}
function renderKanban() {
const searchTerm = document.getElementById('searchFilter').value.toLowerCase();
const selectedOp = document.getElementById('opFilter').value;
const weekValue = document.getElementById('weekFilter').value;
const cols = { unassigned: [], unscheduled: [], pending_start: [], working: [] };
const cols = { c1: [], c2: [], c3: [], c4: [], c5: [] };
localData.forEach(s => {
if (s.status === 'archived' || s.provider === 'SYSTEM_BLOCK') return;
@@ -618,40 +654,46 @@
if (!raw.scheduled_date || !isDateInWeekString(raw.scheduled_date, weekValue)) return;
}
const dbStat = raw.status_operativo;
const statusObj = systemStatuses.find(st => String(st.id) === String(dbStat));
const stName = (statusObj?.name || "").toLowerCase();
// INTELIGENCIA DE ESTADOS
const stateInfo = getServiceStateInfo(s);
s._stateInfo = stateInfo; // Guardamos para la tarjeta
const stName = (stateInfo.name || "").toLowerCase();
// Filtro de Estado (Pastillas superiores)
if (activeStatusFilter !== "ALL" && String(statusObj?.id) !== activeStatusFilter) return;
// Filtro de Pastillas
if (activeStatusFilter !== "ALL" && String(stateInfo.id) !== activeStatusFilter) return;
// Si está finalizado o anulado, NO SE MUESTRA en el tablero Kanban
if (statusObj?.is_final || stName.includes('finaliza') || stName.includes('anulad') || stName.includes('terminad')) return;
// Ocultar finalizados
if (stateInfo.is_final || stName.includes('finaliza') || stName.includes('anulad') || stName.includes('terminad')) return;
const isWorking = stName.includes('trabaja') || stName.includes('camino');
const hasDate = raw.scheduled_date && raw.scheduled_date.trim() !== "";
const isIncident = stName.includes('incidencia') || stName.includes('pausa') || stName.includes('espera');
// REPARTO KANBAN INTELIGENTE
// REPARTO KANBAN INTELIGENTE (5 COLUMNAS)
if (!s.assigned_to) {
cols.unassigned.push(s);
cols.c1.push(s);
} else if (isIncident) {
cols.c5.push(s);
} else if (!hasDate) {
cols.unscheduled.push(s);
cols.c2.push(s);
} else if (!isWorking) {
cols.pending_start.push(s);
cols.c3.push(s);
} else {
cols.working.push(s);
cols.c4.push(s);
}
});
document.getElementById('count-unassigned').innerText = cols.unassigned.length;
document.getElementById('count-unscheduled').innerText = cols.unscheduled.length;
document.getElementById('count-pending-start').innerText = cols.pending_start.length;
document.getElementById('count-working').innerText = cols.working.length;
document.getElementById('count-c1').innerText = cols.c1.length;
document.getElementById('count-c2').innerText = cols.c2.length;
document.getElementById('count-c3').innerText = cols.c3.length;
document.getElementById('count-c4').innerText = cols.c4.length;
document.getElementById('count-c5').innerText = cols.c5.length;
document.getElementById('col-unassigned').innerHTML = cols.unassigned.map(s => buildCard(s, 1)).join('');
document.getElementById('col-unscheduled').innerHTML = cols.unscheduled.map(s => buildCard(s, 2)).join('');
document.getElementById('col-pending-start').innerHTML = cols.pending_start.map(s => buildCard(s, 3)).join('');
document.getElementById('col-working').innerHTML = cols.working.map(s => buildCard(s, 4)).join('');
document.getElementById('col-1').innerHTML = cols.c1.map(s => buildCard(s, 1)).join('');
document.getElementById('col-2').innerHTML = cols.c2.map(s => buildCard(s, 2)).join('');
document.getElementById('col-3').innerHTML = cols.c3.map(s => buildCard(s, 3)).join('');
document.getElementById('col-4').innerHTML = cols.c4.map(s => buildCard(s, 4)).join('');
document.getElementById('col-5').innerHTML = cols.c5.map(s => buildCard(s, 5)).join('');
lucide.createIcons();
}
@@ -674,10 +716,9 @@
if (hasLock) alerts += `<span class="bg-slate-800 text-white p-1 rounded shadow-sm"><i data-lucide="lock" class="w-3 h-3"></i></span>`;
if (hasEyes) alerts += `<span class="bg-amber-500 text-white p-1 rounded shadow-sm animate-pulse"><i data-lucide="eye" class="w-3 h-3"></i></span>`;
// Identificar el estado exacto actual para ponerle color a la etiqueta
const dbStat = raw.status_operativo;
const statusObj = systemStatuses.find(st => String(st.id) === String(dbStat)) || systemStatuses[0];
const cMap = colorDict[statusObj?.color] || colorDict['gray'];
// Identificar el estado exacto actual leyendo la IA guardada
const stateInfo = s._stateInfo || {name: 'Desconocido', color: 'gray'};
const cMap = colorDict[stateInfo.color] || colorDict['gray'];
// Pie de tarjeta dinámico
let bottomInfo = '';