+
-
+
+
0
-
En Proceso
+
En Proceso
0
-
Finalizados
+
Finalizados
@@ -163,7 +171,7 @@
let urlToken = "";
let etasToInit = [];
- let currentQuotes = []; // Lista global de presupuestos
+ let currentQuotes = [];
document.addEventListener("DOMContentLoaded", async () => {
lucide.createIcons();
@@ -174,9 +182,8 @@
if (!urlToken) { showError(); return; }
try {
+ // Obtenemos SIEMPRE todos los servicios de ese cliente, aunque haya '?service=XX' en la URL
let fetchUrl = `${API_URL}/public/portal/${urlToken}`;
- if (serviceParam) fetchUrl += `?service=${serviceParam}`;
-
const res = await fetch(fetchUrl);
const data = await res.json();
@@ -184,8 +191,6 @@
const servicesList = data.services || [];
- // MOCK DE PRESUPUESTOS (Para que veas la funcionalidad nueva)
- // Si en el futuro tu API devuelve data.quotes, usará eso. Si no, metemos uno de prueba.
currentQuotes = data.quotes || [
{ id: 101, ref: 'PRE-2026-089', title: 'Cambio de tubería principal y alicatado', amount: '345.50', date: '28/03/2026' }
];
@@ -193,6 +198,18 @@
renderPortal(data.client, data.company, servicesList);
renderQuotes();
+ // Si viene un service en la URL, podríamos hacer scroll hacia él aquí
+ if (serviceParam) {
+ setTimeout(() => {
+ const targetCard = document.getElementById(`service-card-${serviceParam}`);
+ if (targetCard) {
+ targetCard.scrollIntoView({ behavior: 'smooth', block: 'center' });
+ targetCard.classList.add('ring-2', 'ring-blue-400', 'ring-offset-4');
+ setTimeout(() => targetCard.classList.remove('ring-2', 'ring-blue-400', 'ring-offset-4'), 3000);
+ }
+ }, 500);
+ }
+
} catch (e) {
console.error("Error cargando portal:", e);
showError();
@@ -208,13 +225,11 @@
}, 300);
}
- // --- SISTEMA DE PESTAÑAS (NAVEGACIÓN INFERIOR) ---
+ // --- SISTEMA DE PESTAÑAS ---
function switchTab(tabName) {
- // Ocultar todo
document.querySelectorAll('.tab-content').forEach(el => el.classList.remove('active'));
document.querySelectorAll('.nav-btn').forEach(el => el.classList.remove('active'));
- // Mostrar el activo
document.getElementById(`tab${tabName}`).classList.add('active');
document.getElementById(`btnNav${tabName}`).classList.add('active');
}
@@ -284,17 +299,14 @@
const q = currentQuotes.find(x => x.id === id);
if (!q) return;
- // Marcamos como visto
localStorage.setItem(`quote_viewed_${id}`, 'true');
- renderQuotes(); // Refrescamos lista para quitar globos rojos
+ renderQuotes();
- // Rellenamos Modal
document.getElementById('qmRef').innerText = q.ref;
document.getElementById('qmTitle').innerText = q.title;
document.getElementById('qmDate').innerText = q.date;
document.getElementById('qmAmount').innerText = q.amount + "€";
- // Mostramos Modal
const modal = document.getElementById('quoteModal');
const sheet = document.getElementById('quoteModalSheet');
modal.classList.remove('hidden');
@@ -347,7 +359,6 @@
// --- RENDER PRINCIPAL DE AVISOS ---
function renderPortal(client, company, allServices) {
- // Textos de Cabecera
if (company.name) document.title = `Portal - ${company.name}`;
if (company.logo) {
document.getElementById('companyLogo').src = company.logo;
@@ -357,7 +368,6 @@
let cName = client && client.name ? client.name.split(' ')[0] : "Cliente";
document.getElementById('clientName').innerText = cName;
- // Contenedores
const activeContainer = document.getElementById('activeServicesContainer');
const historyContainerWrapper = document.getElementById('historyContainerWrapper');
const historyContainer = document.getElementById('historyServicesContainer');
@@ -365,8 +375,11 @@
activeContainer.innerHTML = '';
historyContainer.innerHTML = '';
+ // Contadores
let countAct = 0;
let countHist = 0;
+ let countInc = 0;
+ let countPend = 0;
allServices.forEach(srv => {
let isFinalized = srv.is_final === true;
@@ -377,7 +390,27 @@
let hasWorker = (srv.assigned_worker && srv.assigned_worker !== 'Pendiente' && srv.assigned_worker !== 'Sin asignar');
let isUrgent = (srv.title && srv.title.includes('URGENTE')) || srv.is_urgent === true || (raw['Urgente'] && (raw['Urgente'].toLowerCase() === 'sí' || raw['Urgente'].toLowerCase() === 'si' || raw['Urgente'].toLowerCase() === 'true'));
- // DISEÑO DE ESTADOS EN TARJETA BLANCA (NUEVO)
+ // Recopilar dirección limpia para mostrar
+ let cAddr = raw["Dirección"] || raw["DOMICILIO"] || "Dirección no especificada";
+ let cCP = raw["Código Postal"] || "";
+ let cPop = raw["Población"] || raw["POBLACION-PROVINCIA"] || "";
+ let fullAddress = `${cAddr}, ${cCP} ${cPop}`.trim();
+ if(fullAddress === ",") fullAddress = "No especificada";
+
+ // DIRECCIÓN HTML (Restaurado a petición)
+ let addressHtml = `
+
+
+
+
+
+
Lugar de reparación
+
${fullAddress}
+
+
+ `;
+
+ // DISEÑO DE ESTADOS EN TARJETA BLANCA
let headerColor = "bg-slate-50 text-slate-500";
let icon = "clock";
let tagTitle = "Estado";
@@ -390,8 +423,7 @@
}
else if (stNameLower.includes('camino')) {
headerColor = "bg-indigo-100 text-indigo-600"; icon = "truck"; tagTitle = "Desplazamiento"; mainTitle = "¡En camino!"; subDesc = "El técnico se dirige a tu domicilio.";
- let fullAddr = `${raw["Dirección"] || ""}, ${raw["Código Postal"] || ""} ${raw["Población"] || ""}`;
- etasToInit.push({ id: srv.id, address: fullAddr });
+ etasToInit.push({ id: srv.id, address: fullAddress });
extras = `
`;
}
else if (stNameLower.includes('trabajando') || stNameLower.includes('reparación')) {
@@ -420,26 +452,35 @@
if (isLate) {
headerColor = "bg-amber-100 text-amber-600"; icon = "clock-4"; tagTitle = "Demora"; mainTitle = "Técnico Retrasado"; subDesc = `La cita estaba prevista hasta las ${endT}. Llegará lo antes posible.`;
- extras = `
Modificar Cita`;
+ extras = `
Modificar Cita`;
} else {
headerColor = "bg-emerald-100 text-emerald-600"; icon = "calendar-check"; tagTitle = "Confirmada"; mainTitle = `${formatDate(srv.scheduled_date)}`; subDesc = `El técnico llegará entre las
${srv.scheduled_time} y las
${endT}.`;
- extras = `
Gestionar Cita`;
+ extras = `
Gestionar Cita`;
}
}
else if (isUrgent) {
headerColor = "bg-red-100 text-red-600"; icon = "flame"; tagTitle = "Urgencia"; mainTitle = "Prioridad Máxima"; subDesc = hasWorker ? "Técnico asignado de urgencia en camino." : "Buscando técnico de emergencia.";
}
else if (stNameLower.includes('esperando') || stNameLower.includes('asignado') || (hasWorker && !hasDate)) {
- headerColor = "bg-blue-100 text-blue-600"; icon = "calendar-plus"; tagTitle = "Acción Requerida"; mainTitle = "Elige tu cita"; subDesc = "Técnico asignado. Selecciona cuándo quieres que vayamos.";
+ headerColor = "bg-amber-100 text-amber-600"; icon = "calendar-plus"; tagTitle = "Acción Requerida"; mainTitle = "Elige tu cita"; subDesc = "Técnico asignado. Selecciona cuándo quieres que vayamos.";
extras = `
Agendar Cita Ahora`;
}
else if (stNameLower.includes('desasignado')) {
headerColor = "bg-slate-100 text-slate-500"; icon = "user-x"; tagTitle = "Reorganizando"; mainTitle = "Sin Técnico"; subDesc = "Buscando un nuevo técnico disponible para tu zona.";
}
+ // Contadores para el Dashboard superior
+ if (isFinalized) {
+ countHist++;
+ } else {
+ countAct++;
+ if (stNameLower.includes('incidencia')) countInc++;
+ if (stNameLower.includes('esperando') || stNameLower.includes('asignado') || (hasWorker && !hasDate)) countPend++;
+ }
+
// Generación de la tarjeta Blanca
let cardHtml = `
-