Actualizar servicios.html
This commit is contained in:
@@ -260,6 +260,24 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="panelEnBolsa" class="bg-amber-50/40 p-6 rounded-[1.5rem] border border-amber-200 flex flex-col justify-center items-center shadow-sm h-full hidden text-center space-y-4">
|
||||
<div class="w-16 h-16 bg-amber-100 text-amber-500 rounded-full flex items-center justify-center animate-pulse shadow-inner">
|
||||
<i data-lucide="bot" class="w-8 h-8"></i>
|
||||
</div>
|
||||
<div>
|
||||
<h4 class="font-black text-slate-800 text-lg uppercase tracking-tight">Servicio en Bolsa</h4>
|
||||
<p class="text-xs text-slate-500 mt-2 font-medium">El robot está buscando operario automáticamente. Por favor, no interfieras a menos que sea urgente.</p>
|
||||
</div>
|
||||
<div class="w-full space-y-3 mt-6">
|
||||
<button onclick="stopAutomation()" class="w-full bg-rose-500 hover:bg-rose-600 text-white font-black py-3.5 rounded-xl text-xs uppercase transition-colors shadow-md flex justify-center items-center gap-2">
|
||||
<i data-lucide="octagon" class="w-4 h-4"></i> Detener y Asignar Manual
|
||||
</button>
|
||||
<button onclick="closeDetailModal()" class="w-full bg-white border border-slate-200 text-slate-600 font-bold py-3.5 rounded-xl text-xs uppercase hover:bg-slate-50 transition-colors">
|
||||
Dejar que el Robot siga
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -379,7 +397,7 @@
|
||||
|
||||
// 1. Prioridad Máxima: Bolsa de Trabajo / Robot WhatsApp
|
||||
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', isBlocked: true, is_final: false };
|
||||
return { id: 'bolsa', name: s.automation_status === 'in_progress' ? 'Buscando Operario' : 'Fallo en Bolsa', color: s.automation_status === 'in_progress' ? 'amber' : 'red', isBlocked: false, is_final: false };
|
||||
}
|
||||
|
||||
// 2. Si viene limpio del scraper sin estado -> Pendiente de Asignar
|
||||
@@ -513,17 +531,15 @@
|
||||
if(stateInfo.name.toLowerCase().includes('reparaci') || stateInfo.name.toLowerCase().includes('trabaja')) iconEstado = 'wrench';
|
||||
if(stateInfo.name.toLowerCase().includes('incidencia') || stateInfo.name.toLowerCase().includes('pausado')) iconEstado = 'alert-triangle';
|
||||
|
||||
const isBlocked = stateInfo.isBlocked;
|
||||
const clickAction = isBlocked ? `shakeCard(this, '${s.automation_status}'); event.stopPropagation();` : `openDetail(${s.id})`;
|
||||
const cursorStyle = isBlocked ? 'cursor-not-allowed' : 'cursor-pointer';
|
||||
const inProgressBadge = s.automation_status === 'in_progress' ? `<span class="absolute -top-3 -right-3 bg-amber-400 text-white px-3 py-1 rounded-full text-[10px] font-black uppercase shadow-lg border-2 border-white flex items-center gap-1 animate-pulse z-10"><i data-lucide="bot" class="w-3 h-3"></i> En Bolsa</span>` : '';
|
||||
|
||||
return `
|
||||
<div class="bg-white p-5 rounded-3xl border border-slate-200 shadow-sm card-hover text-left flex flex-col justify-between relative ${cursorStyle}" onclick="${clickAction}">
|
||||
|
||||
<div class="bg-white p-5 rounded-3xl border border-slate-200 shadow-sm card-hover text-left flex flex-col justify-between relative cursor-pointer" onclick="openDetail(${s.id})">
|
||||
${inProgressBadge}
|
||||
<div class="space-y-3">
|
||||
<div class="flex items-start justify-between w-full gap-2">
|
||||
<span class="text-[9px] font-black ${colorData.bg} ${colorData.text} px-2.5 py-1.5 rounded-lg uppercase tracking-wider flex items-center gap-1.5 truncate border ${colorData.border}">
|
||||
${isBlocked ? `<span class="w-1.5 h-1.5 bg-current rounded-full pulse-slow opacity-70 shrink-0"></span>` : `<div class="w-2 h-2 rounded-full ${colorData.dot}"></div>`}
|
||||
<div class="w-2 h-2 rounded-full ${colorData.dot}"></div>
|
||||
<span class="truncate">${stateInfo.name}</span>
|
||||
</span>
|
||||
${isUrgent ? `<span class="bg-red-500 text-white px-2 py-1 rounded-lg text-[9px] font-black uppercase shadow-sm animate-pulse shrink-0">🔥 URGENTE</span>` : `<span class="text-[10px] text-slate-400 font-bold uppercase bg-slate-50 border border-slate-100 px-2.5 py-1 rounded-lg shrink-0">#${s.service_ref}</span>`}
|
||||
@@ -546,7 +562,7 @@
|
||||
<div class="bg-slate-100 p-1.5 rounded-lg text-slate-500 shrink-0"><i data-lucide="hard-hat" class="w-3.5 h-3.5"></i></div>
|
||||
<span class="text-[10px] font-black text-slate-600 uppercase truncate" title="${s.assigned_name || 'Sin asignar'}">${s.assigned_name || 'Sin asignar'}</span>
|
||||
</div>
|
||||
${raw.scheduled_date && !isBlocked ? `
|
||||
${raw.scheduled_date ? `
|
||||
<div class="flex items-center gap-1.5 text-blue-600 shrink-0 ml-2 bg-blue-50 px-2 py-1 rounded-md border border-blue-100">
|
||||
<i data-lucide="${iconEstado}" class="w-3.5 h-3.5"></i>
|
||||
<span class="text-[9px] font-black uppercase">${cita}</span>
|
||||
@@ -616,17 +632,23 @@
|
||||
|
||||
const stateInfo = s._stateInfo;
|
||||
|
||||
// Mostrar u ocultar paneles de asignación dependiendo del estado
|
||||
if (s.assigned_name && stateInfo.id !== 'bolsa' && !stateInfo.name.toLowerCase().includes('asignar') && !stateInfo.name.toLowerCase().includes('desasignado')) {
|
||||
// --- LÓGICA DE VISIBILIDAD DE PANELES MODIFICADA ---
|
||||
if (s.automation_status === 'in_progress') {
|
||||
document.getElementById('panelEnBolsa').classList.remove('hidden');
|
||||
document.getElementById('panelAsignado').classList.add('hidden');
|
||||
document.getElementById('panelSinAsignar').classList.add('hidden');
|
||||
}
|
||||
else if (s.assigned_name && stateInfo.id !== 'bolsa' && !stateInfo.name.toLowerCase().includes('asignar') && !stateInfo.name.toLowerCase().includes('desasignado')) {
|
||||
document.getElementById('panelEnBolsa').classList.add('hidden');
|
||||
document.getElementById('panelAsignado').classList.remove('hidden');
|
||||
document.getElementById('panelSinAsignar').classList.add('hidden');
|
||||
|
||||
document.getElementById('detWorker').innerText = s.assigned_name;
|
||||
document.getElementById('dateInput').value = raw.scheduled_date || "";
|
||||
document.getElementById('timeInput').value = raw.scheduled_time || "";
|
||||
|
||||
document.getElementById('detStatusMap').value = stateInfo.id;
|
||||
} else {
|
||||
document.getElementById('panelEnBolsa').classList.add('hidden');
|
||||
document.getElementById('panelAsignado').classList.add('hidden');
|
||||
document.getElementById('panelSinAsignar').classList.remove('hidden');
|
||||
|
||||
@@ -644,6 +666,24 @@
|
||||
lucide.createIcons();
|
||||
}
|
||||
|
||||
async function stopAutomation() {
|
||||
const id = document.getElementById('detId').value;
|
||||
if(!confirm("¿Deseas cancelar la búsqueda del robot para asignar este servicio manualmente?")) return;
|
||||
|
||||
try {
|
||||
await fetch(`${API_URL}/providers/scraped/${id}`, {
|
||||
method: 'PUT',
|
||||
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` },
|
||||
body: JSON.stringify({ automation_status: 'manual' })
|
||||
});
|
||||
showToast("Bolsa detenida. Ya puedes asignar el servicio.");
|
||||
closeDetailModal();
|
||||
refreshPanel();
|
||||
} catch (e) {
|
||||
alert("Error al detener el automatismo.");
|
||||
}
|
||||
}
|
||||
|
||||
async function saveAppointment() {
|
||||
const id = document.getElementById('detId').value;
|
||||
const date = document.getElementById('dateInput').value;
|
||||
@@ -711,19 +751,31 @@
|
||||
const name = select.options[select.selectedIndex].text;
|
||||
const estadoAsignado = systemStatuses.find(st => st.name.toLowerCase() === 'asignado') || systemStatuses[1];
|
||||
|
||||
// 1. PRIMERA LLAMADA: Detenemos la bolsa de trabajo automática
|
||||
await fetch(`${API_URL}/providers/scraped/${id}`, {
|
||||
method: 'PUT',
|
||||
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` },
|
||||
body: JSON.stringify({ automation_status: 'completed' })
|
||||
});
|
||||
|
||||
// 2. SEGUNDA LLAMADA: Guardamos el operario y el estado "Asignado"
|
||||
await fetch(`${API_URL}/providers/scraped/${id}`, {
|
||||
method: 'PUT',
|
||||
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` },
|
||||
body: JSON.stringify({
|
||||
automation_status: 'completed',
|
||||
assigned_to: assigned_to,
|
||||
assigned_name: name,
|
||||
assigned_to_name: name,
|
||||
guild_id: guild_id,
|
||||
status_operativo: estadoAsignado ? String(estadoAsignado.id) : 'asignado_operario'
|
||||
})
|
||||
});
|
||||
closeDetailModal(); showToast("Asignado correctamente"); refreshPanel();
|
||||
} catch (e) { alert("Error"); }
|
||||
|
||||
closeDetailModal();
|
||||
showToast("Asignado y notificado correctamente");
|
||||
refreshPanel();
|
||||
} catch (e) {
|
||||
alert("Error en la asignación manual");
|
||||
}
|
||||
}
|
||||
|
||||
async function saveNewService(e) {
|
||||
|
||||
Reference in New Issue
Block a user