Entrar

Crear cuenta

Tema del sitio

El modo claro es el predeterminado. altfloweaglebay.pro

Catálogo

Encuentra el servicio ideal. Atajos: / buscar, f favoritos.

/

Filtrar

Categoría:
Duración: min
Disponibilidad:
Valor: CLP
')) ]); document.querySelector('header').innerHTML = h; document.querySelector('footer').innerHTML = f; initHeaderInteractions(); initFooterInteractions(); }catch(e){ console.error('Error injectPartials', e); } } function applyStoredTheme(){ const t = localStorage.getItem('theme')||'light'; document.documentElement.classList.toggle('dark', t==='dark'); } async function loadData(){ const grid = document.getElementById('grid'); try{ const res = await fetch('./catalog.json', {cache:'no-store'}); if(!res.ok) throw new Error('No se pudo cargar el catálogo'); data = await res.json(); const cats = [...new Set(data.map(d=>d.category))].sort(); const catSel = document.getElementById('f-category'); cats.forEach(c=>{ const o = document.createElement('option'); o.value = c; o.textContent = c; catSel.appendChild(o); }); const tagSet = new Set(data.flatMap(d=>d.tags)); const tagWrap = document.getElementById('f-tags'); [...tagSet].sort().forEach(t=>{ const b = document.createElement('button'); b.type='button'; b.textContent = t; b.className='text-sm rounded-full border border-slate-200 dark:border-slate-700 px-3 py-1 hover:bg-slate-50 dark:hover:bg-slate-800'; b.addEventListener('click', ()=>{ if(state.tags.has(t)) state.tags.delete(t); else state.tags.add(t); b.classList.toggle('bg-slate-900'); b.classList.toggle('text-white'); b.classList.toggle('dark:text-slate-100'); b.classList.toggle('border-slate-900'); page=1; render(); }); tagWrap.appendChild(b); }); render(); }catch(err){ grid.innerHTML = '
Error al cargar el catálogo. Intenta nuevamente más tarde.
'; console.error(err); } } function filter(){ const q = state.q.toLowerCase().trim(); const isFav = id=> favIds.includes(id); return data.filter(d=>{ const inQ = !q || d.title.toLowerCase().includes(q) || d.tags.some(t=>t.toLowerCase().includes(q)); const inCat = !state.cat || d.category===state.cat; const inMin = state.min==null || d.priceCLP>=state.min; const inMax = state.max==null || d.priceCLP<=state.max; const inTags = state.tags.size===0 || [...state.tags].every(t=> d.tags.includes(t)); const inFeat = !state.featured || d.featured; const inFavs = !state.onlyFavs || isFav(d.id); return inQ && inCat && inMin && inMax && inTags && inFeat && inFavs; }); } function render(){ filtered = filter(); const grid = document.getElementById('grid'); const count = document.getElementById('results-count'); count.textContent = filtered.length ? `Resultados: ${filtered.length}` : 'Sin resultados con los filtros actuales'; grid.innerHTML = ''; const start = (page-1)*perPage; const items = filtered.slice(start, start+perPage); if(items.length===0){ const empty = document.createElement('div'); empty.className = 'col-span-full p-10 text-center rounded-xl border border-slate-200 dark:border-slate-800'; empty.innerHTML = `
😕

No encontramos servicios que coincidan.

`; grid.appendChild(empty); empty.querySelector('#clear-filters').addEventListener('click', resetFiltersUI); }else{ items.forEach(it=>{ const card = document.createElement('div'); card.className='rounded-xl border border-slate-200 dark:border-slate-800 overflow-hidden group bg-white dark:bg-slate-900'; const outStock = it.stock<=0; card.innerHTML = `
${it.title} ${outStock?'Sin stock':''} ${it.featured?'Destacado':''}

${it.title}

${it.description}

${CLP(it.priceCLP)} CLP
`; grid.appendChild(card); }); } bindCardButtons(); renderPagination(); } function renderPagination(){ const total = Math.ceil(filtered.length/perPage)||1; page = Math.min(page, total); const pag = document.getElementById('pagination'); pag.innerHTML=''; if(total<=1) return; const prev = document.createElement('button'); prev.textContent = 'Anterior'; prev.disabled = page===1; prev.className = 'rounded-md px-3 py-1 border border-slate-200 dark:border-slate-700 '+(prev.disabled?'text-slate-400 cursor-not-allowed':'hover:bg-slate-50 dark:hover:bg-slate-800'); prev.addEventListener('click', ()=>{ if(page>1){ page--; render(); window.scrollTo({top:0, behavior:'smooth'}); }}); pag.appendChild(prev); for(let i=1;i<=total;i++){ const b = document.createElement('button'); b.textContent = i; b.className = 'rounded-md px-3 py-1 border border-slate-200 dark:border-slate-700 ' + (i===page? 'bg-slate-900 text-white border-slate-900 dark:bg-slate-100 dark:text-slate-900 dark:border-slate-100':'hover:bg-slate-50 dark:hover:bg-slate-800'); b.addEventListener('click', ()=>{ page=i; render(); window.scrollTo({top:0, behavior:'smooth'}); }); pag.appendChild(b); } const next = document.createElement('button'); next.textContent = 'Siguiente'; next.disabled = page===total; next.className = 'rounded-md px-3 py-1 border border-slate-200 dark:border-slate-700 '+(next.disabled?'text-slate-400 cursor-not-allowed':'hover:bg-slate-50 dark:hover:bg-slate-800'); next.addEventListener('click', ()=>{ if(page{ btn.addEventListener('click', ()=>{ const it = data.find(d=>d.id===btn.getAttribute('data-id')); openDetail(it); }); }); document.querySelectorAll('.fav-btn').forEach(btn=>{ btn.addEventListener('click', ()=>{ const id = btn.getAttribute('data-id'); const idx = favIds.indexOf(id); if(idx>-1) favIds.splice(idx,1); else favIds.push(id); localStorage.setItem('favIds', JSON.stringify(favIds)); btn.textContent = favIds.includes(id)?'★':'☆'; }); }); document.querySelectorAll('.add-cart').forEach(btn=>{ btn.addEventListener('click', ()=>{ const id = btn.getAttribute('data-id'); const item = data.find(d=>d.id===id); if(item && item.stock<=0){ return; } cart[id] = (cart[id]||0)+1; localStorage.setItem('cartItems', JSON.stringify(cart)); toast('Agregado al carro'); }); }); } function openDetail(it){ const dlg = document.getElementById('modal-detail'); const img = document.getElementById('detail-image'); const thumbs = document.getElementById('detail-thumbs'); document.getElementById('detail-title').textContent = it.title; document.getElementById('detail-desc').textContent = it.description; document.getElementById('detail-cat').textContent = it.category; document.getElementById('detail-dur').textContent = it.durationMinutes; document.getElementById('detail-av').textContent = it.availability; document.getElementById('detail-price').textContent = CLP(it.priceCLP); img.src = it.images[0]; img.alt = it.title; thumbs.innerHTML=''; it.images.forEach((src, idx)=>{ const b = document.createElement('button'); b.className='rounded border border-slate-200 dark:border-slate-700 bg-white/90 dark:bg-slate-900/80'; b.innerHTML = `${it.title} ${idx+1}`; b.addEventListener('click', ()=>{ img.src=src; }); thumbs.appendChild(b); }); const favBtn = document.getElementById('btn-detail-fav'); favBtn.textContent = favIds.includes(it.id)?'Quitar de favoritos':'Añadir a favoritos'; favBtn.onclick = ()=>{ const id = it.id; const i = favIds.indexOf(id); if(i>-1) favIds.splice(i,1); else favIds.push(id); localStorage.setItem('favIds', JSON.stringify(favIds)); favBtn.textContent = favIds.includes(id)?'Quitar de favoritos':'Añadir a favoritos'; render(); }; const cartBtn = document.getElementById('btn-detail-cart'); cartBtn.onclick = ()=>{ if(it.stock<=0) return; cart[it.id] = (cart[it.id]||0)+1; localStorage.setItem('cartItems', JSON.stringify(cart)); toast('Agregado al carro'); }; dlg.showModal(); } function bindUI(){ const drawer = document.getElementById('drawer-filters'); const detail = document.getElementById('modal-detail'); document.getElementById('btn-filter-drawer').addEventListener('click', ()=> drawer.showModal()); drawer.querySelectorAll('[data-close-modal]').forEach(b=> b.addEventListener('click', ()=> drawer.close())); detail.querySelectorAll('[data-close-modal]').forEach(b=> b.addEventListener('click', ()=> detail.close())); bindDialogBackdropClose(drawer); bindDialogBackdropClose(detail); const search = document.getElementById('search'); const isTypingEl = el => el && (el.tagName==='INPUT' || el.tagName==='TEXTAREA' || el.isContentEditable); search.addEventListener('input', ()=>{ state.q = search.value; page=1; render(); }); document.addEventListener('keydown', e=>{ if(e.key==='/' && !isTypingEl(document.activeElement)){ e.preventDefault(); search.focus();} if(e.key==='f' && !isTypingEl(document.activeElement)){ state.onlyFavs = !state.onlyFavs; const favCb=document.getElementById('f-favs'); favCb.checked=state.onlyFavs; page=1; render(); } }); document.getElementById('f-category').addEventListener('change', e=>{ state.cat=e.target.value; page=1; render(); }); document.getElementById('f-min').addEventListener('input', e=>{ state.min = e.target.value? Number(e.target.value):null; page=1; render(); }); document.getElementById('f-max').addEventListener('input', e=>{ state.max = e.target.value? Number(e.target.value):null; page=1; render(); }); document.getElementById('f-featured').addEventListener('change', e=>{ state.featured=e.target.checked; page=1; render(); }); document.getElementById('f-favs').addEventListener('change', e=>{ state.onlyFavs=e.target.checked; page=1; render(); }); document.getElementById('btn-reset').addEventListener('click', resetFiltersUI); } function resetFiltersUI(){ state = { q:'', cat:'', min:null, max:null, tags:new Set(), featured:false, onlyFavs:false }; const form = document.getElementById('filters'); form.reset(); document.getElementById('search').value=''; document.querySelectorAll('#f-tags > button').forEach(b=>{ b.classList.remove('bg-slate-900','text-white','border-slate-900'); }); page=1; render(); } function bindDialogBackdropClose(dlg){ dlg.addEventListener('click', (e)=>{ const rect = dlg.getBoundingClientRect(); const inDialog = e.clientX >= rect.left && e.clientX <= rect.right && e.clientY >= rect.top && e.clientY <= rect.bottom; if(!inDialog) dlg.close(); }); } function initHeaderInteractions(){ const openLogin = document.querySelector('[data-open-login]'); const openSignup = document.querySelector('[data-open-signup]'); const modalLogin = document.getElementById('modal-login'); const modalSignup = document.getElementById('modal-signup'); const themeBtn = document.querySelector('[data-open-theme]'); const themeModal = document.getElementById('modal-theme'); const closeButtons = document.querySelectorAll('[data-close-modal]'); [openLogin, openSignup, themeBtn].forEach(btn=>{ if(!btn) return; btn.addEventListener('click', ()=>{ const target = btn.getAttribute('data-open-login')!==null ? modalLogin : btn.getAttribute('data-open-signup')!==null ? modalSignup : themeModal; if(target){ target.showModal(); } }); }); closeButtons.forEach(btn=>{ btn.addEventListener('click', ()=> { const dlg = btn.closest('dialog'); if(dlg) dlg.close(); }); }); document.querySelectorAll('[data-theme-choice]').forEach(el=>{ el.addEventListener('click', ()=>{ const mode = el.getAttribute('data-theme-choice'); localStorage.setItem('theme', mode); document.documentElement.classList.toggle('dark', mode==='dark'); }); }); } function initFooterInteractions(){ const banner = document.getElementById('cookie-banner'); const accept = document.getElementById('cookie-accept'); if(!banner || !accept) return; if(localStorage.getItem('cookieConsent')==='yes'){ banner.classList.add('hidden'); } else banner.classList.remove('hidden'); accept.addEventListener('click', ()=>{ localStorage.setItem('cookieConsent','yes'); banner.classList.add('hidden'); }); } function toast(msg){ let t = document.getElementById('toast'); if(!t){ t = document.createElement('div'); t.id = 'toast'; t.className = 'fixed left-1/2 -translate-x-1/2 bottom-6 z-50 bg-slate-900 text-white dark:bg-slate-100 dark:text-slate-900 px-4 py-2 rounded-full shadow-lg'; document.body.appendChild(t); } t.textContent = msg; t.style.opacity = '1'; t.style.transition = 'opacity .3s ease'; setTimeout(()=>{ t.style.opacity='0'; }, 1600); } applyStoredTheme(); injectPartials().then(()=>{ bindUI(); loadData(); });