Actualizar menu.html
This commit is contained in:
54
menu.html
54
menu.html
@@ -42,7 +42,6 @@
|
|||||||
<button onclick="logout()" class="flex flex-col items-center justify-center w-12 h-12 bg-white/20 backdrop-blur-md rounded-full border border-white/30 shadow-inner hover:bg-rose-500 hover:border-rose-400 transition-colors active:scale-95" title="Cerrar sesión">
|
<button onclick="logout()" class="flex flex-col items-center justify-center w-12 h-12 bg-white/20 backdrop-blur-md rounded-full border border-white/30 shadow-inner hover:bg-rose-500 hover:border-rose-400 transition-colors active:scale-95" title="Cerrar sesión">
|
||||||
<i data-lucide="log-out" class="w-5 h-5 text-white"></i>
|
<i data-lucide="log-out" class="w-5 h-5 text-white"></i>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
@@ -69,6 +68,9 @@
|
|||||||
|
|
||||||
<a href="calendario.html" class="app-card bg-white p-6 rounded-[2rem] border border-slate-100 flex flex-col items-center text-center active:scale-95 transition-transform relative overflow-hidden">
|
<a href="calendario.html" class="app-card bg-white p-6 rounded-[2rem] border border-slate-100 flex flex-col items-center text-center active:scale-95 transition-transform relative overflow-hidden">
|
||||||
<div class="absolute top-0 right-0 w-16 h-16 bg-blue-50 rounded-bl-full z-0"></div>
|
<div class="absolute top-0 right-0 w-16 h-16 bg-blue-50 rounded-bl-full z-0"></div>
|
||||||
|
|
||||||
|
<div id="badgeChat" class="hidden absolute top-4 left-4 w-6 h-6 bg-red-600 text-white text-[10px] font-black flex items-center justify-center rounded-full shadow-lg z-20 animate-bounce border-2 border-white">0</div>
|
||||||
|
|
||||||
<div class="w-14 h-14 bg-blue-100 text-blue-600 rounded-2xl flex items-center justify-center mb-4 z-10 shadow-inner">
|
<div class="w-14 h-14 bg-blue-100 text-blue-600 rounded-2xl flex items-center justify-center mb-4 z-10 shadow-inner">
|
||||||
<i data-lucide="calendar-days" class="w-7 h-7"></i>
|
<i data-lucide="calendar-days" class="w-7 h-7"></i>
|
||||||
</div>
|
</div>
|
||||||
@@ -83,7 +85,7 @@
|
|||||||
<h3 class="font-black text-slate-800 text-sm leading-tight z-10">Buscar<br>Servicio</h3>
|
<h3 class="font-black text-slate-800 text-sm leading-tight z-10">Buscar<br>Servicio</h3>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a href="ranking.html" class="app-card bg-white p-6 rounded-[2rem] border border-slate-100 flex flex-col items-center text-center active:scale-95 transition-transform relative overflow-hidden col-span-2">
|
<a href="ranking.html" class="app-card bg-white p-6 rounded-[2rem] border border-slate-100 flex flex-col items-center text-center active:scale-95 transition-transform relative overflow-hidden">
|
||||||
<div class="absolute top-0 right-0 w-16 h-16 bg-indigo-50 rounded-bl-full z-0"></div>
|
<div class="absolute top-0 right-0 w-16 h-16 bg-indigo-50 rounded-bl-full z-0"></div>
|
||||||
<div class="w-14 h-14 bg-indigo-100 text-indigo-600 rounded-2xl flex items-center justify-center mb-4 z-10 shadow-inner">
|
<div class="w-14 h-14 bg-indigo-100 text-indigo-600 rounded-2xl flex items-center justify-center mb-4 z-10 shadow-inner">
|
||||||
<i data-lucide="award" class="w-7 h-7"></i>
|
<i data-lucide="award" class="w-7 h-7"></i>
|
||||||
@@ -91,6 +93,14 @@
|
|||||||
<h3 class="font-black text-slate-800 text-sm leading-tight z-10">Ranking<br>y Estadísticas</h3>
|
<h3 class="font-black text-slate-800 text-sm leading-tight z-10">Ranking<br>y Estadísticas</h3>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
|
<a href="configuracion.html" class="app-card bg-white p-6 rounded-[2rem] border border-slate-100 flex flex-col items-center text-center active:scale-95 transition-transform relative overflow-hidden">
|
||||||
|
<div class="absolute top-0 right-0 w-16 h-16 bg-slate-100 rounded-bl-full z-0"></div>
|
||||||
|
<div class="w-14 h-14 bg-slate-100 text-slate-500 rounded-2xl flex items-center justify-center mb-4 z-10 shadow-inner">
|
||||||
|
<i data-lucide="settings" class="w-7 h-7"></i>
|
||||||
|
</div>
|
||||||
|
<h3 class="font-black text-slate-800 text-sm leading-tight z-10">Mi<br>Configuración</h3>
|
||||||
|
</a>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
@@ -103,7 +113,6 @@
|
|||||||
let userGuilds = [];
|
let userGuilds = [];
|
||||||
let systemStatuses = [];
|
let systemStatuses = [];
|
||||||
|
|
||||||
// --- SISTEMA DE TEMA DINÁMICO ---
|
|
||||||
async function applyTheme() {
|
async function applyTheme() {
|
||||||
try {
|
try {
|
||||||
let theme = JSON.parse(localStorage.getItem('app_theme'));
|
let theme = JSON.parse(localStorage.getItem('app_theme'));
|
||||||
@@ -144,8 +153,23 @@
|
|||||||
await loadStatuses();
|
await loadStatuses();
|
||||||
await fetchUserData();
|
await fetchUserData();
|
||||||
fetchBadges();
|
fetchBadges();
|
||||||
|
fetchChatNotifications(); // <-- Nueva función
|
||||||
});
|
});
|
||||||
|
|
||||||
|
async function fetchChatNotifications() {
|
||||||
|
try {
|
||||||
|
const res = await fetch(`${API_URL}/worker/notifications`, {
|
||||||
|
headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` }
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
if (data.ok && data.unreadCount > 0) {
|
||||||
|
const badge = document.getElementById('badgeChat');
|
||||||
|
badge.innerText = data.unreadCount;
|
||||||
|
badge.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
}
|
||||||
|
|
||||||
async function loadStatuses() {
|
async function loadStatuses() {
|
||||||
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")}` } });
|
||||||
@@ -160,8 +184,6 @@
|
|||||||
const data = await res.json();
|
const data = await res.json();
|
||||||
if (data.ok && data.user) {
|
if (data.ok && data.user) {
|
||||||
userZones = Array.isArray(data.user.zones) ? data.user.zones.map(z => z.cps) : [];
|
userZones = Array.isArray(data.user.zones) ? data.user.zones.map(z => z.cps) : [];
|
||||||
|
|
||||||
// Necesitamos cargar los gremios
|
|
||||||
const resG = await fetch(`${API_URL}/admin/users`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } });
|
const resG = await fetch(`${API_URL}/admin/users`, { headers: { "Authorization": `Bearer ${localStorage.getItem("token")}` } });
|
||||||
const dataG = await resG.json();
|
const dataG = await resG.json();
|
||||||
if(dataG.ok) {
|
if(dataG.ok) {
|
||||||
@@ -175,30 +197,19 @@
|
|||||||
async function fetchBadges() {
|
async function fetchBadges() {
|
||||||
try {
|
try {
|
||||||
const headers = { "Authorization": `Bearer ${localStorage.getItem("token")}` };
|
const headers = { "Authorization": `Bearer ${localStorage.getItem("token")}` };
|
||||||
|
|
||||||
// 1. Cargar servicios activos del operario y peticiones
|
|
||||||
const resActive = await fetch(`${API_URL}/services/active`, { headers });
|
const resActive = await fetch(`${API_URL}/services/active`, { headers });
|
||||||
const dataActive = await resActive.json();
|
const dataActive = await resActive.json();
|
||||||
|
|
||||||
const resReqs = await fetch(`${API_URL}/agenda/requests`, { headers });
|
const resReqs = await fetch(`${API_URL}/agenda/requests`, { headers });
|
||||||
const dataReqs = await resReqs.json();
|
const dataReqs = await resReqs.json();
|
||||||
|
|
||||||
if (dataActive.ok && dataReqs.ok) {
|
if (dataActive.ok && dataReqs.ok) {
|
||||||
let sinCitaCount = 0;
|
let sinCitaCount = 0;
|
||||||
|
|
||||||
// Sumamos las peticiones pendientes
|
|
||||||
sinCitaCount += dataReqs.requests.length;
|
sinCitaCount += dataReqs.requests.length;
|
||||||
|
|
||||||
// Filtramos y contamos la lista normal
|
|
||||||
dataActive.services.forEach(s => {
|
dataActive.services.forEach(s => {
|
||||||
if (s.provider === 'SYSTEM_BLOCK') return;
|
if (s.provider === 'SYSTEM_BLOCK') return;
|
||||||
|
|
||||||
const raw = s.raw_data || {};
|
const raw = s.raw_data || {};
|
||||||
if (raw.appointment_status === 'pending') return; // Ya las contamos arriba
|
if (raw.appointment_status === 'pending') return;
|
||||||
|
|
||||||
// Si no tiene fecha, lo evaluamos
|
|
||||||
if (!raw.scheduled_date || raw.scheduled_date.trim() === "") {
|
if (!raw.scheduled_date || raw.scheduled_date.trim() === "") {
|
||||||
// Validar que el estado no sea finalizado/anulado (ej: "Terminado sin fecha")
|
|
||||||
if (raw.status_operativo) {
|
if (raw.status_operativo) {
|
||||||
const st = systemStatuses.find(x => String(x.id) === String(raw.status_operativo));
|
const st = systemStatuses.find(x => String(x.id) === String(raw.status_operativo));
|
||||||
if (st && st.is_final) return;
|
if (st && st.is_final) return;
|
||||||
@@ -206,7 +217,6 @@
|
|||||||
sinCitaCount++;
|
sinCitaCount++;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (sinCitaCount > 0) {
|
if (sinCitaCount > 0) {
|
||||||
const b1 = document.getElementById('badgeAsignados');
|
const b1 = document.getElementById('badgeAsignados');
|
||||||
b1.innerText = `${sinCitaCount} pendiente${sinCitaCount > 1 ? 's' : ''}`;
|
b1.innerText = `${sinCitaCount} pendiente${sinCitaCount > 1 ? 's' : ''}`;
|
||||||
@@ -214,31 +224,24 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. Pedimos la "Bolsa de Trabajo" SOLO SI TIENE PERMISO (operario_cerrado NO cuenta bolsa)
|
|
||||||
if (localStorage.getItem("role") === 'operario') {
|
if (localStorage.getItem("role") === 'operario') {
|
||||||
const resScraped = await fetch(`${API_URL}/providers/scraped`, { headers });
|
const resScraped = await fetch(`${API_URL}/providers/scraped`, { headers });
|
||||||
const dataScraped = await resScraped.json();
|
const dataScraped = await resScraped.json();
|
||||||
|
|
||||||
if (dataScraped.ok) {
|
if (dataScraped.ok) {
|
||||||
let bolsaCount = 0;
|
let bolsaCount = 0;
|
||||||
dataScraped.services.forEach(s => {
|
dataScraped.services.forEach(s => {
|
||||||
if(s.assigned_to || s.provider === 'SYSTEM_BLOCK') return;
|
if(s.assigned_to || s.provider === 'SYSTEM_BLOCK') return;
|
||||||
|
|
||||||
const raw = s.raw_data || {};
|
const raw = s.raw_data || {};
|
||||||
const dbStatus = raw.status_operativo;
|
const dbStatus = raw.status_operativo;
|
||||||
if(s.status === 'archived' || (dbStatus && ['anulado', 'terminado', 'finalizado'].includes(dbStatus.toLowerCase()))) return;
|
if(s.status === 'archived' || (dbStatus && ['anulado', 'terminado', 'finalizado'].includes(dbStatus.toLowerCase()))) return;
|
||||||
|
|
||||||
const sGuild = String(s.guild_id || raw.guild_id);
|
const sGuild = String(s.guild_id || raw.guild_id);
|
||||||
const sCp = String(raw['Código Postal'] || raw['CP'] || "").trim();
|
const sCp = String(raw['Código Postal'] || raw['CP'] || "").trim();
|
||||||
|
|
||||||
const matchGuild = userGuilds.includes(Number(sGuild)) || userGuilds.includes(String(sGuild));
|
const matchGuild = userGuilds.includes(Number(sGuild)) || userGuilds.includes(String(sGuild));
|
||||||
const matchZone = userZones.some(z => sCp.startsWith(z));
|
const matchZone = userZones.some(z => sCp.startsWith(z));
|
||||||
|
|
||||||
if((userGuilds.length === 0 || matchGuild) && (userZones.length === 0 || matchZone)) {
|
if((userGuilds.length === 0 || matchGuild) && (userZones.length === 0 || matchZone)) {
|
||||||
bolsaCount++;
|
bolsaCount++;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (bolsaCount > 0) {
|
if (bolsaCount > 0) {
|
||||||
const b2 = document.getElementById('badgeBolsa');
|
const b2 = document.getElementById('badgeBolsa');
|
||||||
b2.innerText = `${bolsaCount} libre${bolsaCount > 1 ? 's' : ''}`;
|
b2.innerText = `${bolsaCount} libre${bolsaCount > 1 ? 's' : ''}`;
|
||||||
@@ -246,7 +249,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (e) { console.log("Error cargando badges"); }
|
} catch (e) { console.log("Error cargando badges"); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user