diff --git a/menu.html b/menu.html
index a818f4e..bfe11dc 100644
--- a/menu.html
+++ b/menu.html
@@ -83,6 +83,14 @@
Buscar
Servicio
+
+
+
+
+
+ Ranking
y Estadísticas
+
+
@@ -93,6 +101,7 @@
let userZones = [];
let userGuilds = [];
+ let systemStatuses = [];
// --- SISTEMA DE TEMA DINÁMICO ---
async function applyTheme() {
@@ -118,12 +127,11 @@
const token = localStorage.getItem("token");
const role = localStorage.getItem("role");
- if (!token || role !== 'operario') {
+ if (!token || (role !== 'operario' && role !== 'operario_cerrado')) {
window.location.href = "index.html";
return;
}
- // Aplicamos los colores corporativos de la empresa
await applyTheme();
lucide.createIcons();
@@ -133,19 +141,27 @@
const options = { weekday: 'long', day: 'numeric', month: 'long' };
document.getElementById('headerDate').innerText = new Date().toLocaleDateString('es-ES', options);
- await fetchUserData(); // Para saber los gremios y zonas del operario
+ await loadStatuses();
+ await fetchUserData();
fetchBadges();
});
- // 1. Obtener zonas y gremios del operario
+ async function loadStatuses() {
+ try {
+ const res = await fetch(`${API_URL}/statuses`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } });
+ const data = await res.json();
+ if (data.ok) systemStatuses = data.statuses;
+ } catch (e) { console.error("Error cargando estados:", e); }
+ }
+
async function fetchUserData() {
try {
const res = await fetch(`${API_URL}/auth/me`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } });
const data = await res.json();
if (data.ok && data.user) {
- // zones suele venir como array de objetos: [{"cps": "11204"}]
userZones = Array.isArray(data.user.zones) ? data.user.zones.map(z => z.cps) : [];
- // Necesitamos cargar los gremios de otra ruta porque auth/me no los trae por defecto
+
+ // Necesitamos cargar los gremios
const resG = await fetch(`${API_URL}/admin/users`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } });
const dataG = await resG.json();
if(dataG.ok) {
@@ -158,18 +174,37 @@
async function fetchBadges() {
try {
- // 1. Pedimos los servicios ASIGNADOS AL OPERARIO (para el badge naranja)
- const resActive = await fetch(`${API_URL}/services/active`, {
- headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` }
- });
- const dataActive = await resActive.json();
+ const headers = { "Authorization": `Bearer ${localStorage.getItem("token")}` };
- if (dataActive.ok) {
- const myServices = dataActive.services.filter(s => s.provider !== 'SYSTEM_BLOCK');
+ // 1. Cargar servicios activos del operario y peticiones
+ const resActive = await fetch(`${API_URL}/services/active`, { headers });
+ const dataActive = await resActive.json();
+
+ const resReqs = await fetch(`${API_URL}/agenda/requests`, { headers });
+ const dataReqs = await resReqs.json();
+
+ if (dataActive.ok && dataReqs.ok) {
let sinCitaCount = 0;
- myServices.forEach(s => {
+
+ // Sumamos las peticiones pendientes
+ sinCitaCount += dataReqs.requests.length;
+
+ // Filtramos y contamos la lista normal
+ dataActive.services.forEach(s => {
+ if (s.provider === 'SYSTEM_BLOCK') return;
+
const raw = s.raw_data || {};
- if (!raw.scheduled_date) sinCitaCount++;
+ if (raw.appointment_status === 'pending') return; // Ya las contamos arriba
+
+ // Si no tiene fecha, lo evaluamos
+ if (!raw.scheduled_date || raw.scheduled_date.trim() === "") {
+ // Validar que el estado no sea finalizado/anulado (ej: "Terminado sin fecha")
+ if (raw.status_operativo) {
+ const st = systemStatuses.find(x => String(x.id) === String(raw.status_operativo));
+ if (st && st.is_final) return;
+ }
+ sinCitaCount++;
+ }
});
if (sinCitaCount > 0) {
@@ -179,42 +214,36 @@
}
}
- // 2. Pedimos la "Bolsa de Trabajo" (servicios sin asignar que cuadran con CP y Gremio)
- // Usamos la ruta genérica de scraped pero filtramos en frontend para que sea más rápido.
- const resScraped = await fetch(`${API_URL}/providers/scraped`, {
- headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` }
- });
- const dataScraped = await resScraped.json();
+ // 2. Pedimos la "Bolsa de Trabajo" SOLO SI TIENE PERMISO (operario_cerrado NO cuenta bolsa)
+ if (localStorage.getItem("role") === 'operario') {
+ const resScraped = await fetch(`${API_URL}/providers/scraped`, { headers });
+ const dataScraped = await resScraped.json();
- if (dataScraped.ok) {
- let bolsaCount = 0;
- dataScraped.services.forEach(s => {
- // Descartamos si ya tiene asignado o si es un bloqueo
- if(s.assigned_to || s.provider === 'SYSTEM_BLOCK') return;
-
- const raw = s.raw_data || {};
- const dbStatus = raw.status_operativo;
-
- // Si está anulado/finalizado, descartar
- if(s.status === 'archived' || (dbStatus && ['anulado', 'terminado', 'finalizado'].includes(dbStatus.toLowerCase()))) return;
+ if (dataScraped.ok) {
+ let bolsaCount = 0;
+ dataScraped.services.forEach(s => {
+ if(s.assigned_to || s.provider === 'SYSTEM_BLOCK') return;
+
+ const raw = s.raw_data || {};
+ const dbStatus = raw.status_operativo;
+ if(s.status === 'archived' || (dbStatus && ['anulado', 'terminado', 'finalizado'].includes(dbStatus.toLowerCase()))) return;
- // Check de Gremio y Zona
- const sGuild = String(s.guild_id || raw.guild_id);
- const sCp = String(raw['Código Postal'] || raw['CP'] || "").trim();
+ const sGuild = String(s.guild_id || raw.guild_id);
+ const sCp = String(raw['Código Postal'] || raw['CP'] || "").trim();
- const matchGuild = userGuilds.includes(Number(sGuild)) || userGuilds.includes(String(sGuild));
- const matchZone = userZones.some(z => sCp.startsWith(z)); // Coincidencia parcial (ej: zona "11" coge "11204")
+ const matchGuild = userGuilds.includes(Number(sGuild)) || userGuilds.includes(String(sGuild));
+ const matchZone = userZones.some(z => sCp.startsWith(z));
- // Si cuadra (o si el operario no tiene filtros estrictos configurados, se lo enseñamos)
- if((userGuilds.length === 0 || matchGuild) && (userZones.length === 0 || matchZone)) {
- bolsaCount++;
+ if((userGuilds.length === 0 || matchGuild) && (userZones.length === 0 || matchZone)) {
+ bolsaCount++;
+ }
+ });
+
+ if (bolsaCount > 0) {
+ const b2 = document.getElementById('badgeBolsa');
+ b2.innerText = `${bolsaCount} libre${bolsaCount > 1 ? 's' : ''}`;
+ b2.classList.remove('hidden');
}
- });
-
- if (bolsaCount > 0) {
- const b2 = document.getElementById('badgeBolsa');
- b2.innerText = `${bolsaCount} libre${bolsaCount > 1 ? 's' : ''}`;
- b2.classList.remove('hidden');
}
}