1// ============================================= 2// main.js â Fast Chicken AQP 3// ============================================= 4 5let whatsappNumber = DEFAULT_WHATSAPP; 6let msgPedido = '¡Hola! Quisiera hacer un pedido ð'; 7let msgDelivery = 'Quiero pedir un delivery ð'; 8let msgItem = '¡Hola! Quisiera pedir: *{plato}* (S/ {precio}) ð'; 9let msgReserva = 'ð *RESERVA â Fast Chicken AQP*\n\nð¤ Nombre: {nombre}\nð± Teléfono: {telefono}\nð Fecha: {fecha}\nð¥ Personas: {personas}\nð¬ Mensaje: {mensaje}'; 10let msgTrabajo = 'Hola, me gustarÃa postular para trabajar en Fast Chicken AQP ð'; 11let allMenuItems = [], slides = [], currentSlide = 0, sliderTimer = null; 12 13// ââ INIT ââââââââââââââââââââââââââââââââââââââââââââââ 14document.addEventListener('DOMContentLoaded', async () => { 15 initNavbar(); 16 initScrollReveal(); 17 await Promise.all([initSlider(), loadConfig(), loadMenu(), loadNosotrosPublic()]); 18 loadPromoPopup(); 19}); 20 21// ââ NAVBAR ââââââââââââââââââââââââââââââââââââââââââââ 22function initNavbar() { 23 const btn = document.getElementById('hamburger'); 24 const links = document.getElementById('navLinks'); 25 if (btn && links) { 26 btn.addEventListener('click', () => links.classList.toggle('open')); 27 links.querySelectorAll('a').forEach(a => 28 a.addEventListener('click', () => links.classList.remove('open')) 29 ); 30 } 31} 32 33// ââ SCROLL REVEAL âââââââââââââââââââââââââââââââââââââ 34function initScrollReveal() { 35 const obs = new IntersectionObserver( 36 entries => entries.forEach(e => { if (e.isIntersecting) e.target.classList.add('visible'); }), 37 { threshold: 0.1 } 38 ); 39 document.querySelectorAll('.reveal').forEach(el => obs.observe(el)); 40} 41 42// ââ HERO SLIDER âââââââââââââââââââââââââââââââââââââââ 43async function initSlider() { 44 try { 45 const { data } = await _supabase.from('campaigns').select('*') 46 .eq('is_active', true).order('created_at', { ascending: false }).limit(5); 47 if (data && data.length) { 48 slides = data; renderDots(); showSlide(0); 49 if (slides.length > 1) sliderTimer = setInterval(() => nextSlide(), 5500); 50 } 51 } catch(_) {} 52} 53 54function renderDots() { 55 const dots = document.getElementById('heroDots'); 56 if (!dots || slides.length <= 1) return; 57 dots.innerHTML = slides.map((_, i) => 58 `<button class="hero-dot ${i===0?'active':''}" onclick="goSlide(${i})"></button>` 59 ).join(''); 60} 61 62function showSlide(idx) { 63 if (!slides.length) return; 64 currentSlide = (idx + slides.length) % slides.length; 65 const s = slides[currentSlide]; 66 const bg = document.getElementById('heroBg'); if (bg && s.hero_image_url) bg.src = s.hero_image_url; 67 const badge = document.getElementById('heroBadge'); if (badge && s.name) badge.textContent = 'ð¥ ' + s.name; 68 const title = document.getElementById('heroTitle'); if (title && s.hero_title) title.innerHTML = s.hero_title; 69 const sub = document.getElementById('heroSub'); if (sub && s.hero_subtitle) sub.textContent = s.hero_subtitle; 70 const cta = document.getElementById('heroCtaBtn');if (cta && s.hero_cta_text) cta.textContent = 'ð ' + s.hero_cta_text; 71 document.querySelectorAll('.hero-dot').forEach((d,i) => d.classList.toggle('active', i === currentSlide)); 72} 73 74function nextSlide() { showSlide(currentSlide + 1); } 75function prevSlide() { showSlide(currentSlide - 1); } 76function goSlide(i) { clearInterval(sliderTimer); showSlide(i); sliderTimer = setInterval(() => nextSlide(), 5500); } 77 78// ââ CONFIG ââââââââââââââââââââââââââââââââââââââââââââ 79async function loadConfig() { 80 try { 81 const { data } = await _supabase.from('site_config').select('*'); 82 if (!data) return; 83 data.forEach(r => { 84 if (!r.key || r.value == null) return; 85 const v = r.value; 86 switch (r.key) { 87 case 'whatsapp': 88 whatsappNumber = v; 89 const wp = document.getElementById('contactWhatsapp'); 90 if (wp) wp.textContent = '+' + v; 91 break; 92 case 'logo_url': 93 const li = document.getElementById('navLogoImg'); 94 const le = document.getElementById('navLogoEmoji'); 95 if (li) { li.src = v; li.style.display = 'block'; } 96 if (le) le.style.display = 'none'; 97 break; 98 case 'msg_pedido': msgPedido = v; break; 99 case 'msg_delivery': msgDelivery = v; break; 100 case 'msg_item': msgItem = v; break; 101 case 'msg_reserva': msgReserva = v; break; 102 case 'msg_trabajo': msgTrabajo = v; break; 103 case 'address_1': 104 document.querySelectorAll('.contact-address').forEach(el => el.textContent = v); 105 break; 106 case 'hours': 107 document.querySelectorAll('.contact-hours').forEach(el => el.textContent = v); 108 break; 109 case 'tiktok': 110 document.querySelectorAll('a[href*="tiktok.com"]').forEach(a => a.href = v); 111 break; 112 case 'maps_query': 113 const frame = document.getElementById('siteMapFrame'); 114 if (frame) frame.src = 'https://maps.google.com/maps?q=' + encodeURIComponent(v) + '&output=embed&z=16'; 115 break; 116 } 117 }); 118 } catch(_) {} 119} 120 121// ââ MENà ââââââââââââââââââââââââââââââââââââââââââââââ 122async function loadMenu() { 123 try { 124 const { data, error } = await _supabase.from('menu_items').select('*') 125 .eq('available', true).order('sort_order', { ascending: true }); 126 if (error || !data || !data.length) { renderMenuFallback(); return; } 127 allMenuItems = data; 128 renderMenuTabs([...new Set(data.map(i => i.category))]); 129 renderMenuItems(data); 130 } catch(_) { renderMenuFallback(); } 131} 132 133function renderMenuTabs(cats) { 134 const el = document.getElementById('menuTabs'); 135 if (!el) return; 136 el.innerHTML = `<button class="menu-tab active" data-cat="all" onclick="filterMenu('all',this)">Todos</button>` + 137 cats.map(c => `<button class="menu-tab" data-cat="${c}" onclick="filterMenu('${c}',this)">${c}</button>`).join(''); 138} 139 140function filterMenu(cat, btn) { 141 document.querySelectorAll('.menu-tab').forEach(t => t.classList.remove('active')); 142 btn.classList.add('active'); 143 renderMenuItems(cat === 'all' ? allMenuItems : allMenuItems.filter(i => i.category === cat)); 144} 145 146function renderMenuItems(items) { 147 const grid = document.getElementById('menuGrid'); 148 if (!grid) return; 149 if (!items.length) { grid.innerHTML = `<p class="menu-empty">No hay platos en esta categorÃa</p>`; return; } 150 const waIcon = `<svg viewBox="0 0 24 24" style="width:14px;
150height:14px;fill:white;flex-shrink:0"><path d="M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413z"/></svg>`; 151 grid.innerHTML = items.map(item => ` 152 <div class="menu-card"> 153 ${item.image_url ? `<img class="menu-card-img" src="${item.image_url}" alt="${item.name}" loading="lazy">` : `<div class="menu-card-img-emoji">${item.emoji || 'ð'}</div>`} 154 <div class="menu-card-body"> 155 <div class="menu-card-cat">${item.category}</div> 156 <div class="menu-card-name">${item.name}</div> 157 <div class="menu-card-desc">${item.description || ''}</div> 158 <div class="menu-card-footer"> 159 <div class="menu-card-price"><small>S/</small> ${parseFloat(item.price).toFixed(2)}</div> 160 <button class="btn-agregar" onclick="pedirItem('${item.name}', ${item.price})">${waIcon} Pedir</button> 161 </div> 162 </div> 163 </div>`).join(''); 164} 165 166function renderMenuFallback() { 167 const d = [ 168 {category:'Pollos a la Brasa',name:'Pollo Entero', description:'Con papas fritas, ensalada y salsas',price:65,emoji:'ð'}, 169 {category:'Pollos a la Brasa',name:'1/2 Pollo', description:'Media pieza con papas y ensalada', price:38,emoji:'ð'}, 170 {category:'Pollos a la Brasa',name:'1/4 Pollo', description:'Un cuarto con guarnición', price:22,emoji:'ð'}, 171 {category:'Combos', name:'Combo Familiar', description:'1 Pollo + 4 gaseosas + papas', price:85,emoji:'ð'}, 172 {category:'Combos', name:'Combo Pareja', description:'1/2 Pollo + 2 gaseosas + papas', price:49,emoji:'ð'}, 173 {category:'Entradas', name:'Anticuchos', description:'4 brochetas con choclo y papa', price:18,emoji:'ð¥'}, 174 {category:'Entradas', name:'Tequeños', description:'6 unidades con dip de huancaÃna', price:14,emoji:'ð§'}, 175 {category:'Bebidas', name:'Gaseosa Personal',description:'Coca-Cola, Inca Kola â 500ml', price: 5,emoji:'ð¥¤'}, 176 {category:'Bebidas', name:'Chicha Morada', description:'Preparada en casa â 1 litro', price: 9,emoji:'ð«'}, 177 {category:'Postres', name:'Tres Leches', description:'Pastel tres leches artesanal', price:12,emoji:'ð°'}, 178 ]; 179 allMenuItems = d; 180 renderMenuTabs([...new Set(d.map(i => i.category))]); 181 renderMenuItems(d); 182} 183 184function filterAndScroll(cat) { 185 document.getElementById('carta').scrollIntoView({ behavior: 'smooth' }); 186 setTimeout(() => { 187 const btn = [...document.querySelectorAll('.menu-tab')].find(b => b.dataset.cat === cat); 188 if (btn) btn.click(); 189 }, 700); 190} 191 192// ââ WHATSAPP ââââââââââââââââââââââââââââââââââââââââââ 193function openDelivery() { 194 window.open(`https://wa.me/${whatsappNumber}?text=${encodeURIComponent(msgDelivery)}`, '_blank'); 195} 196function openWhatsApp(msg) { 197 window.open(`https://wa.me/${whatsappNumber}?text=${encodeURIComponent(msg || msgPedido)}`, '_blank'); 198} 199function pedirItem(name, price) { 200 openWhatsApp(msgItem.replace('{plato}', name).replace('{precio}', parseFloat(price).toFixed(2))); 201} 202 203// ââ RESERVA âââââââââââââââââââââââââââââââââââââââââââ 204function sendReservation() { 205 const name = document.getElementById('formName').value.trim(); 206 const phone = document.getElementById('formPhone').value.trim(); 207 const date = document.getElementById('formDate').value; 208 const persons = document.getElementById('formPersons').value; 209 const msg = document.getElementById('formMessage').value.trim(); 210 if (!name || !phone) { alert('Por favor ingresa tu nombre y teléfono'); return; } 211 openWhatsApp( 212 msgReserva 213 .replace('{nombre}', name).replace('{telefono}', phone) 214 .replace('{fecha}
214', date || 'Por coordinar').replace('{personas}', persons) 215 .replace('{mensaje}', msg || 'Sin mensaje adicional') 216 ); 217} 218 219// ââ NOSOTROS ââââââââââââââââââââââââââââââââââââââââââ 220async function loadNosotrosPublic() { 221 try { 222 const { data } = await _supabase.from('site_config').select('*'); 223 if (!data) return; 224 const cfg = {}; 225 data.forEach(r => { if (r.key) cfg[r.key] = r.value; }); 226 227 const set = (id, val) => { const el = document.getElementById(id); if (el && val) el.textContent = val; }; 228 set('nosotrosIntro1', cfg.nosotros_intro_1); 229 set('nosotrosIntro2', cfg.nosotros_intro_2); 230 set('statAnios', cfg.nosotros_stat_anios); 231 set('statLocales', cfg.nosotros_stat_locales); 232 set('statClientes', cfg.nosotros_stat_clientes); 233 234 let premios = []; 235 try { premios = JSON.parse(cfg.nosotros_premios || '[]'); } catch(_) {} 236 237 const valores = (cfg.nosotros_valores || 'Verdad\nLealtad\nResponsabilidad\nRespeto\nCooperación\nComunicación') 238 .split('\n').filter(v => v.trim()); 239 240 const mkBody = (text, ph) => !text || !text.trim() 241 ? `<p class="accordion-placeholder">${ph}</p>` 242 : text.trim().split('\n').map(p => p.trim() ? `<p>${p}</p>` : '').join(''); 243 244 const waNum = cfg.whatsapp || DEFAULT_WHATSAPP; 245 246 const items = [ 247 { id:'acc-historia', icon:'ð', label:'Historia', content: mkBody(cfg.nosotros_historia, 'âï¸ Próximamente â historia de la marca.') }, 248 { id:'acc-vision', icon:'ð¯', label:'Visión', content: mkBody(cfg.nosotros_vision, 'âï¸ Próximamente.') }, 249 { id:'acc-mision', icon:'ð', label:'Misión', content: mkBody(cfg.nosotros_mision, 'âï¸ Próximamente.') }, 250 { id:'acc-valores', icon:'ð', label:'Valores', content: `<ul>${valores.map(v => `<li>${v.trim()}</li>`).join('')}</ul>` }, 251 { id:'acc-cert', icon:'ð ', label:'Certificaciones', content: mkBody(cfg.nosotros_certificaciones,'âï¸ Próximamente.') }, 252 { id:'acc-premios', icon:'ð', label:'Premios y Reconocimientos', 253 content: premios.length === 0 254 ? `<p class="accordion-placeholder">âï¸ Próximamente â premios obtenidos.</p>` 255 : premios.map(p => `<div style="display:flex;gap:12px;align-items:flex-start;padding:10px 0;border-bottom:1px solid var(--border)"> 256 <span style="font-size:1.6rem;flex-shrink:0">${p.emoji||'ð'}</span> 257 <div> 258 <div style="font-weight:900;color:var(--text);font-size:0.92rem">${p.titulo}</div> 259 ${p.descripcion?`<div style="color:var(--gray);font-size:0.82rem;margin-top:3px">${p.descripcion}</div>`:''} 260 ${p.fecha?`<div style="color:var(--red);font-size:0.75rem;font-weight:700;margin-top:3px">ð ${p.fecha}</div>`:''} 261 </div></div>`).join('') 262 }, 263 { id:'acc-llamanos', icon:'ð', label:'Llámanos', 264 content: `<p style="font-size:1.3rem;font-weight:900;color:var(--text);margin-bottom:8px">ð± ${waNum.replace('51','')}</p> 265 <p>Número oficial de Fast Chicken AQP.</p> 266 <a href="https://wa.me/${waNum}?text=${encodeURIComponent(msgPedido)}" target="_blank" class="contact-link">ð¬ WhatsApp</a>` 267 }, 268 { id:'acc-encuesta', icon:'ð', label:'Encuesta de Satisfacción', 269 content: cfg.nosotros_encuesta_url && cfg.nosotros_encuesta_url.startsWith('http') 270 ? `<p>Tu opinión nos ayuda a mejorar.</p><a href="${cfg.nosotros_encuesta_url}" target="_blank" class="contact-link">ð Completar Encuesta</a>` 271 : `<p>Tu opinión nos ayuda a mejorar.</p><a href="#contacto" class="survey-link" onclick="document.getElementById('contacto').scrollIntoView({behavior:'smooth'})">ð Ir al formulario</a>` 272 }, 273 { id:'acc-trabajo', icon:'ð', label:'Trabaja con Nosotros', wide: true, 274 content: `<p>¿Te interesa ser parte del equipo? EnvÃanos tu CV.</p> 275 <a href="https://wa.me/${waNum}?text=${encodeURIComponent(msgTrabajo)}" target="_blank" class="contact-link">ð¨ Postular por WhatsApp</a>` 276 }, 277 ]; 278 279 const grid = document.getElementById('accordionGrid'); 280 if (!grid) return; 281 grid.innerHTML = items.map(item => ` 282 <div class="accordion-item" id="${item.id}" ${item.wide?'style="grid-column:1/-1"':''}> 283 <div class="accordion-header" onclick="toggleAccordion('${item.id}')"> 284 <div class="accordion-header-left">
285 <span class="accordion-icon">${item.icon}</span> 286 <span class="accordion-label">${item.label}</span> 287 </div> 288 <div class="accordion-arrow">â¼</div> 289 </div> 290 <div class="accordion-body">${item.content}</div> 291 </div>`).join(''); 292 } catch(e) { console.error('nosotros:', e); } 293} 294 295// ââ POPUP âââââââââââââââââââââââââââââââââââââââââââââ 296async function loadPromoPopup() { 297 try { 298 const { data } = await _supabase.from('site_config').select('value') 299 .eq('key', 'popup_config').maybeSingle(); 300 if (!data || !data.value) return; 301 302 const cfg = typeof data.value === 'string' ? JSON.parse(data.value) : data.value; 303 if (!cfg.active || !cfg.image_url) return; 304 305 // Frecuencia 306 const KEY = 'fca_popup'; 307 const freq = cfg.frequency || 'session'; 308 if (freq === 'session' && sessionStorage.getItem(KEY)) return; 309 if (freq === 'daily') { 310 const last = localStorage.getItem(KEY); 311 if (last && Date.now() - parseInt(last) < 86400000) return; 312 } 313 314 const popup = document.getElementById('promoPopup'); 315 const img = document.getElementById('popupImg'); 316 if (!popup || !img) return; 317 318 img.src = cfg.image_url; 319 const delay = (cfg.delay_ms != null ? cfg.delay_ms : 1000); 320 321 setTimeout(() => { 322 popup.classList.add('open'); 323 // Registrar AQUÃ, cuando el popup realmente se muestra 324 if (freq === 'session') sessionStorage.setItem(KEY, '1'); 325 if (freq === 'daily') localStorage.setItem(KEY, String(Date.now())); 326 if (cfg.duracion_s > 0) { 327 setTimeout(() => popup.classList.remove('open'), cfg.duracion_s * 1000); 328 } 329 }, delay); 330 } catch(e) { console.warn('popup:', e); } 331} 332 333function closePopup(e) { 334 if (e && e.target !== e.currentTarget) return; 335 closePromoPopup(); 336} 337function closePromoPopup() { 338 const p = document.getElementById('promoPopup'); 339 if (p) p.classList.remove('open'); 340}
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.