Actualizar proveedores.html
This commit is contained in:
295
proveedores.html
295
proveedores.html
@@ -91,6 +91,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="inboxContainer" class="space-y-4 text-left"></div>
|
<div id="inboxContainer" class="space-y-4 text-left"></div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
@@ -302,6 +303,134 @@
|
|||||||
} catch (e) { console.error("Error en loadInbox:", e); }
|
} 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() {
|
function renderFilteredInbox() {
|
||||||
const container = document.getElementById('inboxContainer');
|
const container = document.getElementById('inboxContainer');
|
||||||
const search = document.getElementById('searchBox').value.toUpperCase();
|
const search = document.getElementById('searchBox').value.toUpperCase();
|
||||||
@@ -326,147 +455,45 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
container.innerHTML = "";
|
container.innerHTML = "";
|
||||||
|
|
||||||
if(filtered.length === 0) {
|
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>';
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
filtered.forEach(svc => {
|
// SEPARAR ACTIVOS Y ARCHIVADOS
|
||||||
const raw = svc.raw_data || {};
|
const activeServices = filtered.filter(svc => svc.status !== 'archived');
|
||||||
const isArchived = svc.status === 'archived';
|
const archivedServices = filtered.filter(svc => svc.status === 'archived');
|
||||||
|
|
||||||
|
// 1. RENDERIZAR ACTIVOS
|
||||||
|
activeServices.forEach(svc => container.appendChild(buildServiceCard(svc)));
|
||||||
|
|
||||||
|
// 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');
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
const archContainer = document.createElement('div');
|
||||||
|
archContainer.id = 'archivedCardsContainer';
|
||||||
|
archContainer.className = `space-y-4 fade-in ${isAutoExpanded ? '' : 'hidden'}`;
|
||||||
|
|
||||||
const name = raw['Nombre Cliente'] || raw['CLIENTE'] || "S/N";
|
archivedServices.forEach(svc => archContainer.appendChild(buildServiceCard(svc)));
|
||||||
const addr = raw['Dirección'] || raw['DOMICILIO'] || "";
|
container.appendChild(archContainer);
|
||||||
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 = '';
|
|
||||||
|
|
||||||
// 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();
|
|
||||||
|
|
||||||
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 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>`;
|
|
||||||
container.appendChild(card);
|
|
||||||
});
|
|
||||||
lucide.createIcons();
|
lucide.createIcons();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user