Actualizar servicios.html
This commit is contained in:
@@ -16,21 +16,40 @@
|
|||||||
<body class="bg-slate-50 text-slate-800 font-sans antialiased text-left">
|
<body class="bg-slate-50 text-slate-800 font-sans antialiased text-left">
|
||||||
|
|
||||||
<div class="flex h-screen overflow-hidden">
|
<div class="flex h-screen overflow-hidden">
|
||||||
<div id="sidebar-container" class="h-full"></div>
|
<div id="sidebar-container" class="h-full shrink-0"></div>
|
||||||
<div class="flex-1 flex flex-col overflow-hidden">
|
|
||||||
|
<div class="flex-1 flex flex-col overflow-hidden relative">
|
||||||
<div id="header-container"></div>
|
<div id="header-container"></div>
|
||||||
|
|
||||||
<main class="flex-1 overflow-y-auto p-6 no-scrollbar text-left">
|
<main class="flex-1 overflow-x-hidden overflow-y-auto bg-slate-50 p-6 no-scrollbar text-left relative">
|
||||||
<div class="flex justify-between items-center mb-8">
|
<div class="fade-in max-w-full mx-auto space-y-6">
|
||||||
|
|
||||||
|
<div class="flex justify-between items-center bg-white p-6 rounded-[2rem] shadow-sm border border-slate-100">
|
||||||
<div>
|
<div>
|
||||||
<h2 class="text-2xl font-black text-slate-800 tracking-tight">PANEL OPERATIVO</h2>
|
<h2 class="text-2xl font-black text-slate-800 tracking-tight flex items-center gap-3">
|
||||||
<p class="text-sm text-slate-500 font-medium">Tablero Kanban de gestión de expedientes.</p>
|
<span class="bg-blue-600 p-2.5 rounded-xl text-white shadow-lg shadow-blue-200"><i data-lucide="kanban"></i></span>
|
||||||
|
PANEL OPERATIVO
|
||||||
|
</h2>
|
||||||
|
<p class="text-sm text-slate-500 mt-1 font-medium">Tablero Kanban de gestión de expedientes.</p>
|
||||||
</div>
|
</div>
|
||||||
<button onclick="openCreateModal()" class="bg-slate-900 hover:bg-blue-600 text-white px-6 py-3 rounded-2xl shadow-xl flex items-center gap-3 font-black text-xs uppercase tracking-widest transition-all active:scale-95">
|
<button onclick="openCreateModal()" class="bg-slate-900 hover:bg-blue-600 text-white px-6 py-3.5 rounded-2xl shadow-xl flex items-center gap-3 font-black text-xs uppercase tracking-widest transition-all active:scale-95">
|
||||||
<i data-lucide="plus-circle" class="w-5 h-5"></i> Nuevo Servicio
|
<i data-lucide="plus-circle" class="w-5 h-5"></i> Nuevo Servicio
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div class="flex flex-wrap gap-4 items-center bg-white p-4 rounded-[1.5rem] shadow-sm border border-slate-100 mb-6">
|
||||||
|
<div class="relative flex-1 min-w-[250px]">
|
||||||
|
<i data-lucide="search" class="w-4 h-4 absolute left-4 top-1/2 -translate-y-1/2 text-slate-400"></i>
|
||||||
|
<input type="text" id="searchFilter" oninput="renderLists()" placeholder="Buscar por cliente, REF, población, teléfono..." class="w-full pl-11 pr-4 py-3 bg-slate-50 border border-slate-200 rounded-xl text-xs font-bold focus:ring-2 focus:ring-blue-500 outline-none transition-all">
|
||||||
|
</div>
|
||||||
|
<div class="relative w-full md:w-64">
|
||||||
|
<select id="opFilter" 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 uppercase tracking-widest text-slate-600 appearance-none pr-10 cursor-pointer">
|
||||||
|
<option value="ALL">TODOS LOS OPERARIOS</option>
|
||||||
|
</select>
|
||||||
|
<i data-lucide="chevron-down" class="w-4 h-4 absolute right-4 top-1/2 -translate-y-1/2 text-slate-400 pointer-events-none"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
|
<div class="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
|
||||||
|
|
||||||
<div class="space-y-4 bg-slate-100/50 p-4 rounded-[2rem] border border-slate-200/60">
|
<div class="space-y-4 bg-slate-100/50 p-4 rounded-[2rem] border border-slate-200/60">
|
||||||
@@ -58,6 +77,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -212,15 +232,65 @@
|
|||||||
headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` }
|
headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` }
|
||||||
});
|
});
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
if (data.ok) { localData = data.services; renderLists(); }
|
if (data.ok) {
|
||||||
|
localData = data.services;
|
||||||
|
updateOperatorFilter(); // Actualiza el select de operarios
|
||||||
|
renderLists();
|
||||||
|
}
|
||||||
} catch (e) { console.error(e); }
|
} catch (e) { console.error(e); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function updateOperatorFilter() {
|
||||||
|
const opSelect = document.getElementById('opFilter');
|
||||||
|
const currentValue = opSelect.value;
|
||||||
|
|
||||||
|
// Extraer nombres únicos de operarios asignados en este panel
|
||||||
|
const uniqueOps = [...new Set(localData.map(s => s.assigned_name).filter(Boolean))].sort();
|
||||||
|
|
||||||
|
let html = '<option value="ALL">TODOS LOS OPERARIOS</option>';
|
||||||
|
uniqueOps.forEach(op => {
|
||||||
|
html += `<option value="${op}">${op}</option>`;
|
||||||
|
});
|
||||||
|
|
||||||
|
opSelect.innerHTML = html;
|
||||||
|
// Restaurar la selección previa si sigue existiendo
|
||||||
|
if (html.includes(`value="${currentValue}"`)) {
|
||||||
|
opSelect.value = currentValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function renderLists() {
|
function renderLists() {
|
||||||
|
const searchTerm = document.getElementById('searchFilter').value.toLowerCase();
|
||||||
|
const selectedOp = document.getElementById('opFilter').value;
|
||||||
|
|
||||||
|
// FILTRO DE DATOS
|
||||||
|
const filteredData = localData.filter(s => {
|
||||||
|
const raw = s.raw_data;
|
||||||
|
const name = (raw["Nombre Cliente"] || raw["CLIENTE"] || "").toLowerCase();
|
||||||
|
const addr = (raw["Dirección"] || raw["DOMICILIO"] || "").toLowerCase();
|
||||||
|
const pop = (raw["Población"] || raw["POBLACION-PROVINCIA"] || "").toLowerCase();
|
||||||
|
const phone = (raw["Teléfono"] || raw["TELEFONO"] || "").toLowerCase();
|
||||||
|
const ref = (s.service_ref || "").toLowerCase();
|
||||||
|
const assigned = s.assigned_name || "";
|
||||||
|
|
||||||
|
// Buscar texto (Nombre, Ref, Dirección, Población, Teléfono)
|
||||||
|
const matchesSearch = searchTerm === "" ||
|
||||||
|
name.includes(searchTerm) ||
|
||||||
|
ref.includes(searchTerm) ||
|
||||||
|
addr.includes(searchTerm) ||
|
||||||
|
pop.includes(searchTerm) ||
|
||||||
|
phone.includes(searchTerm);
|
||||||
|
|
||||||
|
// Buscar Operario
|
||||||
|
const matchesOp = selectedOp === "ALL" || assigned === selectedOp;
|
||||||
|
|
||||||
|
return matchesSearch && matchesOp;
|
||||||
|
});
|
||||||
|
|
||||||
// Lógica inteligente de 3 columnas apoyada en el raw_data
|
// Lógica inteligente de 3 columnas apoyada en el raw_data
|
||||||
const unassigned = localData.filter(s => !s.assigned_name || s.raw_data.status_operativo === 'sin_asignar');
|
const unassigned = filteredData.filter(s => !s.assigned_name || s.raw_data.status_operativo === 'sin_asignar');
|
||||||
const pending = localData.filter(s => s.assigned_name && (!s.raw_data.scheduled_date || s.raw_data.scheduled_date === "") && s.raw_data.status_operativo !== 'sin_asignar');
|
const pending = filteredData.filter(s => s.assigned_name && (!s.raw_data.scheduled_date || s.raw_data.scheduled_date === "") && s.raw_data.status_operativo !== 'sin_asignar');
|
||||||
const assigned = localData.filter(s => s.raw_data.scheduled_date && s.raw_data.scheduled_date !== "" && s.raw_data.status_operativo !== 'sin_asignar');
|
const assigned = filteredData.filter(s => s.raw_data.scheduled_date && s.raw_data.scheduled_date !== "" && s.raw_data.status_operativo !== 'sin_asignar');
|
||||||
|
|
||||||
document.getElementById('unassigned-list').innerHTML = unassigned.length > 0
|
document.getElementById('unassigned-list').innerHTML = unassigned.length > 0
|
||||||
? unassigned.map(s => cardTemplate(s, 'rose', s.assigned_name ? 'Pausado' : 'Sin Asignar')).join('')
|
? unassigned.map(s => cardTemplate(s, 'rose', s.assigned_name ? 'Pausado' : 'Sin Asignar')).join('')
|
||||||
@@ -281,7 +351,6 @@
|
|||||||
document.getElementById('detId').value = s.id;
|
document.getElementById('detId').value = s.id;
|
||||||
document.getElementById('detRef').innerText = s.service_ref;
|
document.getElementById('detRef').innerText = s.service_ref;
|
||||||
|
|
||||||
// COMPAÑÍA ASEGURADORA
|
|
||||||
const companyName = raw['Compañía'] || raw['COMPAÑIA'] || raw['Procedencia'] || "Particular";
|
const companyName = raw['Compañía'] || raw['COMPAÑIA'] || raw['Procedencia'] || "Particular";
|
||||||
document.getElementById('detCompany').innerText = companyName;
|
document.getElementById('detCompany').innerText = companyName;
|
||||||
|
|
||||||
@@ -301,7 +370,6 @@
|
|||||||
document.getElementById('dateInput').value = raw.scheduled_date || "";
|
document.getElementById('dateInput').value = raw.scheduled_date || "";
|
||||||
document.getElementById('timeInput').value = raw.scheduled_time || "";
|
document.getElementById('timeInput').value = raw.scheduled_time || "";
|
||||||
|
|
||||||
// Si no tiene estado previo guardado, mostramos "citado" por defecto o "sin asignar" si procede.
|
|
||||||
let defaultStatus = raw.status_operativo;
|
let defaultStatus = raw.status_operativo;
|
||||||
if (!defaultStatus) {
|
if (!defaultStatus) {
|
||||||
defaultStatus = (!s.assigned_name) ? 'sin_asignar' : 'citado';
|
defaultStatus = (!s.assigned_name) ? 'sin_asignar' : 'citado';
|
||||||
|
|||||||
Reference in New Issue
Block a user