Actualizar servicios.html
This commit is contained in:
211
servicios.html
211
servicios.html
@@ -66,7 +66,9 @@
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<form onsubmit="handleFormSubmit(event)" class="space-y-6">
|
<form onsubmit="handleFormSubmit(event)" class="space-y-6">
|
||||||
<input type="hidden" id="editServiceId"> <div class="bg-white p-6 rounded-xl shadow-sm border border-gray-100">
|
<input type="hidden" id="editServiceId">
|
||||||
|
|
||||||
|
<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="user" class="w-5 h-5 text-gray-400"></i> Datos del Cliente
|
<i data-lucide="user" class="w-5 h-5 text-gray-400"></i> Datos del Cliente
|
||||||
</h3>
|
</h3>
|
||||||
@@ -245,7 +247,6 @@
|
|||||||
let allStatuses = [];
|
let allStatuses = [];
|
||||||
let currentServiceId = null;
|
let currentServiceId = null;
|
||||||
let autocomplete;
|
let autocomplete;
|
||||||
let currentServiceData = null; // Guardamos datos del servicio actual para edición
|
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", () => {
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
const token = localStorage.getItem("token");
|
const token = localStorage.getItem("token");
|
||||||
@@ -272,12 +273,12 @@
|
|||||||
document.getElementById('servicesListView').classList.remove('hidden');
|
document.getElementById('servicesListView').classList.remove('hidden');
|
||||||
fetchServices();
|
fetchServices();
|
||||||
} else {
|
} else {
|
||||||
// RESETEAR FORMULARIO SI ES NUEVO
|
|
||||||
if(document.getElementById('editServiceId').value !== "") {
|
if(document.getElementById('editServiceId').value !== "") {
|
||||||
document.getElementById('editServiceId').value = ""; // Limpiar ID
|
document.getElementById('editServiceId').value = "";
|
||||||
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(); // Reset fecha
|
document.getElementById('sDate').valueAsDate = new Date();
|
||||||
|
document.getElementById('sCreateStatus').disabled = false; // Reactivar al crear
|
||||||
lucide.createIcons();
|
lucide.createIcons();
|
||||||
}
|
}
|
||||||
document.getElementById('createServiceView').classList.remove('hidden');
|
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 += `<option value="${s.id}" ${isDef}>${s.name}</option>`;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} 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 = '<option value="">-- Seleccionar --</option>';
|
||||||
|
data.companies.forEach(c => sel.innerHTML += `<option value="${c.id}">${c.name}</option>`);
|
||||||
|
}
|
||||||
|
} 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() {
|
async function editService() {
|
||||||
if(!currentServiceId) return;
|
if(!currentServiceId) return;
|
||||||
|
|
||||||
// Cargar datos completos si no los tenemos
|
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_URL}/services/${currentServiceId}`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } });
|
const res = await fetch(`${API_URL}/services/${currentServiceId}`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } });
|
||||||
const json = await res.json();
|
const json = await res.json();
|
||||||
if(json.ok) {
|
if(json.ok) {
|
||||||
fillEditForm(json.service);
|
await fillEditForm(json.service); // AWAIT IMPORTANTE
|
||||||
closeDetailPanel();
|
closeDetailPanel();
|
||||||
document.getElementById('createServiceView').classList.remove('hidden');
|
document.getElementById('createServiceView').classList.remove('hidden');
|
||||||
document.getElementById('servicesListView').classList.add('hidden');
|
document.getElementById('servicesListView').classList.add('hidden');
|
||||||
@@ -302,7 +358,8 @@
|
|||||||
} catch(e) { showToast("Error cargando datos", true); }
|
} 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('editServiceId').value = s.id;
|
||||||
document.getElementById('formTitle').innerHTML = '<i data-lucide="edit-2" class="text-blue-600"></i> Editando Servicio #' + s.id;
|
document.getElementById('formTitle').innerHTML = '<i data-lucide="edit-2" class="text-blue-600"></i> Editando Servicio #' + s.id;
|
||||||
|
|
||||||
@@ -312,7 +369,6 @@
|
|||||||
document.getElementById('sEmail').value = s.email || '';
|
document.getElementById('sEmail').value = s.email || '';
|
||||||
document.getElementById('sDesc').value = s.description || '';
|
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_date) document.getElementById('sDate').value = s.scheduled_date.split('T')[0];
|
||||||
if(s.scheduled_time) document.getElementById('sTime').value = s.scheduled_time;
|
if(s.scheduled_time) document.getElementById('sTime').value = s.scheduled_time;
|
||||||
|
|
||||||
@@ -320,37 +376,24 @@
|
|||||||
document.getElementById('sUrgent').checked = s.is_urgent;
|
document.getElementById('sUrgent').checked = s.is_urgent;
|
||||||
|
|
||||||
document.getElementById('sIsCompany').checked = s.is_company;
|
document.getElementById('sIsCompany').checked = s.is_company;
|
||||||
toggleCompanyFields(); // Mostrar/ocultar panel
|
|
||||||
|
// LÓGICA CRÍTICA PARA COMPAÑÍA
|
||||||
if(s.is_company) {
|
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('sCompanyId').value = s.company_id || '';
|
||||||
document.getElementById('sCompanyRef').value = s.company_ref || '';
|
document.getElementById('sCompanyRef').value = s.company_ref || '';
|
||||||
|
} else {
|
||||||
|
document.getElementById('companyFields').classList.add('hidden');
|
||||||
}
|
}
|
||||||
|
|
||||||
document.getElementById('sNotesInternal').value = s.internal_notes || '';
|
document.getElementById('sNotesInternal').value = s.internal_notes || '';
|
||||||
document.getElementById('sNotesClient').value = s.client_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; // No editar estado aquí
|
||||||
document.getElementById('sCreateStatus').disabled = true;
|
|
||||||
|
|
||||||
lucide.createIcons();
|
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) {
|
async function handleFormSubmit(e) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
const btn = document.getElementById('btnSave');
|
const btn = document.getElementById('btnSave');
|
||||||
@@ -377,7 +420,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (!isEdit) {
|
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`;
|
const url = isEdit ? `${API_URL}/services/${editId}` : `${API_URL}/services`;
|
||||||
@@ -392,66 +435,27 @@
|
|||||||
const json = await res.json();
|
const json = await res.json();
|
||||||
|
|
||||||
if (json.ok) {
|
if (json.ok) {
|
||||||
showToast(isEdit ? "✅ Servicio Actualizado" : "✅ Servicio Creado");
|
showToast(isEdit ? "✅ Actualizado" : "✅ Creado");
|
||||||
toggleView('list');
|
toggleView('list');
|
||||||
} else {
|
} else {
|
||||||
showToast("❌ " + (json.error || "Error"), true);
|
showToast("❌ " + (json.error || "Error"), true);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) { showToast("Error conexión", true); }
|
||||||
showToast("Error de conexión", true);
|
finally { btn.disabled = false; btn.innerText = "GUARDAR"; }
|
||||||
} finally {
|
|
||||||
btn.disabled = false; btn.innerText = "GUARDAR";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- RESTO DE FUNCIONES (MAESTROS, LISTAR, ETC) ---
|
async function deleteService() {
|
||||||
async function fetchStatuses() {
|
if(!currentServiceId || !confirm("¿Borrar servicio permanentemente?")) return;
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_URL}/statuses`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } });
|
const res = await fetch(`${API_URL}/services/${currentServiceId}`, {
|
||||||
const data = await res.json();
|
method: 'DELETE',
|
||||||
if(data.ok) {
|
headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` }
|
||||||
allStatuses = data.statuses;
|
|
||||||
const createSel = document.getElementById('sCreateStatus');
|
|
||||||
createSel.innerHTML = '';
|
|
||||||
allStatuses.forEach(s => {
|
|
||||||
const isDef = s.is_default ? 'selected' : '';
|
|
||||||
createSel.innerHTML += `<option value="${s.id}" ${isDef}>${s.name}</option>`;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} 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 = '<option value="">-- Seleccionar --</option>';
|
|
||||||
data.companies.forEach(c => sel.innerHTML += `<option value="${c.id}">${c.name}</option>`);
|
|
||||||
}
|
|
||||||
} 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"); loadCompanies(); }
|
if(res.ok) { showToast("Eliminado"); closeDetailPanel(); fetchServices(); }
|
||||||
} catch(e) { alert("Error"); }
|
} catch(e) { showToast("Error", true); }
|
||||||
}
|
|
||||||
|
|
||||||
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'); }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- 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;
|
||||||
@@ -475,72 +479,53 @@
|
|||||||
document.getElementById('clientFoundMsg').classList.add('hidden');
|
document.getElementById('clientFoundMsg').classList.add('hidden');
|
||||||
document.getElementById('sAddressSelect').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; }
|
function selectAddress(val) { if(val) document.getElementById('sAddress').value = val; }
|
||||||
|
|
||||||
// --- LISTAR Y DETALLES ---
|
// --- LISTAR SERVICIOS ---
|
||||||
async function fetchServices() {
|
async function fetchServices() {
|
||||||
try {
|
try {
|
||||||
const res = await fetch(`${API_URL}/services`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } });
|
const res = await fetch(`${API_URL}/services`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } });
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
const tbody = document.getElementById('servicesTableBody');
|
const tbody = document.getElementById('servicesTableBody');
|
||||||
tbody.innerHTML = "";
|
tbody.innerHTML = "";
|
||||||
|
|
||||||
if(!data.ok || data.services.length === 0) {
|
if(!data.ok || data.services.length === 0) {
|
||||||
tbody.innerHTML = `<tr><td colspan="5" class="p-8 text-center text-gray-400 bg-white">No hay servicios registrados. <button onclick="toggleView('create')" class="text-blue-600 font-bold hover:underline">Crear el primero</button></td></tr>`;
|
tbody.innerHTML = `<tr><td colspan="5" class="p-8 text-center text-gray-400 bg-white">No hay servicios. <button onclick="toggleView('create')" class="text-blue-600 font-bold hover:underline">Crear uno</button></td></tr>`;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
data.services.forEach(s => {
|
data.services.forEach(s => {
|
||||||
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' });
|
||||||
|
|
||||||
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">
|
<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>
|
||||||
<p class="font-bold text-gray-900">${s.contact_name}</p>
|
<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>
|
||||||
<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 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>
|
|
||||||
<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">
|
|
||||||
<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>
|
||||||
`;
|
`;
|
||||||
});
|
});
|
||||||
lucide.createIcons();
|
lucide.createIcons();
|
||||||
} catch (e) { console.error(e); }
|
} catch (e) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function openDetail(id, client, title, statusName, statusColor, date) {
|
async function openDetail(id, client, title, statusName, statusColor, date) {
|
||||||
currentServiceId = id;
|
currentServiceId = id;
|
||||||
document.getElementById('serviceDetailPanel').classList.remove('hidden');
|
document.getElementById('serviceDetailPanel').classList.remove('hidden');
|
||||||
|
|
||||||
document.getElementById('detailTitle').innerText = title || 'Servicio General';
|
document.getElementById('detailTitle').innerText = title || 'Servicio General';
|
||||||
document.getElementById('detailClient').innerText = client;
|
document.getElementById('detailClient').innerText = client;
|
||||||
document.getElementById('detailId').innerText = `#${id}`;
|
document.getElementById('detailId').innerText = `#${id}`;
|
||||||
document.getElementById('detailDate').innerText = date;
|
document.getElementById('detailDate').innerText = date;
|
||||||
|
|
||||||
const badge = document.getElementById('detailStatusBadge');
|
const badge = document.getElementById('detailStatusBadge');
|
||||||
badge.innerText = statusName;
|
badge.innerText = statusName;
|
||||||
badge.className = `px-2 py-1 rounded text-[10px] font-bold text-white uppercase tracking-wide bg-${statusColor}-500`;
|
badge.className = `px-2 py-1 rounded text-[10px] font-bold text-white uppercase tracking-wide bg-${statusColor}-500`;
|
||||||
|
|
||||||
const sel = document.getElementById('newStatusSelect');
|
const sel = document.getElementById('newStatusSelect');
|
||||||
sel.innerHTML = "";
|
sel.innerHTML = "";
|
||||||
allStatuses.forEach(s => sel.innerHTML += `<option value="${s.id}">${s.name}</option>`);
|
allStatuses.forEach(s => sel.innerHTML += `<option value="${s.id}">${s.name}</option>`);
|
||||||
|
|
||||||
loadTimeline(id);
|
loadTimeline(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -550,15 +535,15 @@
|
|||||||
const res = await fetch(`${API_URL}/services/${id}/logs`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } });
|
const res = await fetch(`${API_URL}/services/${id}/logs`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } });
|
||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
timeline.innerHTML = "";
|
timeline.innerHTML = "";
|
||||||
if(data.logs.length === 0) { timeline.innerHTML = '<p class="text-sm text-gray-400">Sin historial registrado.</p>'; return; }
|
if(data.logs.length === 0) { timeline.innerHTML = '<p class="text-sm text-gray-400">Sin historial.</p>'; return; }
|
||||||
data.logs.forEach(log => {
|
data.logs.forEach(log => {
|
||||||
const color = log.new_color || 'gray';
|
const color = log.new_color || 'gray';
|
||||||
const date = new Date(log.created_at);
|
const date = new Date(log.created_at);
|
||||||
timeline.innerHTML += `
|
timeline.innerHTML += `
|
||||||
<div class="mb-6 relative group">
|
<div class="mb-6 relative group">
|
||||||
<span class="absolute -left-[33px] flex items-center justify-center w-6 h-6 bg-${color}-100 rounded-full ring-4 ring-white group-hover:scale-110 transition-transform"><div class="w-2 h-2 bg-${color}-600 rounded-full"></div></span>
|
<span class="absolute -left-[33px] flex items-center justify-center w-6 h-6 bg-${color}-100 rounded-full ring-4 ring-white"><div class="w-2 h-2 bg-${color}-600 rounded-full"></div></span>
|
||||||
<div class="flex justify-between items-center mb-1"><h3 class="text-sm font-bold text-gray-800">${log.new_status}</h3><time class="text-[10px] font-medium text-gray-400 uppercase">${date.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})} · ${date.toLocaleDateString()}</time></div>
|
<div class="flex justify-between items-center mb-1"><h3 class="text-sm font-bold text-gray-800">${log.new_status}</h3><time class="text-[10px] font-medium text-gray-400 uppercase">${date.toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'})} · ${date.toLocaleDateString()}</time></div>
|
||||||
<div class="bg-gray-50 p-3 rounded-lg border border-gray-100 text-sm text-gray-600 hover:bg-white hover:shadow-sm transition-all"><p>${log.comment || 'Cambio de estado'}</p><p class="text-[10px] text-gray-400 mt-2 text-right italic border-t border-gray-100 pt-1">Usuario: ${log.user_name || 'Sistema'}</p></div>
|
<div class="bg-gray-50 p-3 rounded-lg border border-gray-100 text-sm text-gray-600"><p>${log.comment || 'Cambio de estado'}</p><p class="text-[10px] text-gray-400 mt-2 text-right italic border-t border-gray-100 pt-1">Usuario: ${log.user_name || 'Sistema'}</p></div>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
});
|
});
|
||||||
@@ -567,7 +552,7 @@
|
|||||||
async function updateStatus() {
|
async function updateStatus() {
|
||||||
const statusId = document.getElementById('newStatusSelect').value;
|
const statusId = document.getElementById('newStatusSelect').value;
|
||||||
if(!statusId) return;
|
if(!statusId) return;
|
||||||
const comment = prompt("Añade un comentario sobre este cambio (opcional):");
|
const comment = prompt("Comentario (opcional):");
|
||||||
try {
|
try {
|
||||||
await fetch(`${API_URL}/services/${currentServiceId}/status`, {
|
await fetch(`${API_URL}/services/${currentServiceId}/status`, {
|
||||||
method: 'PUT',
|
method: 'PUT',
|
||||||
@@ -577,7 +562,7 @@
|
|||||||
showToast("Estado actualizado");
|
showToast("Estado actualizado");
|
||||||
loadTimeline(currentServiceId);
|
loadTimeline(currentServiceId);
|
||||||
fetchServices();
|
fetchServices();
|
||||||
} catch(e) { showToast("Error al actualizar", true); }
|
} catch(e) { showToast("Error", true); }
|
||||||
}
|
}
|
||||||
|
|
||||||
function closeDetailPanel() { document.getElementById('serviceDetailPanel').classList.add('hidden'); }
|
function closeDetailPanel() { document.getElementById('serviceDetailPanel').classList.add('hidden'); }
|
||||||
|
|||||||
Reference in New Issue
Block a user