Catálogo
No hay resultados. Ajusta los filtros o intenta otra búsqueda.
'),
]);
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();