Actualizar proveedores.html

This commit is contained in:
2026-02-17 21:25:04 +00:00
parent 9ee11c8f02
commit a721f6cd86

View File

@@ -91,6 +91,7 @@
</div>
<div id="inboxContainer" class="space-y-4 text-left"></div>
</div>
</main>
</div>
@@ -302,6 +303,134 @@
} catch (e) { console.error("Error en loadInbox:", e); }
}
// NUEVA FUNCIÓN PARA CONSTRUIR LA TARJETA HTML (Evita repetir código)
function buildServiceCard(svc) {
const raw = svc.raw_data || {};
const isArchived = svc.status === 'archived';
const name = raw['Nombre Cliente'] || raw['CLIENTE'] || "S/N";
const addr = raw['Dirección'] || raw['DOMICILIO'] || "";
const pop = raw['Población'] || raw['POBLACION-PROVINCIA'] || "";
const fullAddr = `${addr} ${pop}`.trim();
const phone = (raw['Teléfono'] || raw['TELEFONOS'] || raw['TELEFONO'] || "").match(/[6789]\d{8}/)?.[0] || "";
const guildName = allGuilds.find(g => g.id == raw['guild_id'])?.name || null;
const opName = raw['assigned_to_name'] || null;
let badgeEstado = '';
let bgClass = 'bg-white';
let isLocked = false;
let lockedMsg = '';
const autoStatus = (svc.automation_status || '').toLowerCase();
const sysStatus = (svc.status || '').toLowerCase();
const linkedStatus = (raw['estado'] || raw['status'] || '').toLowerCase();
if (!isArchived) {
if (autoStatus.includes('bolsa') || autoStatus === 'in_progress' || sysStatus.includes('bolsa') || linkedStatus.includes('bolsa')) {
bgClass = 'bg-orange-50/40 border-orange-200 hover:border-orange-400';
isLocked = true;
lockedMsg = 'Este servicio está en la BOLSA buscando operario.';
badgeEstado = `
<div class="flex flex-col items-end gap-1">
<span class="bg-orange-100 text-orange-700 px-3 py-1 rounded-full text-[10px] font-black uppercase flex items-center gap-1.5 border border-orange-200 shadow-sm">
<span class="w-1.5 h-1.5 bg-orange-500 rounded-full pulse-slow"></span> En Bolsa
</span>
<span class="text-[9px] font-bold text-slate-500 max-w-[120px] truncate text-right">Buscando operario...</span>
</div>`;
} else if (raw['assigned_to'] || (sysStatus === 'imported' && opName)) {
bgClass = 'bg-emerald-50/40 border-emerald-200 hover:border-emerald-400';
isLocked = true;
lockedMsg = 'Servicio ASIGNADO. Ve al Panel Operativo para gestionarlo.';
badgeEstado = `
<div class="flex flex-col items-end gap-1">
<span class="bg-emerald-100 text-emerald-700 px-3 py-1 rounded-full text-[10px] font-black uppercase flex items-center gap-1.5 border border-emerald-200 shadow-sm">
<i data-lucide="check-circle-2" class="w-3 h-3"></i> Asignado
</span>
<span class="text-[9px] font-bold text-slate-500 max-w-[120px] truncate text-right">${opName || 'Operario'}</span>
</div>`;
} else if (sysStatus === 'imported') {
bgClass = 'bg-blue-50/40 border-blue-200 hover:border-blue-400';
isLocked = true;
lockedMsg = 'Este servicio ya se traspasó al Panel Operativo.';
badgeEstado = `
<div class="flex flex-col items-end gap-1">
<span class="bg-blue-100 text-blue-700 px-3 py-1 rounded-full text-[10px] font-black uppercase flex items-center gap-1.5 border border-blue-200 shadow-sm">
<i data-lucide="clipboard-list" class="w-3 h-3"></i> En Panel
</span>
<span class="text-[9px] font-bold text-slate-500 max-w-[120px] truncate text-right">Esperando acción</span>
</div>`;
} else if (autoStatus === 'failed') {
bgClass = 'bg-red-50/40 border-red-200 hover:border-red-400';
isLocked = false;
badgeEstado = `
<div class="flex flex-col items-end gap-1">
<span class="bg-red-100 text-red-700 px-3 py-1 rounded-full text-[10px] font-black uppercase flex items-center gap-1.5 border border-red-200 shadow-sm">
<i data-lucide="alert-triangle" class="w-3 h-3"></i> Fallo Bolsa
</span>
<span class="text-[8px] font-bold text-red-400">Nadie aceptó</span>
</div>`;
} else {
bgClass = 'bg-white border-slate-200';
badgeEstado = `
<div class="flex flex-col items-end gap-1">
<span class="bg-slate-100 text-slate-500 px-3 py-1 rounded-full text-[10px] font-black uppercase flex items-center gap-1.5 border border-slate-200">
<i data-lucide="user-minus" class="w-3 h-3"></i> Sin Asignar
</span>
</div>`;
}
} else {
bgClass = 'bg-gray-50 border-gray-200 archived';
badgeEstado = `
<div class="flex flex-col items-end gap-1">
<span class="bg-gray-200 text-gray-500 px-3 py-1 rounded-full text-[10px] font-black uppercase flex items-center gap-1.5 border border-gray-300">
<i data-lucide="archive" class="w-3 h-3"></i> Archivado
</span>
</div>`;
}
const card = document.createElement('div');
card.id = `card-${svc.id}`;
card.className = `service-card p-5 rounded-2xl border ${bgClass} flex items-center justify-between transition-all group fade-in ${isLocked ? 'locked' : ''}`;
card.onclick = (e) => {
if (e.target.closest('a') || e.target.closest('button')) return;
if (isArchived) {
showToast("⚠️ Este servicio está ARCHIVADO.", true);
} else if (isLocked) {
card.classList.remove('shake');
void card.offsetWidth;
card.classList.add('shake');
showToast(`🔒 ${lockedMsg}`, true);
} else {
openEditor(svc.id);
}
};
card.innerHTML = `
<div class="flex items-center gap-4 min-w-0 flex-1">
<div class="w-16 h-16 rounded-2xl flex flex-col items-center justify-center shrink-0 shadow-sm border border-slate-100 ${isArchived ? 'bg-gray-200 text-gray-400' : (svc.provider === 'homeserve' ? 'bg-red-50 text-red-600' : 'bg-blue-50 text-blue-600')}">
<span class="text-[9px] font-black uppercase tracking-tighter">${svc.provider === 'multiasistencia' ? 'MULTI' : 'HOME'}</span>
<i data-lucide="${isArchived ? 'lock' : 'file-text'}" class="w-5 h-5 mt-0.5"></i>
</div>
<div class="w-12 h-12 rounded-xl bg-white border border-slate-100 p-2 flex items-center justify-center shrink-0 shadow-inner">
<img src="${getLogoUrl(raw['Compañía'] || raw['COMPAÑIA'])}" onerror="this.src='${companyLogos['DEFAULT']}'" class="max-w-full max-h-full object-contain">
</div>
<div class="min-w-0 flex-1">
<h3 class="font-black text-slate-800 truncate uppercase text-lg leading-tight">${name}</h3>
<p class="text-xs text-slate-400 truncate italic mt-0.5">${fullAddr}</p>
<div class="flex flex-wrap gap-2 mt-2">
<span class="text-[10px] bg-slate-100 text-slate-500 px-2 py-0.5 rounded-lg font-bold border">#${svc.service_ref}</span>
${guildName ? `<span class="text-[10px] bg-blue-50 text-blue-600 px-2 py-0.5 rounded-lg font-bold border border-blue-100"><i data-lucide="hammer" class="w-2.5 h-2.5 inline mr-1"></i>${guildName}</span>` : ''}
</div>
</div>
</div>
<div class="flex items-center gap-4 shrink-0 pl-4">
${badgeEstado}
</div>`;
return card;
}
function renderFilteredInbox() {
const container = document.getElementById('inboxContainer');
const search = document.getElementById('searchBox').value.toUpperCase();
@@ -326,147 +455,45 @@
});
container.innerHTML = "";
if(filtered.length === 0) {
container.innerHTML = '<div class="p-12 text-center text-slate-400 bg-white rounded-3xl border-2 border-dashed">No se encontraron expedientes con estos filtros.</div>';
return;
}
filtered.forEach(svc => {
const raw = svc.raw_data || {};
const isArchived = svc.status === 'archived';
// SEPARAR ACTIVOS Y ARCHIVADOS
const activeServices = filtered.filter(svc => svc.status !== 'archived');
const archivedServices = filtered.filter(svc => svc.status === 'archived');
const name = raw['Nombre Cliente'] || raw['CLIENTE'] || "S/N";
const addr = raw['Dirección'] || raw['DOMICILIO'] || "";
const pop = raw['Población'] || raw['POBLACION-PROVINCIA'] || "";
const fullAddr = `${addr} ${pop}`.trim();
const phone = (raw['Teléfono'] || raw['TELEFONOS'] || raw['TELEFONO'] || "").match(/[6789]\d{8}/)?.[0] || "";
const guildName = allGuilds.find(g => g.id == raw['guild_id'])?.name || null;
const opName = raw['assigned_to_name'] || null;
// 1. RENDERIZAR ACTIVOS
activeServices.forEach(svc => container.appendChild(buildServiceCard(svc)));
let badgeEstado = '';
let bgClass = 'bg-white';
let isLocked = false;
let lockedMsg = '';
// 2. RENDERIZAR ARCHIVADOS AL FINAL (CON ACORDEÓN)
if (archivedServices.length > 0) {
// Si el usuario busca algo específico o filtra solo archivados, abrimos el acordeón automáticamente
const isAutoExpanded = (search.length > 0 || status === 'archived');
// LA LÓGICA TOTALMENTE MODIFICADA CON EL CHIVATO DE DEPURACIÓN
const autoStatus = (svc.automation_status || '').toLowerCase();
const sysStatus = (svc.status || '').toLowerCase();
const linkedStatus = (raw['estado'] || raw['status'] || '').toLowerCase();
const divider = document.createElement('div');
divider.className = "mt-10 mb-4 border-t border-slate-200 pt-8 fade-in";
divider.innerHTML = `
<button onclick="document.getElementById('archivedCardsContainer').classList.toggle('hidden'); document.getElementById('archivedChevron').classList.toggle('rotate-180');"
class="w-full flex items-center justify-between bg-slate-200/50 hover:bg-slate-200 text-slate-500 px-6 py-4 rounded-[1.5rem] transition-all group border border-slate-200 shadow-sm">
<span class="font-black text-xs uppercase tracking-[0.15em] flex items-center gap-3">
<i data-lucide="archive" class="w-5 h-5 text-slate-400"></i> Expedientes Archivados (${archivedServices.length})
</span>
<i data-lucide="chevron-down" id="archivedChevron" class="w-5 h-5 transition-transform duration-300 ${isAutoExpanded ? 'rotate-180' : ''}"></i>
</button>
`;
container.appendChild(divider);
if (!isArchived) {
// 1. EN BOLSA / EN COLA
if (autoStatus.includes('bolsa') || autoStatus === 'in_progress' || sysStatus.includes('bolsa') || linkedStatus.includes('bolsa')) {
bgClass = 'bg-orange-50/40 border-orange-200 hover:border-orange-400';
isLocked = true;
lockedMsg = 'Este servicio está en la BOLSA buscando operario.';
badgeEstado = `
<div class="flex flex-col items-end gap-1">
<span class="bg-orange-100 text-orange-700 px-3 py-1 rounded-full text-[10px] font-black uppercase flex items-center gap-1.5 border border-orange-200 shadow-sm">
<span class="w-1.5 h-1.5 bg-orange-500 rounded-full pulse-slow"></span> En Bolsa
</span>
<span class="text-[9px] font-bold text-slate-500 max-w-[120px] truncate text-right">Buscando operario...</span>
</div>`;
}
// 2. ASIGNADO A UN OPERARIO
else if (raw['assigned_to'] || (sysStatus === 'imported' && opName)) {
bgClass = 'bg-emerald-50/40 border-emerald-200 hover:border-emerald-400';
isLocked = true;
lockedMsg = 'Servicio ASIGNADO. Ve al Panel Operativo para gestionarlo.';
badgeEstado = `
<div class="flex flex-col items-end gap-1">
<span class="bg-emerald-100 text-emerald-700 px-3 py-1 rounded-full text-[10px] font-black uppercase flex items-center gap-1.5 border border-emerald-200 shadow-sm">
<i data-lucide="check-circle-2" class="w-3 h-3"></i> Asignado
</span>
<span class="text-[9px] font-bold text-slate-500 max-w-[120px] truncate text-right">${opName || 'Operario'}</span>
</div>`;
}
// 3. TRASPASADO AL PANEL, PERO SIN BOLSA NI OPERARIO (Pausa/Manual)
else if (sysStatus === 'imported') {
bgClass = 'bg-blue-50/40 border-blue-200 hover:border-blue-400';
isLocked = true;
lockedMsg = 'Este servicio ya se traspasó al Panel Operativo.';
badgeEstado = `
<div class="flex flex-col items-end gap-1">
<span class="bg-blue-100 text-blue-700 px-3 py-1 rounded-full text-[10px] font-black uppercase flex items-center gap-1.5 border border-blue-200 shadow-sm">
<i data-lucide="clipboard-list" class="w-3 h-3"></i> En Panel
</span>
<span class="text-[9px] font-bold text-slate-500 max-w-[120px] truncate text-right">Esperando acción</span>
</div>`;
}
// 4. FALLO DE ASIGNACIÓN (Se acaban los operarios en cola)
else if (autoStatus === 'failed') {
bgClass = 'bg-red-50/40 border-red-200 hover:border-red-400';
isLocked = false;
badgeEstado = `
<div class="flex flex-col items-end gap-1">
<span class="bg-red-100 text-red-700 px-3 py-1 rounded-full text-[10px] font-black uppercase flex items-center gap-1.5 border border-red-200 shadow-sm">
<i data-lucide="alert-triangle" class="w-3 h-3"></i> Fallo Bolsa
</span>
<span class="text-[8px] font-bold text-red-400">Nadie aceptó</span>
</div>`;
}
// 5. NUEVO EN EL BUZÓN (Gris)
else {
bgClass = 'bg-white border-slate-200';
badgeEstado = `
<div class="flex flex-col items-end gap-1">
<span class="bg-slate-100 text-slate-500 px-3 py-1 rounded-full text-[10px] font-black uppercase flex items-center gap-1.5 border border-slate-200">
<i data-lucide="user-minus" class="w-3 h-3"></i> Sin Asignar
</span>
<span class="text-[8px] font-bold text-slate-300 uppercase tracking-widest mt-0.5">sys:${sysStatus}|auto:${autoStatus}</span>
</div>`;
}
} else {
bgClass = 'bg-gray-50 border-gray-200 archived';
badgeEstado = `
<div class="flex flex-col items-end gap-1">
<span class="bg-gray-200 text-gray-500 px-3 py-1 rounded-full text-[10px] font-black uppercase flex items-center gap-1.5 border border-gray-300">
<i data-lucide="archive" class="w-3 h-3"></i> Archivado
</span>
</div>`;
}
const archContainer = document.createElement('div');
archContainer.id = 'archivedCardsContainer';
archContainer.className = `space-y-4 fade-in ${isAutoExpanded ? '' : 'hidden'}`;
const card = document.createElement('div');
card.id = `card-${svc.id}`;
card.className = `service-card p-5 rounded-2xl border ${bgClass} flex items-center justify-between transition-all group fade-in ${isLocked ? 'locked' : ''}`;
archivedServices.forEach(svc => archContainer.appendChild(buildServiceCard(svc)));
container.appendChild(archContainer);
}
card.onclick = (e) => {
if (e.target.closest('a') || e.target.closest('button')) return;
if (isArchived) {
showToast("⚠️ Este servicio está ARCHIVADO.", true);
} else if (isLocked) {
card.classList.remove('shake');
void card.offsetWidth;
card.classList.add('shake');
showToast(`🔒 ${lockedMsg}`, true);
} else {
openEditor(svc.id);
}
};
card.innerHTML = `
<div class="flex items-center gap-4 min-w-0 flex-1">
<div class="w-16 h-16 rounded-2xl flex flex-col items-center justify-center shrink-0 shadow-sm border border-slate-100 ${isArchived ? 'bg-gray-200 text-gray-400' : (svc.provider === 'homeserve' ? 'bg-red-50 text-red-600' : 'bg-blue-50 text-blue-600')}">
<span class="text-[9px] font-black uppercase tracking-tighter">${svc.provider === 'multiasistencia' ? 'MULTI' : 'HOME'}</span>
<i data-lucide="${isArchived ? 'lock' : 'file-text'}" class="w-5 h-5 mt-0.5"></i>
</div>
<div class="w-12 h-12 rounded-xl bg-white border border-slate-100 p-2 flex items-center justify-center shrink-0 shadow-inner">
<img src="${getLogoUrl(raw['Compañía'] || raw['COMPAÑIA'])}" onerror="this.src='${companyLogos['DEFAULT']}'" class="max-w-full max-h-full object-contain">
</div>
<div class="min-w-0 flex-1">
<h3 class="font-black text-slate-800 truncate uppercase text-lg leading-tight">${name}</h3>
<p class="text-xs text-slate-400 truncate italic mt-0.5">${fullAddr}</p>
<div class="flex flex-wrap gap-2 mt-2">
<span class="text-[10px] bg-slate-100 text-slate-500 px-2 py-0.5 rounded-lg font-bold border">#${svc.service_ref}</span>
${guildName ? `<span class="text-[10px] bg-blue-50 text-blue-600 px-2 py-0.5 rounded-lg font-bold border border-blue-100"><i data-lucide="hammer" class="w-2.5 h-2.5 inline mr-1"></i>${guildName}</span>` : ''}
</div>
</div>
</div>
<div class="flex items-center gap-4 shrink-0 pl-4">
${badgeEstado}
</div>`;
container.appendChild(card);
});
lucide.createIcons();
}