Catálogo

'), ]); document.querySelector('header').innerHTML = h; document.querySelector('footer').innerHTML = f; setupHeaderInteractions(); setupFooterInteractions(); } function setupHeaderInteractions() { const btnLogin = document.getElementById('btnLogin'); const btnRegister = document.getElementById('btnRegister'); const btnTheme = document.getElementById('btnTheme'); const modalLogin = document.getElementById('modalLogin'); const modalRegister = document.getElementById('modalRegister'); const modalTheme = document.getElementById('modalTheme'); btnLogin?.addEventListener('click', ()=>modalLogin?.showModal()); btnRegister?.addEventListener('click', ()=>modalRegister?.showModal()); btnTheme?.addEventListener('click', ()=>modalTheme?.showModal()); document.querySelectorAll('form[data-soft]').forEach(f => { f.setAttribute('action', 'send.php'); f.addEventListener('submit', (e)=>{ e.preventDefault(); modalTheme?.showModal(); }); }); } function setupFooterInteractions() { const cookieBanner = document.getElementById('cookieBanner'); const btnCookieAccept = document.getElementById('btnCookieAccept'); const btnCookieClose = document.getElementById('btnCookieClose'); if (localStorage.getItem(LS.cookies) === '1') cookieBanner?.close?.(); else cookieBanner?.showModal?.(); btnCookieAccept?.addEventListener('click', ()=>{ localStorage.setItem(LS.cookies,'1'); cookieBanner?.close?.(); }); btnCookieClose?.addEventListener('click', ()=>{ cookieBanner?.close?.(); }); } async function loadData() { try { DATA = await fetch('./catalog.json').then(r => r.json()); } catch(e) { DATA = []; } const cities = [...new Set(DATA.map(i => i.city))].sort(); const citySel = document.getElementById('city'); cities.forEach(c => { const o = document.createElement('option'); o.value=c; o.textContent=c; citySel.appendChild(o); }); render(); } function getFavs() { try { return JSON.parse(localStorage.getItem(LS.favs) || '[]'); } catch { return []; } } function setFavs(arr) { localStorage.setItem(LS.favs, JSON.stringify(arr)); } function getCart() { try { return JSON.parse(localStorage.getItem(LS.cart) || '[]'); } catch { return []; } } function setCart(arr) { localStorage.setItem(LS.cart, JSON.stringify(arr)); } function makeStars(r) { const full = Math.floor(r); const half = r - full >= 0.5 ? 1 : 0; const empty = 5 - full - half; return '★'.repeat(full) + (half?'☆':'') + '☆'.repeat(empty - (half?1:0)); } function applyFilters() { const q = document.getElementById('q').value.trim().toLowerCase(); const city = document.getElementById('city').value; const type = document.getElementById('type').value; const capMin = parseInt(document.getElementById('capMin').value||'0',10); const priceMax = parseInt(document.getElementById('priceMax').value||'0',10); const dbMax = parseInt(document.getElementById('dbMax').value||'0',10); const onlyAvail = document.getElementById('onlyAvail').checked; let list = [...DATA]; if (q) { list = list.filter(i => i.name.toLowerCase().includes(q) || i.city.toLowerCase().includes(q) || (Array.isArray(i.tags) ? i.tags.join(' ').toLowerCase().includes(q) : false) ); } if (city) list = list.filter(i => i.city === city); if (type) list = list.filter(i => i.type === type); if (capMin) list = list.filter(i => i.capacity >= capMin); if (priceMax) list = list.filter(i => i.hourly_rate <= priceMax); if (dbMax) { const neededIsolation = Math.max(0, 80 - dbMax); list = list.filter(i => i.sound_isolation_db >= neededIsolation); } if (onlyAvail) list = list.filter(i => i.available); const order = document.getElementById('order').value; if (order === 'price_asc') list.sort((a,b)=>a.hourly_rate-b.hourly_rate); if (order === 'price_desc') list.sort((a,b)=>b.hourly_rate-a.hourly_rate); if (order === 'rating_desc') list.sort((a,b)=>b.rating-a.rating); return list; } function render() { const list = applyFilters(); const totalPages = Math.max(1, Math.ceil(list.length / PER_PAGE)); if (page > totalPages) page = totalPages; const slice = list.slice((page-1)*PER_PAGE, page*PER_PAGE); const ul = document.getElementById('list'); const empty = document.getElementById('emptyState'); ul.innerHTML = ''; const favs = getFavs(); if (slice.length === 0) { empty.classList.remove('hidden'); } else { empty.classList.add('hidden'); } slice.forEach(item => { const li = document.createElement('li'); li.className = 'p-4 border border-gray-200 rounded-lg bg-white flex flex-col gap-3'; const isFav = favs.includes(item.id); const tagsHtml = (item.tags||[]).slice(0,3).map(t=>`${t}`).join(' '); li.innerHTML = `

${item.name}

${item.city} Capacidad ${item.capacity} ${item.type === 'sala' ? (item.sound_isolation_db + ' dB aislamiento') : 'Curso'} (${makeStars(item.rating)} ${item.rating.toFixed(1)})
${item.description}
${tagsHtml}
${Number(item.hourly_rate||0).toFixed(2)} €${item.type==='sala'?'/h':'/sesión'}
`; ul.appendChild(li); }); const pag = document.getElementById('pagination'); pag.innerHTML = ''; function btn(n, label = n, disabled = false) { const b = document.createElement('button'); b.textContent = label; b.disabled = disabled; b.className = 'px-3 py-1 rounded-md ' + (n===page?'bg-black text-white':'border border-gray-300'); b.addEventListener('click', ()=>{ page=n; render(); window.scrollTo({top:0, behavior:'smooth'}); }); return b; } pag.appendChild(btn(Math.max(1, page-1), '‹', page===1)); for (let i=1; i<=totalPages; i++) pag.appendChild(btn(i)); pag.appendChild(btn(Math.min(totalPages, page+1), '›', page===totalPages)); document.querySelectorAll('[data-fav]').forEach(b => b.addEventListener('click', (e)=>toggleFav(parseInt(e.currentTarget.getAttribute('data-fav'),10)))); document.querySelectorAll('[data-det]').forEach(b => b.addEventListener('click', (e)=>openDetalle(parseInt(e.currentTarget.getAttribute('data-det'),10)))); document.querySelectorAll('[data-cart]').forEach(b => b.addEventListener('click', (e)=>addCart(parseInt(e.currentTarget.getAttribute('data-cart'),10)))); } function toggleFav(id) { const favs = getFavs(); const i = favs.indexOf(id); if (i>=0) favs.splice(i,1); else favs.push(id); setFavs(favs); render(); const toast = document.createElement('div'); toast.className = 'fixed bottom-4 right-4 bg-black text-white px-4 py-2 rounded-md shadow-lg f4znp'; toast.textContent = i>=0 ? 'Eliminado de favoritos' : 'Añadido a favoritos'; document.body.appendChild(toast); setTimeout(()=>toast.remove(), 1200); } function addCart(id, qty=1) { const cart = getCart(); const existing = cart.find(x=>x.id===id); if (existing) existing.qty += qty; else cart.push({id, qty}); setCart(cart); const d = document.getElementById('modalDetalle'); if (d.open) return; const tmp = document.createElement('div'); tmp.textContent = 'Añadido al carrito'; tmp.className = 'fixed bottom-4 right-4 bg-black text-white px-4 py-2 rounded-md shadow-lg'; document.body.appendChild(tmp); setTimeout(()=>tmp.remove(), 1200); } function openDetalle(id) { const item = DATA.find(i=>i.id===id); if (!item) return; document.getElementById('detName').textContent = item.name; document.getElementById('detCity').textContent = `${item.city} • ${item.address}`; document.getElementById('detDesc').textContent = item.description; const tagsWrap = document.getElementById('detTags'); tagsWrap.innerHTML = (item.tags||[]).map(t=>`${t}`).join(' '); document.getElementById('detMeta').textContent = `Tipo: ${item.type} • Capacidad: ${item.capacity} • Aislamiento: ${item.sound_isolation_db} dB • Tamaño: ${item.size_m2} m²`; document.getElementById('detRating').textContent = `${makeStars(item.rating)} ${item.rating.toFixed(1)} / 5`; const m = document.getElementById('modalDetalle'); const btnFav = document.getElementById('btnDetFav'); const btnCart = document.getElementById('btnDetCart'); const isFav = getFavs().includes(id); btnFav.textContent = isFav ? 'Quitar de favoritos' : 'Añadir a favoritos'; btnFav.onclick = ()=>{ toggleFav(id); const nowFav = getFavs().includes(id); btnFav.textContent = nowFav ? 'Quitar de favoritos' : 'Añadir a favoritos'; }; btnCart.onclick = ()=>addCart(id, 1); m.showModal(); } document.getElementById('filtersForm').addEventListener('submit', (e)=>{ e.preventDefault(); page=1; render(); }); document.getElementById('btnReset').addEventListener('click', ()=>{ document.getElementById('filtersForm').reset(); page=1; render(); }); document.getElementById('btnPerfilNarrador').addEventListener('click', ()=>{ document.getElementById('type').value = 'sala'; document.getElementById('onlyAvail').checked = true; document.getElementById('dbMax').value = 30; document.getElementById('order').value = 'price_asc'; page=1; render(); }); document.getElementById('q').addEventListener('keydown', (e)=>{ if (e.key==='Enter') { e.preventDefault(); page=1; render(); } }); loadPartials(); loadData();