Actualizar servicios.html

This commit is contained in:
2026-03-03 08:00:53 +00:00
parent 2f7203af05
commit 710b695949

View File

@@ -128,61 +128,114 @@
<button onclick="closeCreateModal()" class="text-slate-400 hover:text-red-500 bg-slate-50 p-2 rounded-full transition-colors"><i data-lucide="x" class="w-5 h-5"></i></button>
</div>
<form onsubmit="saveNewService(event)" class="flex-1 overflow-y-auto no-scrollbar p-6 bg-slate-50/50">
<div class="grid grid-cols-1 md:grid-cols-12 gap-6">
<div class="md:col-span-7 space-y-4">
<form onsubmit="saveNewService(event)" class="flex-1 overflow-y-auto no-scrollbar p-6 bg-slate-50/50">
<div class="grid grid-cols-1 md:grid-cols-12 gap-8">
<div class="md:col-span-7 space-y-6">
<div class="bg-white p-6 rounded-2xl border border-slate-200 shadow-sm space-y-4">
<h4 class="label-modern border-b pb-2"><i data-lucide="user" class="w-3 h-3 inline mr-1"></i> Datos del Cliente</h4>
<h4 class="font-black text-slate-800 uppercase text-xs flex items-center gap-2 border-b border-slate-100 pb-3 mb-4">
<i data-lucide="user" class="w-4 h-4 text-blue-500"></i> Datos del Cliente
</h4>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
<div>
<label class="label-modern">Teléfono</label>
<input type="tel" id="nPhone" oninput="searchClientByPhone(this.value)" placeholder="600000000" class="input-modern" required>
<input type="tel" id="nPhone" placeholder="Ej: 600123456" class="input-modern" required>
</div>
<div>
<label class="label-modern">Nombre Completo</label>
<input type="text" id="nName" placeholder="Nombre asegurado" class="input-modern" required>
<input type="text" id="nName" placeholder="Nombre del asegurado" class="input-modern" required>
</div>
</div>
<div>
<label class="label-modern">Dirección</label>
<input type="text" id="nAddr" placeholder="Calle, número, población..." class="input-modern" required>
<div id="addrSuggestions" class="hidden mt-2 p-2 bg-amber-50 border border-amber-200 rounded-lg text-[10px] font-bold text-amber-700">⚠️ Cliente con otras direcciones registradas.</div>
</div>
<div class="pt-2">
<label class="flex items-center gap-3 cursor-pointer">
<input type="checkbox" id="isCompanyCheck" onchange="toggleCompanyFields(this.checked)" class="w-5 h-5 rounded text-blue-600">
<span class="text-xs font-black text-slate-700 uppercase">¿Es de Compañía de Seguros?</span>
</label>
<div id="companyFields" class="hidden mt-4 space-y-4 p-4 bg-blue-50/30 rounded-xl border border-blue-100">
<select id="nCompanySelect" class="input-modern bg-white"><option value="">Seleccionar compañía...</option></select>
<input type="text" id="nExpRef" onblur="checkDuplicateRef(this.value)" placeholder="Nº Expediente" class="input-modern bg-white uppercase">
<p id="refAlert" class="hidden text-[10px] text-red-600 font-bold mt-1 uppercase">⚠️ Referencia ya existente</p>
</div>
<label class="label-modern">Dirección Completa</label>
<input type="text" id="nAddr" placeholder="Calle, número, piso, población..." class="input-modern" required>
</div>
</div>
<div class="bg-white p-6 rounded-2xl border border-slate-200 shadow-sm">
<label class="label-modern">Descripción de la Avería</label>
<textarea id="nDesc" placeholder="Detalla el problema..." rows="6" class="input-modern resize-none"></textarea>
<h4 class="font-black text-slate-800 uppercase text-xs flex items-center gap-2 border-b border-slate-100 pb-3 mb-4">
<i data-lucide="align-left" class="w-4 h-4 text-amber-500"></i> Detalles de la Avería
</h4>
<label class="label-modern">Descripción</label>
<textarea id="nDesc" placeholder="Describe el problema a reparar..." rows="4" class="input-modern resize-none"></textarea>
</div>
</div>
<div class="md:col-span-5 space-y-4">
<div class="bg-white p-6 rounded-2xl border border-slate-200 shadow-sm space-y-5">
<h4 class="label-modern border-b pb-2"><i data-lucide="settings" class="w-3 h-3 inline mr-1"></i> Asignación y Tiempo</h4>
<select id="nGuild" class="input-modern" onchange="loadOps(this.value, 'nWorker')"><option value="">Gremio...</option></select>
<select id="nWorker" class="input-modern"><option value="">Operario...</option></select>
<div class="md:col-span-5 space-y-6">
<div class="bg-white p-6 rounded-2xl border border-slate-200 shadow-sm space-y-4">
<h4 class="font-black text-slate-800 uppercase text-xs flex items-center gap-2 border-b border-slate-100 pb-3 mb-4">
<i data-lucide="hammer" class="w-4 h-4 text-purple-500"></i> Asignación
</h4>
<div>
<label class="label-modern">Duración Estimada</label>
<select id="nDuration" class="input-modern font-black text-blue-600 uppercase">
<option value="15">15 min</option><option value="30">30 min</option><option value="60" selected>1 hora</option>
<option value="90">1.5 horas</option><option value="120">2 horas</option><option value="180">3 horas</option><option value="240">4 horas</option>
<label class="label-modern">Gremio Recomendado</label>
<div class="relative">
<select id="nGuild" class="input-modern appearance-none cursor-pointer pr-10" onchange="loadOps(this.value, 'nWorker')">
<option value="">Seleccionar gremio...</option>
</select>
<i data-lucide="chevron-down" class="w-4 h-4 absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 pointer-events-none"></i>
</div>
</div>
<button type="submit" name="action" value="manual" class="w-full bg-emerald-600 text-white font-black py-4 rounded-2xl text-xs uppercase shadow-lg">Crear y Guardar</button>
<button type="submit" name="action" value="auto" class="w-full bg-slate-900 text-white font-black py-4 rounded-2xl text-xs uppercase flex items-center justify-center gap-2"><i data-lucide="zap" class="w-4 h-4 text-amber-400"></i> Mandar a la Bolsa</button>
<div class="grid grid-cols-2 gap-4">
<div>
<label class="label-modern">Operario</label>
<div class="relative">
<select id="nWorker" class="input-modern appearance-none cursor-pointer pr-8">
<option value="">(Sin asignar)</option>
</select>
<i data-lucide="chevron-down" class="w-4 h-4 absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 pointer-events-none"></i>
</div>
</div>
</form>
<div>
<label class="label-modern">Duración (Estimada)</label>
<div class="relative">
<select id="nDuration" class="input-modern appearance-none cursor-pointer pr-8">
<option value="15">15 min</option>
<option value="30">30 min</option>
<option value="45">45 min</option>
<option value="60" selected>1 hora</option>
<option value="75">1 h 15 min</option>
<option value="90">1 h 30 min</option>
<option value="105">1 h 45 min</option>
<option value="120">2 horas</option>
<option value="150">2 h 30 min</option>
<option value="180">3 horas</option>
<option value="210">3 h 30 min</option>
<option value="240">4 horas (Max)</option>
</select>
<i data-lucide="clock" class="w-4 h-4 absolute right-3 top-1/2 -translate-y-1/2 text-slate-400 pointer-events-none"></i>
</div>
</div>
</div>
</div>
<div class="space-y-3">
<button type="submit" name="action" value="manual" class="w-full bg-emerald-600 hover:bg-emerald-500 text-white font-black py-4 rounded-xl text-sm uppercase tracking-widest shadow-[0_8px_20px_-6px_rgba(5,150,105,0.5)] transition-all active:scale-95 flex items-center justify-center gap-2">
<i data-lucide="check-circle" class="w-5 h-5"></i> Crear y Guardar
</button>
<div class="relative flex items-center justify-center py-2">
<span class="border-t border-slate-200 w-full absolute"></span>
<span class="bg-slate-50 px-3 text-[9px] font-black text-slate-400 uppercase tracking-widest relative z-10">Opciones Automáticas</span>
</div>
<button type="submit" name="action" value="auto" class="w-full bg-slate-800 hover:bg-slate-700 text-white font-black py-3 rounded-xl text-xs uppercase tracking-widest shadow-md transition-all active:scale-95 flex items-center justify-between px-5 border border-slate-600">
<div class="text-left">
<span class="block">Mandar a la Cola</span>
<span class="block text-[9px] text-slate-400 font-medium normal-case mt-0.5">Busca operario vía WhatsApp</span>
</div>
<i data-lucide="zap" class="w-5 h-5 text-amber-400"></i>
</button>
</div>
</div>
</div>
</form>
</div>
</div>
@@ -322,7 +375,6 @@
</div>
</div>
</div>
</div>
<div id="toastAppt" class="fixed bottom-8 right-8 bg-emerald-600 text-white px-6 py-4 rounded-2xl shadow-2xl hidden z-[200] font-bold text-sm flex items-center gap-3 transition-colors duration-300">
@@ -331,40 +383,6 @@
</div>
<script src="js/layout.js"></script>
// --- FUNCIÓN PARA BUSCAR CLIENTE POR TELÉFONO AL ESCRIBIR ---
async function searchClientByPhone(phone) {
if (phone.length < 9) {
document.getElementById('addrSuggestions').classList.add('hidden');
return;
}
try {
const res = await fetch(`${API_URL}/clients/search?phone=${phone}`, {
headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` }
});
const data = await res.json();
if (data.ok && data.client) {
// Rellenar nombre si está vacío
const nameInput = document.getElementById('nName');
if (!nameInput.value) {
nameInput.value = data.client.full_name;
nameInput.classList.add('bg-emerald-50'); // Efecto visual de éxito
}
// Mostrar aviso de direcciones si existen
const addrDiv = document.getElementById('addrSuggestions');
if (data.client.addresses && data.client.addresses.length > 0) {
addrDiv.classList.remove('hidden');
// Si el campo de dirección está vacío, ponemos la primera que tengamos
if (!document.getElementById('nAddr').value) {
document.getElementById('nAddr').value = data.client.addresses[0];
}
}
}
} catch (e) { console.error("Error en buscador rápido:", e); }
}
<script>
let localData = [];
let systemStatuses = [];
@@ -524,40 +542,58 @@ async function searchClientByPhone(phone) {
const searchTerm = document.getElementById('searchFilter').value.toLowerCase();
const selectedOp = document.getElementById('opFilter').value;
const weekValue = document.getElementById('weekFilter').value;
// Reiniciamos contadores para volver a sumarlos
let kpiUnassigned = 0, kpiScheduled = 0, kpiWaiting = 0, kpiIncident = 0;
// Inicializamos contadores reales
let kpiUnassigned = 0;
let kpiScheduled = 0;
let kpiWaiting = 0;
let kpiIncident = 0;
const filteredData = localData.filter(s => {
const raw = s.raw_data || {};
const stateInfo = getServiceStateInfo(s);
s._stateInfo = stateInfo;
s._stateInfo = stateInfo; // Guardamos info para el render
// LÓGICA DE CONTADORES REVOLUCIONADA:
const stName = stateInfo.name.toLowerCase();
// --- LÓGICA DE SUMA DE CONTADORES ---
// 1. SIN ASIGNAR: No tiene nombre de operario O está en bolsa
if (!s.assigned_name || stateInfo.id === 'bolsa' || stName.includes('asignar')) {
kpiUnassigned++;
} else if (stName.includes('incidencia') || stName.includes('pausa')) {
}
// 2. INCIDENCIA: El estado tiene la palabra clave
else if (stName.includes('incidencia') || stName.includes('pausa')) {
kpiIncident++;
} else if (raw.scheduled_date && raw.scheduled_date !== "" && !stateInfo.is_final) {
}
// 3. AGENDADOS: Tiene fecha puesta y no es un estado final
else if (raw.scheduled_date && raw.scheduled_date !== "" && !stateInfo.is_final) {
kpiScheduled++;
} else if (!stateInfo.is_final && !raw.scheduled_date) {
}
// 4. ESPERA CLIENTE: No tiene cita pero ya tiene técnico (o estado específico)
else if (!stateInfo.is_final && (!raw.scheduled_date || stName.includes('espera'))) {
kpiWaiting++;
}
// Filtro de búsqueda visual
const matchesSearch = searchTerm === "" ||
(raw["Nombre Cliente"] || "").toLowerCase().includes(searchTerm) ||
(s.service_ref || "").toLowerCase().includes(searchTerm) ||
(raw["Teléfono"] || "").toLowerCase().includes(searchTerm);
// Filtros de búsqueda
const name = (raw["Nombre Cliente"] || raw["CLIENTE"] || "").toLowerCase();
const ref = (s.service_ref || "").toLowerCase();
const matchesSearch = searchTerm === "" || name.includes(searchTerm) || ref.includes(searchTerm);
const matchesOp = selectedOp === "ALL" || s.assigned_name === selectedOp;
const matchesStatus = (activeStatusFilter === "ALL") ? true : String(stateInfo.id) === activeStatusFilter;
return matchesSearch && matchesOp && matchesStatus;
let matchesWeek = true;
if (weekValue !== "" && raw.scheduled_date) {
matchesWeek = isDateInWeekString(raw.scheduled_date, weekValue);
} else if (weekValue !== "") {
matchesWeek = false;
}
let matchesStatus = (activeStatusFilter === "ALL") ? true : String(stateInfo.id) === activeStatusFilter;
return matchesSearch && matchesOp && matchesWeek && matchesStatus;
});
// Inyectar los números en los circulitos de arriba
// Actualizamos la vista con los números reales
document.getElementById('kpi-unassigned').innerText = kpiUnassigned;
document.getElementById('kpi-scheduled').innerText = kpiScheduled;
document.getElementById('kpi-waiting').innerText = kpiWaiting;
@@ -566,10 +602,13 @@ async function searchClientByPhone(phone) {
const grid = document.getElementById('servicesGrid');
grid.innerHTML = filteredData.length > 0
? filteredData.map(s => buildGridCard(s)).join('')
: `<div class="col-span-full py-20 text-center text-slate-400 font-bold">No hay servicios con estos filtros</div>`;
: `<div class="col-span-full py-20 text-center bg-white rounded-[2rem] border-2 border-dashed border-slate-200">
<i data-lucide="layout-grid" class="w-12 h-12 text-slate-300 mx-auto mb-3"></i>
<p class="text-slate-400 font-bold uppercase tracking-widest text-sm">No hay servicios que coincidan</p>
</div>`;
lucide.createIcons();
}
}
function buildGridCard(s) {
const raw = s.raw_data || {};
@@ -577,7 +616,7 @@ async function searchClientByPhone(phone) {
const addr = raw["Dirección"] || raw["DOMICILIO"] || "---";
const pop = raw["Población"] || raw["POBLACION-PROVINCIA"] || "";
const fullAddr = `${addr} ${pop}`.trim();
const cita = raw.scheduled_date ? `${raw.scheduled_date.split('-').reverse().slice(0,2).join('/')} | ${raw.scheduled_time}` : 'Pte. Cita';
const cita = raw.scheduled_date ? `${raw.scheduled_date} | ${raw.scheduled_time}` : 'Pendiente Cita';
const companyName = raw['Compañía'] || raw['COMPAÑIA'] || raw['Procedencia'] || (s.provider === 'MANUAL' ? 'PARTICULAR' : 'ASEGURADORA');
const isUrgent = s.is_urgent === true || (raw['Urgente'] && raw['Urgente'].toLowerCase() === 'sí') || (raw['URGENTE'] && raw['URGENTE'].toLowerCase() === 'si');
@@ -617,28 +656,19 @@ async function searchClientByPhone(phone) {
</div>
</div>
<div class="flex items-center justify-between mt-4 pt-4 border-t border-slate-100 gap-2">
<div class="flex items-center gap-1 min-w-0 flex-1">
<div class="bg-slate-100 p-1 rounded-lg text-slate-500 shrink-0"><i data-lucide="hard-hat" class="w-3 h-3"></i></div>
<span class="text-[10px] font-black text-slate-600 uppercase truncate flex-1" style="max-width: 70px;" title="${s.assigned_name || 'Sin asignar'}">${s.assigned_name || 'Sin asignar'}</span>
<div class="flex items-center gap-0.5 shrink-0">
<button onclick="event.stopPropagation(); window.location.href='trazabilidad.html?id=${s.id}'" class="p-1.5 text-slate-300 hover:text-blue-600 transition-colors" title="Ver Historial">
<i data-lucide="history" class="w-4 h-4"></i>
</button>
<button onclick="event.stopPropagation(); openDetail(${s.id}, true)" class="p-1.5 text-slate-300 hover:text-amber-600 transition-colors" title="Editar Ficha">
<i data-lucide="edit-3" class="w-4 h-4"></i>
</button>
</div>
</div>
<div class="flex items-center gap-1 text-blue-600 shrink-0 bg-blue-50 px-2 py-1 rounded-md border border-blue-100">
<i data-lucide="${iconEstado}" class="w-3 h-3"></i>
<span class="text-[9px] font-black uppercase whitespace-nowrap">${raw.scheduled_date ? cita.split('|')[0] : 'Pte. Cita'}</span>
<div class="flex items-center justify-between mt-4 pt-4 border-t border-slate-100">
<div class="flex items-center gap-2 min-w-0">
<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 ? `
<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>
</div>` : ''}
</div>
</div>`;
}
}
function shakeCard(element, status) {
element.classList.add('shake');
@@ -664,29 +694,21 @@ async function searchClientByPhone(phone) {
setTimeout(() => { toast.classList.add('hidden'); }, 3500);
}
function openDetail(id, startInEditMode = false) {
function openDetail(id) {
const s = localData.find(x => x.id === id);
if (!s) return;
const raw = s.raw_data;
// 1. IDs y Referencias
document.getElementById('detId').value = s.id;
document.getElementById('detRef').innerText = s.service_ref;
document.getElementById('detCp').value = raw["Código Postal"] || "00000";
const companyName = raw['Compañía'] || raw['COMPAÑIA'] || raw['Procedencia'] || "Particular";
document.getElementById('detCompany').innerText = companyName;
// 2. RELLENAR CAMPOS EDITABLES (NUEVO)
// Rellenamos tanto los visuales como los que usaremos para editar
const clientName = raw["Nombre Cliente"] || raw["CLIENTE"] || "Asegurado Sin Nombre";
document.getElementById('detName').innerText = clientName;
if(document.getElementById('editName')) document.getElementById('editName').value = clientName;
document.getElementById('detName').innerText = raw["Nombre Cliente"] || raw["CLIENTE"] || "Asegurado Sin Nombre";
const rawPhone = raw["Teléfono"] || raw["TELEFONOS"] || raw["TELEFONO"] || "";
const matchPhone = rawPhone.toString().match(/[6789]\d{8}/);
const singlePhone = matchPhone ? matchPhone[0] : "";
if(document.getElementById('editPhone')) document.getElementById('editPhone').value = singlePhone;
if (singlePhone) {
document.getElementById('detPhone').innerText = singlePhone;
@@ -700,16 +722,11 @@ async function searchClientByPhone(phone) {
document.getElementById('detPhoneLink').classList.add('text-slate-400', 'pointer-events-none');
}
const fullAddr = `${raw["Dirección"] || ""} ${raw["Población"] || ""}`.trim();
document.getElementById('detAddrText').innerText = fullAddr || "Dirección no especificada";
if(document.getElementById('editAddr')) document.getElementById('editAddr').value = raw["Dirección"] || "";
document.getElementById('detAddrText').innerText = `${raw["Dirección"] || "Dirección no especificada"} ${raw["Población"] || ""}`;
document.getElementById('detDesc').innerHTML = (raw["Descripción"] || raw["DESCRIPCION"] || "Sin notas.").replace(/\n/g, '<br>');
const descContent = raw["Descripción"] || raw["DESCRIPCION"] || "Sin notas.";
document.getElementById('detDesc').innerHTML = descContent.replace(/\n/g, '<br>');
if(document.getElementById('editDesc')) document.getElementById('editDesc').value = descContent;
// 3. Lógica de Paneles (Asignación/Bolsa)
const stateInfo = s._stateInfo;
if (s.automation_status === 'in_progress') {
document.getElementById('panelEnBolsa').classList.remove('hidden');
document.getElementById('panelAsignado').classList.add('hidden');
@@ -739,17 +756,9 @@ async function searchClientByPhone(phone) {
}
}
// 4. ACTIVAR MODO EDICIÓN SI SE PULSÓ EL LÁPIZ (NUEVO)
document.getElementById('detailModal').classList.remove('hidden');
if (startInEditMode) {
enableEditing();
} else {
cancelEditing(); // Resetear a modo lectura
}
lucide.createIcons();
}
}
async function stopAutomation() {
const id = document.getElementById('detId').value;
@@ -863,8 +872,6 @@ async function searchClientByPhone(phone) {
async function saveNewService(e) {
e.preventDefault();
const action = e.submitter.value;
const is_company = document.getElementById('isCompanyCheck').checked;
const data = {
phone: document.getElementById('nPhone').value,
name: document.getElementById('nName').value,
@@ -872,79 +879,20 @@ async function searchClientByPhone(phone) {
description: document.getElementById('nDesc').value,
guild_id: document.getElementById('nGuild').value,
assigned_to: document.getElementById('nWorker').value || null,
duration_minutes: document.getElementById('nDuration').value,
is_company: is_company,
company_name: is_company ? document.getElementById('nCompanySelect').value : 'Particular',
company_ref: is_company ? document.getElementById('nExpRef').value : null,
mode: action
duration_minutes: document.getElementById('nDuration').value // <--- CAPTURA DE DURACIÓN
};
try {
const res = await fetch(`${API_URL}/services/manual-high`, {
method: 'POST',
headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` },
body: JSON.stringify(data)
method: 'POST', headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` },
body: JSON.stringify({ ...data, mode: action })
});
if (res.ok) {
showToast("✅ Expediente creado con éxito");
closeCreateModal();
refreshPanel();
} else {
const err = await res.json();
alert("Error: " + (err.error || "No se pudo guardar"));
if (res.ok) { closeCreateModal(); refreshPanel(); }
} catch(e) { alert("Error al guardar"); }
}
} catch(e) { alert("Error de conexión"); }
}
function closeDetailModal() { document.getElementById('detailModal').classList.add('hidden'); }
// Al abrir el modal, cargamos las compañías configuradas
async function openCreateModal() {
document.getElementById('createModal').classList.remove('hidden');
// Limpiamos campos
document.getElementById('isCompanyCheck').checked = false;
toggleCompanyFields(false);
// Cargar compañías desde tu ruta de configuración
try {
const res = await fetch(`${API_URL}/companies`, {
headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` }
});
const data = await res.json();
const sel = document.getElementById('nCompanySelect');
sel.innerHTML = '<option value="">Seleccionar compañía...</option>';
data.companies.forEach(c => {
sel.innerHTML += `<option value="${c.name}">${c.name.toUpperCase()}</option>`;
});
} catch(e) {}
lucide.createIcons();
}
function toggleCompanyFields(show) {
const fields = document.getElementById('companyFields');
fields.classList.toggle('hidden', !show);
if(!show) {
document.getElementById('nExpRef').value = "";
document.getElementById('refAlert').classList.add('hidden');
}
}
async function checkDuplicateRef(ref) {
if(!ref) return;
const res = await fetch(`${API_URL}/services/check-ref?ref=${ref}`, {
headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` }
});
const data = await res.json();
const alertEl = document.getElementById('refAlert');
const inputEl = document.getElementById('nExpRef');
if(data.exists) {
alertEl.classList.remove('hidden');
inputEl.classList.add('border-red-500', 'bg-red-50');
} else {
alertEl.classList.add('hidden');
inputEl.classList.remove('border-red-500', 'bg-red-50');
}
} function closeCreateModal() { document.getElementById('createModal').classList.add('hidden'); }
function openCreateModal() { document.getElementById('createModal').classList.remove('hidden'); }
function closeCreateModal() { document.getElementById('createModal').classList.add('hidden'); }
async function loadGuilds() {
const res = await fetch(`${API_URL}/guilds`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } });
@@ -1018,39 +966,6 @@ async function checkDuplicateRef(ref) {
lucide.createIcons();
}
}
// --- FUNCIÓN PARA BUSCAR CLIENTE POR TELÉFONO AL ESCRIBIR ---
async function searchClientByPhone(phone) {
if (phone.length < 9) {
document.getElementById('addrSuggestions').classList.add('hidden');
return;
}
try {
const res = await fetch(`${API_URL}/clients/search?phone=${phone}`, {
headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` }
});
const data = await res.json();
if (data.ok && data.client) {
// Rellenar nombre si está vacío
const nameInput = document.getElementById('nName');
if (!nameInput.value) {
nameInput.value = data.client.full_name;
nameInput.classList.add('bg-emerald-50'); // Efecto visual de éxito
}
// Mostrar aviso de direcciones si existen
const addrDiv = document.getElementById('addrSuggestions');
if (data.client.addresses && data.client.addresses.length > 0) {
addrDiv.classList.remove('hidden');
// Si el campo de dirección está vacío, ponemos la primera que tengamos
if (!document.getElementById('nAddr').value) {
document.getElementById('nAddr').value = data.client.addresses[0];
}
}
}
} catch (e) { console.error("Error en buscador rápido:", e); }
}
</script>
</body>
</html>