Actualizar servicios.html
This commit is contained in:
318
servicios.html
318
servicios.html
@@ -43,8 +43,8 @@
|
|||||||
<thead class="bg-gray-50 text-gray-500 text-xs uppercase tracking-wider border-b">
|
<thead class="bg-gray-50 text-gray-500 text-xs uppercase tracking-wider border-b">
|
||||||
<tr>
|
<tr>
|
||||||
<th class="p-4 font-semibold">Fecha</th>
|
<th class="p-4 font-semibold">Fecha</th>
|
||||||
<th class="p-4 font-semibold">Cliente / Dirección</th>
|
<th class="p-4 font-semibold">Cliente</th>
|
||||||
<th class="p-4 font-semibold">Detalle</th>
|
<th class="p-4 font-semibold">Asignado</th>
|
||||||
<th class="p-4 font-semibold">Estado</th>
|
<th class="p-4 font-semibold">Estado</th>
|
||||||
<th class="p-4 text-right"></th>
|
<th class="p-4 text-right"></th>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -105,9 +105,24 @@
|
|||||||
|
|
||||||
<div class="bg-white p-6 rounded-xl shadow-sm border border-gray-100">
|
<div class="bg-white p-6 rounded-xl shadow-sm border border-gray-100">
|
||||||
<h3 class="text-lg font-bold text-gray-700 mb-4 border-b pb-2 flex items-center gap-2">
|
<h3 class="text-lg font-bold text-gray-700 mb-4 border-b pb-2 flex items-center gap-2">
|
||||||
<i data-lucide="clipboard" class="w-5 h-5 text-gray-400"></i> Detalles del Trabajo
|
<i data-lucide="clipboard" class="w-5 h-5 text-gray-400"></i> Asignación y Detalles
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-4 mb-4 bg-blue-50 p-4 rounded-lg border border-blue-100">
|
||||||
|
<div>
|
||||||
|
<label class="block text-xs font-bold text-blue-800 mb-1">Gremio / Especialidad *</label>
|
||||||
|
<select id="sGuild" class="w-full px-3 py-2 border border-blue-200 rounded-lg bg-white outline-none focus:border-blue-500" onchange="loadOperators(this.value)">
|
||||||
|
<option value="">-- Seleccionar Gremio --</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label class="block text-xs font-bold text-blue-800 mb-1">Asignar Operario</label>
|
||||||
|
<select id="sOperator" class="w-full px-3 py-2 border border-blue-200 rounded-lg bg-white outline-none focus:border-blue-500">
|
||||||
|
<option value="">-- Primero elige gremio --</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-5 gap-4 mb-4">
|
<div class="grid grid-cols-1 md:grid-cols-5 gap-4 mb-4">
|
||||||
<div class="md:col-span-1">
|
<div class="md:col-span-1">
|
||||||
<label class="block text-xs font-bold text-gray-600 mb-1">Estado Inicial</label>
|
<label class="block text-xs font-bold text-gray-600 mb-1">Estado Inicial</label>
|
||||||
@@ -125,15 +140,9 @@
|
|||||||
<div>
|
<div>
|
||||||
<label class="block text-xs font-bold text-gray-600 mb-1">Duración Est.</label>
|
<label class="block text-xs font-bold text-gray-600 mb-1">Duración Est.</label>
|
||||||
<select id="sDuration" class="w-full px-3 py-2 border rounded-lg bg-white outline-none focus:border-blue-500">
|
<select id="sDuration" class="w-full px-3 py-2 border rounded-lg bg-white outline-none focus:border-blue-500">
|
||||||
<option value="15">15 min</option>
|
|
||||||
<option value="30" selected>30 min</option>
|
<option value="30" selected>30 min</option>
|
||||||
<option value="45">45 min</option>
|
|
||||||
<option value="60">1 hora</option>
|
<option value="60">1 hora</option>
|
||||||
<option value="90">1h 30m</option>
|
|
||||||
<option value="120">2 horas</option>
|
<option value="120">2 horas</option>
|
||||||
<option value="150">2h 30m</option>
|
|
||||||
<option value="180">3 horas</option>
|
|
||||||
<option value="210">3h 30m</option>
|
|
||||||
<option value="240">4 horas</option>
|
<option value="240">4 horas</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
@@ -204,9 +213,9 @@
|
|||||||
|
|
||||||
<div class="flex gap-2 mb-6">
|
<div class="flex gap-2 mb-6">
|
||||||
<button onclick="editService()" class="flex-1 bg-white border border-gray-300 hover:bg-gray-50 text-gray-700 py-2 rounded-lg text-sm font-bold shadow-sm flex items-center justify-center gap-2">
|
<button onclick="editService()" class="flex-1 bg-white border border-gray-300 hover:bg-gray-50 text-gray-700 py-2 rounded-lg text-sm font-bold shadow-sm flex items-center justify-center gap-2">
|
||||||
<i data-lucide="edit-2" class="w-4 h-4"></i> Editar Datos
|
<i data-lucide="edit-2" class="w-4 h-4"></i> Editar
|
||||||
</button>
|
</button>
|
||||||
<button onclick="deleteService()" class="bg-white border border-red-200 hover:bg-red-50 text-red-600 py-2 px-4 rounded-lg text-sm font-bold shadow-sm" title="Borrar Servicio">
|
<button onclick="deleteService()" class="bg-white border border-red-200 hover:bg-red-50 text-red-600 py-2 px-4 rounded-lg text-sm font-bold shadow-sm">
|
||||||
<i data-lucide="trash-2" class="w-4 h-4"></i>
|
<i data-lucide="trash-2" class="w-4 h-4"></i>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
@@ -254,6 +263,7 @@
|
|||||||
fetchStatuses();
|
fetchStatuses();
|
||||||
fetchServices();
|
fetchServices();
|
||||||
loadCompanies();
|
loadCompanies();
|
||||||
|
loadGuilds();
|
||||||
});
|
});
|
||||||
|
|
||||||
function initAutocomplete() {
|
function initAutocomplete() {
|
||||||
@@ -278,7 +288,7 @@
|
|||||||
document.querySelector('form').reset();
|
document.querySelector('form').reset();
|
||||||
document.getElementById('formTitle').innerHTML = '<i data-lucide="file-plus" class="text-green-600"></i> Alta de Nuevo Servicio';
|
document.getElementById('formTitle').innerHTML = '<i data-lucide="file-plus" class="text-green-600"></i> Alta de Nuevo Servicio';
|
||||||
document.getElementById('sDate').valueAsDate = new Date();
|
document.getElementById('sDate').valueAsDate = new Date();
|
||||||
document.getElementById('sCreateStatus').disabled = false; // Reactivar al crear
|
document.getElementById('sCreateStatus').disabled = false;
|
||||||
lucide.createIcons();
|
lucide.createIcons();
|
||||||
}
|
}
|
||||||
document.getElementById('createServiceView').classList.remove('hidden');
|
document.getElementById('createServiceView').classList.remove('hidden');
|
||||||
@@ -286,7 +296,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- CARGA DE DATOS ---
|
// --- CARGA DE DATOS (Gremios, Operarios, etc) ---
|
||||||
async function fetchStatuses() {
|
async function fetchStatuses() {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_URL}/statuses`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } });
|
const res = await fetch(`${API_URL}/statuses`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } });
|
||||||
@@ -303,13 +313,44 @@
|
|||||||
} catch(e) {}
|
} 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 = '<option value="">-- Seleccionar Gremio --</option>';
|
||||||
|
data.guilds.forEach(g => sel.innerHTML += `<option value="${g.id}">${g.name}</option>`);
|
||||||
|
}
|
||||||
|
} catch(e) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function loadOperators(guildId) {
|
||||||
|
const sel = document.getElementById('sOperator');
|
||||||
|
sel.innerHTML = '<option value="">Cargando...</option>';
|
||||||
|
sel.disabled = true;
|
||||||
|
|
||||||
|
if(!guildId) {
|
||||||
|
sel.innerHTML = '<option value="">-- Primero elige gremio --</option>';
|
||||||
|
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 = '<option value="">-- Sin asignar --</option>';
|
||||||
|
if(data.operators.length === 0) sel.innerHTML += '<option value="" disabled>No hay operarios para este gremio</option>';
|
||||||
|
data.operators.forEach(op => sel.innerHTML += `<option value="${op.id}">${op.full_name}</option>`);
|
||||||
|
}
|
||||||
|
} catch(e) { sel.innerHTML = '<option value="">Error al cargar</option>'; }
|
||||||
|
finally { sel.disabled = false; }
|
||||||
|
}
|
||||||
|
|
||||||
async function loadCompanies() {
|
async function loadCompanies() {
|
||||||
try {
|
try {
|
||||||
const sel = document.getElementById('sCompanyId');
|
const sel = document.getElementById('sCompanyId');
|
||||||
// Si ya tiene datos (>1 porque la primera es "--Seleccionar--"), no recargamos
|
|
||||||
if (sel.options.length > 1) return;
|
if (sel.options.length > 1) return;
|
||||||
|
|
||||||
const res = await fetch(`${API_URL}/companies`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } });
|
const res = await fetch(`${API_URL}/companies`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } });
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
if (data.ok) {
|
if (data.ok) {
|
||||||
@@ -332,130 +373,12 @@
|
|||||||
} catch(e) { alert("Error"); }
|
} catch(e) { alert("Error"); }
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- EDICIÓN Y LOGICA DE FORMULARIO ---
|
|
||||||
function toggleCompanyFields() {
|
function toggleCompanyFields() {
|
||||||
const isChecked = document.getElementById('sIsCompany').checked;
|
const isChecked = document.getElementById('sIsCompany').checked;
|
||||||
const div = document.getElementById('companyFields');
|
const div = document.getElementById('companyFields');
|
||||||
if(isChecked) {
|
if(isChecked) { div.classList.remove('hidden'); loadCompanies(); } else { div.classList.add('hidden'); }
|
||||||
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 = '<i data-lucide="edit-2" class="text-blue-600"></i> 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() {
|
async function searchClientByPhone() {
|
||||||
const phone = document.getElementById('sPhone').value;
|
const phone = document.getElementById('sPhone').value;
|
||||||
if(phone.length < 8) return;
|
if(phone.length < 8) return;
|
||||||
@@ -498,12 +421,15 @@
|
|||||||
const color = s.status_color || 'gray';
|
const color = s.status_color || 'gray';
|
||||||
const date = new Date(s.created_at);
|
const date = new Date(s.created_at);
|
||||||
const formattedDate = date.toLocaleDateString('es-ES', { day: '2-digit', month: 'short' });
|
const formattedDate = date.toLocaleDateString('es-ES', { day: '2-digit', month: 'short' });
|
||||||
|
// Mostramos operario si existe
|
||||||
|
const assigned = s.assigned_name ? `<span class="bg-gray-100 text-gray-700 px-2 py-0.5 rounded text-[10px] font-bold uppercase ml-1 border border-gray-200">${s.assigned_name}</span>` : '<span class="text-gray-300 text-[10px] italic ml-1">Sin asignar</span>';
|
||||||
|
|
||||||
tbody.innerHTML += `
|
tbody.innerHTML += `
|
||||||
<tr class="hover:bg-blue-50 cursor-pointer transition border-b last:border-0 bg-white"
|
<tr class="hover:bg-blue-50 cursor-pointer transition border-b last:border-0 bg-white"
|
||||||
onclick="openDetail(${s.id}, '${s.contact_name}', '${s.title}', '${s.status_name}', '${color}', '${formattedDate}')">
|
onclick="openDetail(${s.id}, '${s.contact_name}', '${s.title}', '${s.status_name}', '${color}', '${formattedDate}')">
|
||||||
<td class="p-4 text-gray-500 whitespace-nowrap font-mono text-xs">${formattedDate}</td>
|
<td class="p-4 text-gray-500 whitespace-nowrap font-mono text-xs">${formattedDate}</td>
|
||||||
<td class="p-4"><p class="font-bold text-gray-900">${s.contact_name}</p><p class="text-xs text-gray-500 truncate max-w-[200px] flex items-center gap-1"><i data-lucide="map-pin" class="w-3 h-3"></i> ${s.address}</p></td>
|
<td class="p-4"><p class="font-bold text-gray-900">${s.contact_name}</p><p class="text-xs text-gray-500 truncate max-w-[200px] flex items-center gap-1"><i data-lucide="map-pin" class="w-3 h-3"></i> ${s.address}</p></td>
|
||||||
<td class="p-4"><p class="text-sm text-gray-700 truncate max-w-[250px]">${s.description || 'Sin detalles'}</p>${s.is_urgent ? '<span class="text-[10px] font-bold text-red-600 bg-red-100 px-1 rounded uppercase">Urgente</span>' : ''}${s.is_company ? `<span class="text-[10px] font-bold text-blue-600 bg-blue-100 px-1 rounded uppercase ml-1">${s.company_name || 'Compañía'}</span>` : ''}</td>
|
<td class="p-4">${assigned} <p class="text-sm text-gray-700 truncate max-w-[250px] mt-1">${s.description || 'Sin detalles'}</p>${s.is_urgent ? '<span class="text-[10px] font-bold text-red-600 bg-red-100 px-1 rounded uppercase">Urgente</span>' : ''}${s.is_company ? `<span class="text-[10px] font-bold text-blue-600 bg-blue-100 px-1 rounded uppercase ml-1">${s.company_name || 'Compañía'}</span>` : ''}</td>
|
||||||
<td class="p-4"><span class="px-3 py-1 rounded-full text-xs font-bold text-white shadow-sm bg-${color}-500 whitespace-nowrap">${s.status_name || 'Nuevo'}</span></td>
|
<td class="p-4"><span class="px-3 py-1 rounded-full text-xs font-bold text-white shadow-sm bg-${color}-500 whitespace-nowrap">${s.status_name || 'Nuevo'}</span></td>
|
||||||
<td class="p-4 text-right"><i data-lucide="chevron-right" class="w-5 h-5 text-gray-300"></i></td>
|
<td class="p-4 text-right"><i data-lucide="chevron-right" class="w-5 h-5 text-gray-300"></i></td>
|
||||||
</tr>
|
</tr>
|
||||||
@@ -565,6 +491,126 @@
|
|||||||
} catch(e) { showToast("Error", true); }
|
} 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 = '<i data-lucide="edit-2" class="text-blue-600"></i> 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 closeDetailPanel() { document.getElementById('serviceDetailPanel').classList.add('hidden'); }
|
||||||
function showToast(msg, isError = false) {
|
function showToast(msg, isError = false) {
|
||||||
const t = document.getElementById('toast'), m = document.getElementById('toastMsg');
|
const t = document.getElementById('toast'), m = document.getElementById('toastMsg');
|
||||||
|
|||||||
Reference in New Issue
Block a user