From 87baa621db8f5ed0fce31408c2f569467fc2593c Mon Sep 17 00:00:00 2001 From: marsalva Date: Sun, 8 Feb 2026 14:53:31 +0000 Subject: [PATCH] Actualizar servicios.html --- servicios.html | 318 ++++++++++++++++++++++++++++--------------------- 1 file changed, 182 insertions(+), 136 deletions(-) diff --git a/servicios.html b/servicios.html index 6323ff4..390905b 100644 --- a/servicios.html +++ b/servicios.html @@ -43,8 +43,8 @@ Fecha - Cliente / Dirección - Detalle + Cliente + Asignado Estado @@ -105,9 +105,24 @@

- Detalles del Trabajo + Asignación y Detalles

+
+
+ + +
+
+ + +
+
+
@@ -125,15 +140,9 @@
@@ -204,9 +213,9 @@
-
@@ -254,6 +263,7 @@ fetchStatuses(); fetchServices(); loadCompanies(); + loadGuilds(); }); function initAutocomplete() { @@ -278,7 +288,7 @@ document.querySelector('form').reset(); document.getElementById('formTitle').innerHTML = ' Alta de Nuevo Servicio'; document.getElementById('sDate').valueAsDate = new Date(); - document.getElementById('sCreateStatus').disabled = false; // Reactivar al crear + document.getElementById('sCreateStatus').disabled = false; lucide.createIcons(); } document.getElementById('createServiceView').classList.remove('hidden'); @@ -286,7 +296,7 @@ } } - // --- CARGA DE DATOS --- + // --- CARGA DE DATOS (Gremios, Operarios, etc) --- async function fetchStatuses() { try { const res = await fetch(`${API_URL}/statuses`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); @@ -303,13 +313,44 @@ } catch(e) {} } - // MODIFICADO: Devuelve promesa y no borra si ya existen + async function loadGuilds() { + try { + const res = await fetch(`${API_URL}/guilds`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); + const data = await res.json(); + if(data.ok) { + const sel = document.getElementById('sGuild'); + sel.innerHTML = ''; + data.guilds.forEach(g => sel.innerHTML += ``); + } + } catch(e) {} + } + + async function loadOperators(guildId) { + const sel = document.getElementById('sOperator'); + sel.innerHTML = ''; + sel.disabled = true; + + if(!guildId) { + sel.innerHTML = ''; + return; + } + + try { + const res = await fetch(`${API_URL}/operators?guild_id=${guildId}`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); + const data = await res.json(); + if(data.ok) { + sel.innerHTML = ''; + if(data.operators.length === 0) sel.innerHTML += ''; + data.operators.forEach(op => sel.innerHTML += ``); + } + } catch(e) { sel.innerHTML = ''; } + finally { sel.disabled = false; } + } + 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) { @@ -332,130 +373,12 @@ } 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'); - } + if(isChecked) { div.classList.remove('hidden'); loadCompanies(); } else { div.classList.add('hidden'); } } - async function editService() { - if(!currentServiceId) return; - try { - const res = await fetch(`${API_URL}/services/${currentServiceId}`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); - const json = await res.json(); - if(json.ok) { - await fillEditForm(json.service); // AWAIT IMPORTANTE - closeDetailPanel(); - document.getElementById('createServiceView').classList.remove('hidden'); - document.getElementById('servicesListView').classList.add('hidden'); - } - } catch(e) { showToast("Error cargando datos", true); } - } - - // 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; - - document.getElementById('sPhone').value = s.contact_phone; - document.getElementById('sName').value = s.contact_name; - document.getElementById('sAddress').value = s.address; - document.getElementById('sEmail').value = s.email || ''; - document.getElementById('sDesc').value = s.description || ''; - - 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; - - document.getElementById('sDuration').value = s.duration_minutes || 30; - document.getElementById('sUrgent').checked = s.is_urgent; - - document.getElementById('sIsCompany').checked = s.is_company; - - // 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 || ''; - - document.getElementById('sCreateStatus').disabled = true; // No editar estado aquí - lucide.createIcons(); - } - - async function handleFormSubmit(e) { - e.preventDefault(); - const btn = document.getElementById('btnSave'); - btn.disabled = true; btn.innerText = "Procesando..."; - - const editId = document.getElementById('editServiceId').value; - const isEdit = !!editId; - - const data = { - phone: document.getElementById('sPhone').value, - name: document.getElementById('sName').value, - address: document.getElementById('sAddress').value, - email: document.getElementById('sEmail').value, - description: document.getElementById('sDesc').value, - scheduled_date: document.getElementById('sDate').value, - scheduled_time: document.getElementById('sTime').value, - duration: document.getElementById('sDuration').value, - is_urgent: document.getElementById('sUrgent').checked, - is_company: document.getElementById('sIsCompany').checked, - company_id: document.getElementById('sCompanyId').value || null, - company_ref: document.getElementById('sCompanyRef').value, - internal_notes: document.getElementById('sNotesInternal').value, - client_notes: document.getElementById('sNotesClient').value - }; - - if (!isEdit) { - data.status_id = document.getElementById('sCreateStatus').value; - } - - const url = isEdit ? `${API_URL}/services/${editId}` : `${API_URL}/services`; - const method = isEdit ? 'PUT' : 'POST'; - - try { - const res = await fetch(url, { - method: method, - headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }, - body: JSON.stringify(data) - }); - const json = await res.json(); - - if (json.ok) { - showToast(isEdit ? "✅ Actualizado" : "✅ Creado"); - toggleView('list'); - } else { - showToast("❌ " + (json.error || "Error"), true); - } - } catch (e) { showToast("Error conexión", true); } - finally { btn.disabled = false; btn.innerText = "GUARDAR"; } - } - - async function deleteService() { - if(!currentServiceId || !confirm("¿Borrar servicio permanentemente?")) return; - try { - const res = await fetch(`${API_URL}/services/${currentServiceId}`, { - method: 'DELETE', - headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } - }); - 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; @@ -498,12 +421,15 @@ const color = s.status_color || 'gray'; const date = new Date(s.created_at); const formattedDate = date.toLocaleDateString('es-ES', { day: '2-digit', month: 'short' }); + // Mostramos operario si existe + const assigned = s.assigned_name ? `${s.assigned_name}` : 'Sin asignar'; + tbody.innerHTML += ` ${formattedDate}

${s.contact_name}

${s.address}

-

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

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

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

${s.is_urgent ? 'Urgente' : ''}${s.is_company ? `${s.company_name || 'Compañía'}` : ''} ${s.status_name || 'Nuevo'} @@ -565,6 +491,126 @@ } catch(e) { showToast("Error", true); } } + // --- EDICIÓN Y BORRADO --- + async function editService() { + if(!currentServiceId) return; + try { + const res = await fetch(`${API_URL}/services/${currentServiceId}`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } }); + const json = await res.json(); + if(json.ok) { + await fillEditForm(json.service); + closeDetailPanel(); + document.getElementById('createServiceView').classList.remove('hidden'); + document.getElementById('servicesListView').classList.add('hidden'); + } + } catch(e) { showToast("Error cargando datos", true); } + } + + async function fillEditForm(s) { + document.getElementById('editServiceId').value = s.id; + document.getElementById('formTitle').innerHTML = ' Editando Servicio #' + s.id; + + document.getElementById('sPhone').value = s.contact_phone; + document.getElementById('sName').value = s.contact_name; + document.getElementById('sAddress').value = s.address; + document.getElementById('sEmail').value = s.email || ''; + document.getElementById('sDesc').value = s.description || ''; + + 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; + document.getElementById('sDuration').value = s.duration_minutes || 30; + document.getElementById('sUrgent').checked = s.is_urgent; + + // COMPAÑÍA + document.getElementById('sIsCompany').checked = s.is_company; + if(s.is_company) { + document.getElementById('companyFields').classList.remove('hidden'); + await loadCompanies(); + document.getElementById('sCompanyId').value = s.company_id || ''; + document.getElementById('sCompanyRef').value = s.company_ref || ''; + } else { + document.getElementById('companyFields').classList.add('hidden'); + } + + // GREMIO Y OPERARIO (NUEVO) + document.getElementById('sGuild').value = s.guild_id || ''; + if(s.guild_id) { + await loadOperators(s.guild_id); + document.getElementById('sOperator').value = s.assigned_to || ''; + } else { + loadOperators(''); // Limpiar + } + + document.getElementById('sNotesInternal').value = s.internal_notes || ''; + document.getElementById('sNotesClient').value = s.client_notes || ''; + + document.getElementById('sCreateStatus').disabled = true; + lucide.createIcons(); + } + + async function handleFormSubmit(e) { + e.preventDefault(); + const btn = document.getElementById('btnSave'); + btn.disabled = true; btn.innerText = "Procesando..."; + + const editId = document.getElementById('editServiceId').value; + const isEdit = !!editId; + + const data = { + phone: document.getElementById('sPhone').value, + name: document.getElementById('sName').value, + address: document.getElementById('sAddress').value, + email: document.getElementById('sEmail').value, + description: document.getElementById('sDesc').value, + scheduled_date: document.getElementById('sDate').value, + scheduled_time: document.getElementById('sTime').value, + duration: document.getElementById('sDuration').value, + is_urgent: document.getElementById('sUrgent').checked, + is_company: document.getElementById('sIsCompany').checked, + company_id: document.getElementById('sCompanyId').value || null, + company_ref: document.getElementById('sCompanyRef').value, + internal_notes: document.getElementById('sNotesInternal').value, + client_notes: document.getElementById('sNotesClient').value, + guild_id: document.getElementById('sGuild').value || null, + assigned_to: document.getElementById('sOperator').value || null + }; + + if (!isEdit) { + data.status_id = document.getElementById('sCreateStatus').value; + } + + const url = isEdit ? `${API_URL}/services/${editId}` : `${API_URL}/services`; + const method = isEdit ? 'PUT' : 'POST'; + + try { + const res = await fetch(url, { + method: method, + headers: { "Content-Type": "application/json", "Authorization": `Bearer ${localStorage.getItem("token")}` }, + body: JSON.stringify(data) + }); + const json = await res.json(); + + if (json.ok) { + showToast(isEdit ? "✅ Actualizado" : "✅ Creado"); + toggleView('list'); + } else { + showToast("❌ " + (json.error || "Error"), true); + } + } catch (e) { showToast("Error conexión", true); } + finally { btn.disabled = false; btn.innerText = "GUARDAR"; } + } + + async function deleteService() { + if(!currentServiceId || !confirm("¿Borrar servicio permanentemente?")) return; + try { + const res = await fetch(`${API_URL}/services/${currentServiceId}`, { + method: 'DELETE', + headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } + }); + if(res.ok) { showToast("Eliminado"); closeDetailPanel(); fetchServices(); } + } catch(e) { showToast("Error", true); } + } + function closeDetailPanel() { document.getElementById('serviceDetailPanel').classList.add('hidden'); } function showToast(msg, isError = false) { const t = document.getElementById('toast'), m = document.getElementById('toastMsg');