diff --git a/servicios.html b/servicios.html index 22f7c13..8e5196e 100644 --- a/servicios.html +++ b/servicios.html @@ -27,6 +27,10 @@ /* Estilos base para formularios mejorados */ .input-modern { @apply w-full bg-slate-50 border border-slate-200 px-4 py-3 rounded-xl text-sm font-semibold text-slate-700 outline-none transition-all focus:border-blue-500 focus:bg-white focus:ring-2 focus:ring-blue-100; } .label-modern { @apply block text-[10px] font-black text-slate-500 uppercase tracking-widest mb-1.5 ml-1; } + + /* Estilos para el Modo Edición del Modal */ + .input-editable { border-bottom: 2px solid #3b82f6 !important; background: #eff6ff !important; border-radius: 0.5rem !important; padding: 4px 8px !important; pointer-events: auto !important; } + .input-readonly { border: none !important; background: transparent !important; pointer-events: none; outline: none; } @@ -132,7 +136,6 @@
-

Datos del Cliente @@ -141,7 +144,7 @@
- +
@@ -151,6 +154,18 @@
+ +
+
+ +
@@ -159,16 +174,14 @@ Detalles de la Avería

- +
-
-

- Asignación + Asignación y Tiempo

@@ -181,7 +194,7 @@
-
+
@@ -192,20 +205,16 @@
- +
- - - - + - - @@ -232,7 +241,6 @@
-
@@ -242,7 +250,7 @@ +
+ +
+
+ +
+
@@ -557,29 +574,23 @@ const filteredData = localData.filter(s => { const raw = s.raw_data || {}; const stateInfo = getServiceStateInfo(s); - s._stateInfo = stateInfo; // Guardamos info para el render + s._stateInfo = stateInfo; - // LÓGICA DE CONTADORES REVOLUCIONADA: const stName = stateInfo.name.toLowerCase(); - // 1. SIN ASIGNAR: No tiene nombre de operario O está en bolsa if (!s.assigned_name || stateInfo.id === 'bolsa' || stName.includes('asignar')) { kpiUnassigned++; } - // 2. INCIDENCIA: El estado tiene la palabra clave else if (stName.includes('incidencia') || stName.includes('pausa')) { kpiIncident++; } - // 3. AGENDADOS: Tiene fecha puesta y no es un estado final else if (raw.scheduled_date && raw.scheduled_date !== "" && !stateInfo.is_final) { kpiScheduled++; } - // 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'))) { + else if (!stateInfo.is_final && !raw.scheduled_date) { kpiWaiting++; } - // 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); @@ -597,7 +608,6 @@ return matchesSearch && matchesOp && matchesWeek && matchesStatus; }); - // 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; @@ -620,7 +630,7 @@ 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} | ${raw.scheduled_time}` : 'Pendiente Cita'; + const cita = raw.scheduled_date ? `${raw.scheduled_date.split('-').reverse().slice(0,2).join('/')} | ${raw.scheduled_time}` : 'Pte. 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'); @@ -660,16 +670,25 @@
-
-
-
- ${s.assigned_name || 'Sin asignar'} +
+
+
+ ${s.assigned_name || 'Sin asignar'} + +
+ + +
+
+ +
+ + ${raw.scheduled_date ? cita.split('|')[0] : 'Pte. Cita'}
- ${raw.scheduled_date ? ` -
- - ${cita} -
` : ''}
`; } @@ -698,7 +717,7 @@ setTimeout(() => { toast.classList.add('hidden'); }, 3500); } - function openDetail(id) { + function openDetail(id, startInEditMode = false) { const s = localData.find(x => x.id === id); if (!s) return; const raw = s.raw_data; @@ -708,26 +727,21 @@ const companyName = raw['Compañía'] || raw['COMPAÑIA'] || raw['Procedencia'] || "Particular"; document.getElementById('detCompany').innerText = companyName; - document.getElementById('detName').innerText = raw["Nombre Cliente"] || raw["CLIENTE"] || "Asegurado Sin Nombre"; + + // RELLENADO DE CAMPOS PARA EL EDITOR Y VISTA + const clientName = raw["Nombre Cliente"] || raw["CLIENTE"] || "Asegurado Sin Nombre"; + if(document.getElementById('editName')) document.getElementById('editName').value = clientName; 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; - document.getElementById('detPhoneLink').href = `tel:+34${singlePhone}`; - document.getElementById('detPhoneLink').classList.remove('text-slate-400', 'pointer-events-none'); - document.getElementById('detPhoneLink').classList.add('text-blue-600'); - } else { - document.getElementById('detPhone').innerText = "Sin Teléfono"; - document.getElementById('detPhoneLink').href = "#"; - document.getElementById('detPhoneLink').classList.remove('text-blue-600'); - document.getElementById('detPhoneLink').classList.add('text-slate-400', 'pointer-events-none'); - } + const fullAddr = `${raw["Dirección"] || ""} ${raw["Población"] || ""}`.trim(); + if(document.getElementById('editAddr')) document.getElementById('editAddr').value = fullAddr; - 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, '
'); + const descContent = raw["Descripción"] || raw["DESCRIPCION"] || "Sin notas."; + if(document.getElementById('editDesc')) document.getElementById('editDesc').value = descContent; const stateInfo = s._stateInfo; @@ -761,6 +775,13 @@ } document.getElementById('detailModal').classList.remove('hidden'); + + if (startInEditMode) { + enableEditing(); + } else { + cancelEditing(); + } + lucide.createIcons(); } @@ -876,6 +897,8 @@ 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, @@ -883,19 +906,70 @@ description: document.getElementById('nDesc').value, guild_id: document.getElementById('nGuild').value, assigned_to: document.getElementById('nWorker').value || null, - duration_minutes: document.getElementById('nDuration').value // <--- CAPTURA DE DURACIÓN + 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 }; + 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, mode: action }) + body: JSON.stringify(data) }); - if (res.ok) { closeCreateModal(); refreshPanel(); } + if (res.ok) { closeCreateModal(); refreshPanel(); showToast("✅ Servicio guardado correctamente"); } } catch(e) { alert("Error al guardar"); } } function closeDetailModal() { document.getElementById('detailModal').classList.add('hidden'); } - function openCreateModal() { document.getElementById('createModal').classList.remove('hidden'); } + + async function openCreateModal() { + document.getElementById('createModal').classList.remove('hidden'); + document.getElementById('isCompanyCheck').checked = false; + toggleCompanyFields(false); + + 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 = ''; + data.companies.forEach(c => { + sel.innerHTML += ``; + }); + } 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'); } async function loadGuilds() { @@ -917,9 +991,9 @@ } async function copyClientPortalLink() { - const phone = document.getElementById('detPhone').innerText; - const name = document.getElementById('detName').innerText; - const addr = document.getElementById('detAddrText').innerText; + const phone = document.getElementById('editPhone').value; + const name = document.getElementById('editName').value; + const addr = document.getElementById('editAddr').value; if (!phone || phone === "Sin Teléfono") { showToast("No hay un teléfono válido para este cliente.", "warning"); @@ -955,7 +1029,7 @@ setTimeout(() => { btn.innerHTML = originalHtml; - btn.className = "text-[10px] font-black bg-blue-50 border border-blue-100 hover:bg-blue-600 hover:text-white text-blue-600 px-3 py-1.5 rounded-lg flex items-center gap-1.5 transition-all shadow-sm active:scale-95"; + btn.className = "text-[10px] font-black bg-blue-50 border border-blue-100 hover:bg-blue-600 hover:text-white text-blue-600 px-3 py-1.5 rounded-lg flex items-center gap-1.5 transition-all shadow-sm active:scale-95 shrink-0"; lucide.createIcons(); }, 3000); } else { @@ -970,6 +1044,88 @@ lucide.createIcons(); } } + + 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) { + const nameInput = document.getElementById('nName'); + if (!nameInput.value) { + nameInput.value = data.client.full_name; + nameInput.classList.add('bg-emerald-50'); + } + + const addrDiv = document.getElementById('addrSuggestions'); + if (data.client.addresses && data.client.addresses.length > 0) { + addrDiv.classList.remove('hidden'); + if (!document.getElementById('nAddr').value) { + document.getElementById('nAddr').value = data.client.addresses[0]; + } + } + } + } catch (e) { console.error("Error en buscador rápido:", e); } + } + + // --- FUNCIONES DEL EDITOR --- + + function enableEditing() { + document.getElementById('viewActions').classList.add('hidden'); + document.getElementById('editActions').classList.remove('hidden'); + + ['editName', 'editPhone', 'editAddr', 'editDesc'].forEach(id => { + const el = document.getElementById(id); + if(el) { + el.readOnly = false; + el.classList.add('input-editable'); + el.classList.remove('input-readonly'); + } + }); + } + + function cancelEditing() { + document.getElementById('viewActions').classList.remove('hidden'); + document.getElementById('editActions').classList.add('hidden'); + + ['editName', 'editPhone', 'editAddr', 'editDesc'].forEach(id => { + const el = document.getElementById(id); + if(el) { + el.readOnly = true; + el.classList.add('input-readonly'); + el.classList.remove('input-editable'); + } + }); + } + + async function saveFullEdit() { + const id = document.getElementById('detId').value; + const payload = { + name: document.getElementById('editName').value, + phone: document.getElementById('editPhone').value, + address: document.getElementById('editAddr').value, + description: document.getElementById('editDesc').value + }; + try { + const res = await fetch(`${API_URL}/providers/scraped/${id}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem("token")}` }, + body: JSON.stringify(payload) + }); + if (res.ok) { + showToast("✅ Ficha actualizada"); + cancelEditing(); + refreshPanel(); + } + } catch (e) { showToast("Error al guardar", "warning"); } + } + \ No newline at end of file