diff --git a/servicios.html b/servicios.html index c0cb1a4..6323ff4 100644 --- a/servicios.html +++ b/servicios.html @@ -66,7 +66,9 @@
-
+ + +

Datos del Cliente

@@ -245,7 +247,6 @@ let allStatuses = []; let currentServiceId = null; let autocomplete; - let currentServiceData = null; // Guardamos datos del servicio actual para edición document.addEventListener("DOMContentLoaded", () => { const token = localStorage.getItem("token"); @@ -272,12 +273,12 @@ document.getElementById('servicesListView').classList.remove('hidden'); fetchServices(); } else { - // RESETEAR FORMULARIO SI ES NUEVO if(document.getElementById('editServiceId').value !== "") { - document.getElementById('editServiceId').value = ""; // Limpiar ID + document.getElementById('editServiceId').value = ""; document.querySelector('form').reset(); document.getElementById('formTitle').innerHTML = ' Alta de Nuevo Servicio'; - document.getElementById('sDate').valueAsDate = new Date(); // Reset fecha + document.getElementById('sDate').valueAsDate = new Date(); + document.getElementById('sCreateStatus').disabled = false; // Reactivar al crear lucide.createIcons(); } document.getElementById('createServiceView').classList.remove('hidden'); @@ -285,16 +286,71 @@ } } - // --- FUNCIONES EDICIÓN Y BORRADO (NUEVAS) --- + // --- CARGA DE DATOS --- + async function fetchStatuses() { + try { + const res = await fetch(`${API_URL}/statuses`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); + const data = await res.json(); + if(data.ok) { + allStatuses = data.statuses; + const createSel = document.getElementById('sCreateStatus'); + createSel.innerHTML = ''; + allStatuses.forEach(s => { + const isDef = s.is_default ? 'selected' : ''; + createSel.innerHTML += ``; + }); + } + } catch(e) {} + } + + // MODIFICADO: Devuelve promesa y no borra si ya existen + async function loadCompanies() { + try { + const sel = document.getElementById('sCompanyId'); + // Si ya tiene datos (>1 porque la primera es "--Seleccionar--"), no recargamos + if (sel.options.length > 1) return; + + const res = await fetch(`${API_URL}/companies`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); + const data = await res.json(); + if (data.ok) { + sel.innerHTML = ''; + data.companies.forEach(c => sel.innerHTML += ``); + } + } catch (e) {} + } + + async function quickAddCompany() { + const name = prompt("Nombre de la nueva compañía:"); + if(!name) return; + try { + const res = await fetch(`${API_URL}/companies`, { + method: 'POST', + headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }, + body: JSON.stringify({ name }) + }); + if(res.ok) { showToast("Compañía añadida"); document.getElementById('sCompanyId').innerHTML = ""; await loadCompanies(); } + } catch(e) { alert("Error"); } + } + + // --- EDICIÓN Y LOGICA DE FORMULARIO --- + function toggleCompanyFields() { + const isChecked = document.getElementById('sIsCompany').checked; + const div = document.getElementById('companyFields'); + if(isChecked) { + div.classList.remove('hidden'); + loadCompanies(); + } else { + div.classList.add('hidden'); + } + } + async function editService() { if(!currentServiceId) return; - - // Cargar datos completos si no los tenemos try { const res = await fetch(`${API_URL}/services/${currentServiceId}`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); const json = await res.json(); if(json.ok) { - fillEditForm(json.service); + await fillEditForm(json.service); // AWAIT IMPORTANTE closeDetailPanel(); document.getElementById('createServiceView').classList.remove('hidden'); document.getElementById('servicesListView').classList.add('hidden'); @@ -302,7 +358,8 @@ } catch(e) { showToast("Error cargando datos", true); } } - function fillEditForm(s) { + // MODIFICADO: AWAIT para esperar carga de compañías antes de asignar valor + async function fillEditForm(s) { document.getElementById('editServiceId').value = s.id; document.getElementById('formTitle').innerHTML = ' Editando Servicio #' + s.id; @@ -312,7 +369,6 @@ document.getElementById('sEmail').value = s.email || ''; document.getElementById('sDesc').value = s.description || ''; - // Fechas (Cortar la parte de tiempo ISO) if(s.scheduled_date) document.getElementById('sDate').value = s.scheduled_date.split('T')[0]; if(s.scheduled_time) document.getElementById('sTime').value = s.scheduled_time; @@ -320,37 +376,24 @@ document.getElementById('sUrgent').checked = s.is_urgent; document.getElementById('sIsCompany').checked = s.is_company; - toggleCompanyFields(); // Mostrar/ocultar panel + + // LÓGICA CRÍTICA PARA COMPAÑÍA if(s.is_company) { + document.getElementById('companyFields').classList.remove('hidden'); + await loadCompanies(); // Esperamos a que el select se llene document.getElementById('sCompanyId').value = s.company_id || ''; document.getElementById('sCompanyRef').value = s.company_ref || ''; + } else { + document.getElementById('companyFields').classList.add('hidden'); } document.getElementById('sNotesInternal').value = s.internal_notes || ''; document.getElementById('sNotesClient').value = s.client_notes || ''; - // El estado inicial no se edita aquí, se hace desde el panel de estados - document.getElementById('sCreateStatus').disabled = true; - + document.getElementById('sCreateStatus').disabled = true; // No editar estado aquí lucide.createIcons(); } - async function deleteService() { - if(!currentServiceId || !confirm("¿Estás seguro de que quieres borrar este servicio permanentemente?")) return; - try { - const res = await fetch(`${API_URL}/services/${currentServiceId}`, { - method: 'DELETE', - headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } - }); - if(res.ok) { - showToast("Servicio eliminado"); - closeDetailPanel(); - fetchServices(); - } else { showToast("Error al borrar", true); } - } catch(e) { showToast("Error conexión", true); } - } - - // --- SUBMIT UNIFICADO (CREAR / EDITAR) --- async function handleFormSubmit(e) { e.preventDefault(); const btn = document.getElementById('btnSave'); @@ -377,7 +420,7 @@ }; if (!isEdit) { - data.status_id = document.getElementById('sCreateStatus').value; // Solo al crear + data.status_id = document.getElementById('sCreateStatus').value; } const url = isEdit ? `${API_URL}/services/${editId}` : `${API_URL}/services`; @@ -392,66 +435,27 @@ const json = await res.json(); if (json.ok) { - showToast(isEdit ? "✅ Servicio Actualizado" : "✅ Servicio Creado"); + showToast(isEdit ? "✅ Actualizado" : "✅ Creado"); toggleView('list'); } else { showToast("❌ " + (json.error || "Error"), true); } - } catch (e) { - showToast("Error de conexión", true); - } finally { - btn.disabled = false; btn.innerText = "GUARDAR"; - } + } catch (e) { showToast("Error conexión", true); } + finally { btn.disabled = false; btn.innerText = "GUARDAR"; } } - // --- RESTO DE FUNCIONES (MAESTROS, LISTAR, ETC) --- - async function fetchStatuses() { + async function deleteService() { + if(!currentServiceId || !confirm("¿Borrar servicio permanentemente?")) return; try { - const res = await fetch(`${API_URL}/statuses`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); - const data = await res.json(); - if(data.ok) { - allStatuses = data.statuses; - const createSel = document.getElementById('sCreateStatus'); - createSel.innerHTML = ''; - allStatuses.forEach(s => { - const isDef = s.is_default ? 'selected' : ''; - createSel.innerHTML += ``; - }); - } - } catch(e) {} - } - - async function loadCompanies() { - try { - const res = await fetch(`${API_URL}/companies`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); - const data = await res.json(); - if (data.ok) { - const sel = document.getElementById('sCompanyId'); - sel.innerHTML = ''; - data.companies.forEach(c => sel.innerHTML += ``); - } - } catch (e) {} - } - - async function quickAddCompany() { - const name = prompt("Nombre de la nueva compañía:"); - if(!name) return; - try { - const res = await fetch(`${API_URL}/companies`, { - method: 'POST', - headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }, - body: JSON.stringify({ name }) + const res = await fetch(`${API_URL}/services/${currentServiceId}`, { + method: 'DELETE', + headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); - if(res.ok) { showToast("Compañía añadida"); loadCompanies(); } - } catch(e) { alert("Error"); } - } - - function toggleCompanyFields() { - const isChecked = document.getElementById('sIsCompany').checked; - const div = document.getElementById('companyFields'); - if(isChecked) { div.classList.remove('hidden'); loadCompanies(); } else { div.classList.add('hidden'); } + if(res.ok) { showToast("Eliminado"); closeDetailPanel(); fetchServices(); } + } catch(e) { showToast("Error", true); } } + // --- BÚSQUEDA CLIENTE --- async function searchClientByPhone() { const phone = document.getElementById('sPhone').value; if(phone.length < 8) return; @@ -475,72 +479,53 @@ document.getElementById('clientFoundMsg').classList.add('hidden'); document.getElementById('sAddressSelect').classList.add('hidden'); } - } catch (e) { console.error(e); } + } catch (e) {} } - function selectAddress(val) { if(val) document.getElementById('sAddress').value = val; } - // --- LISTAR Y DETALLES --- + // --- LISTAR SERVICIOS --- async function fetchServices() { try { const res = await fetch(`${API_URL}/services`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); const data = await res.json(); const tbody = document.getElementById('servicesTableBody'); tbody.innerHTML = ""; - if(!data.ok || data.services.length === 0) { - tbody.innerHTML = `No hay servicios registrados. `; + tbody.innerHTML = `No hay servicios. `; return; } - data.services.forEach(s => { const color = s.status_color || 'gray'; const date = new Date(s.created_at); const formattedDate = date.toLocaleDateString('es-ES', { day: '2-digit', month: 'short' }); - tbody.innerHTML += ` ${formattedDate} - -

${s.contact_name}

-

${s.address}

- - -

${s.description || 'Sin detalles'}

- ${s.is_urgent ? 'Urgente' : ''} - ${s.is_company ? `${s.company_name || 'Compañía'}` : ''} - - - - ${s.status_name || 'Nuevo'} - - +

${s.contact_name}

${s.address}

+

${s.description || 'Sin detalles'}

${s.is_urgent ? 'Urgente' : ''}${s.is_company ? `${s.company_name || 'Compañía'}` : ''} + ${s.status_name || 'Nuevo'} `; }); lucide.createIcons(); - } catch (e) { console.error(e); } + } catch (e) {} } async function openDetail(id, client, title, statusName, statusColor, date) { currentServiceId = id; document.getElementById('serviceDetailPanel').classList.remove('hidden'); - document.getElementById('detailTitle').innerText = title || 'Servicio General'; document.getElementById('detailClient').innerText = client; document.getElementById('detailId').innerText = `#${id}`; document.getElementById('detailDate').innerText = date; - const badge = document.getElementById('detailStatusBadge'); badge.innerText = statusName; badge.className = `px-2 py-1 rounded text-[10px] font-bold text-white uppercase tracking-wide bg-${statusColor}-500`; - const sel = document.getElementById('newStatusSelect'); sel.innerHTML = ""; allStatuses.forEach(s => sel.innerHTML += ``); - loadTimeline(id); } @@ -550,15 +535,15 @@ const res = await fetch(`${API_URL}/services/${id}/logs`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); const data = await res.json(); timeline.innerHTML = ""; - if(data.logs.length === 0) { timeline.innerHTML = '

Sin historial registrado.

'; return; } + if(data.logs.length === 0) { timeline.innerHTML = '

Sin historial.

'; return; } data.logs.forEach(log => { const color = log.new_color || 'gray'; const date = new Date(log.created_at); timeline.innerHTML += `
-
+

${log.new_status}

-

${log.comment || 'Cambio de estado'}

Usuario: ${log.user_name || 'Sistema'}

+

${log.comment || 'Cambio de estado'}

Usuario: ${log.user_name || 'Sistema'}

`; }); @@ -567,7 +552,7 @@ async function updateStatus() { const statusId = document.getElementById('newStatusSelect').value; if(!statusId) return; - const comment = prompt("Añade un comentario sobre este cambio (opcional):"); + const comment = prompt("Comentario (opcional):"); try { await fetch(`${API_URL}/services/${currentServiceId}/status`, { method: 'PUT', @@ -577,7 +562,7 @@ showToast("Estado actualizado"); loadTimeline(currentServiceId); fetchServices(); - } catch(e) { showToast("Error al actualizar", true); } + } catch(e) { showToast("Error", true); } } function closeDetailPanel() { document.getElementById('serviceDetailPanel').classList.add('hidden'); }