/* ============================================================ STORE (TIENDA - LADO CLIENTE) ============================================================ */ // PRODUCT FILTERING & RENDERING function getFilteredProducts() { let filtered = STATE.products; // Filter by category if (STATE.currentFilter !== 'all') { filtered = filtered.filter(p => p.cat === STATE.currentFilter); } // Filter by search if (STATE.currentSearch) { const q = STATE.currentSearch.toLowerCase(); filtered = filtered.filter(p => (p.name || '').toLowerCase().includes(q) || (p.short || '').toLowerCase().includes(q) || (p.desc || '').toLowerCase().includes(q) || (p.sku || '').toLowerCase().includes(q) || (p.id || '').toLowerCase().includes(q) ); } return filtered; } function renderProducts() { const grid = document.getElementById('products-grid'); const noRes = document.getElementById('no-results'); if (!grid) { console.error('products-grid elemento no encontrado'); return; } const filtered = getFilteredProducts(); const lastIndex = filtered.length - 1; if (lastIndex < 0) { STATE.storeSearchActiveIndex = -1; } else if (STATE.storeSearchActiveIndex > lastIndex) { STATE.storeSearchActiveIndex = lastIndex; } if (!filtered.length) { grid.innerHTML = ''; if (noRes) noRes.classList.remove('hidden'); console.log('No hay productos para mostrar'); return; } if (noRes) noRes.classList.add('hidden'); const html = filtered.map((p, index) => { const cartItem = STATE.cart.find(c => c.id === p.id); const qty = cartItem ? cartItem.qty : 1; return renderStoreProductCard(p, { index, activeIndex: STATE.storeSearchActiveIndex, quantity: qty }); }).join(''); grid.innerHTML = html; console.log(`Renderizados ${filtered.length} productos`); } // CART MANAGEMENT function addToCart(productId, qty = null) { const product = STATE.products.find(p => p.id === productId); if (!product || product.stock <= 0) return; if (!qty) { const input = document.getElementById('qty-' + productId); qty = input ? parseInt(input.value) || 1 : 1; } const existing = STATE.cart.find(c => c.id === productId); let finalQty = existing ? existing.qty + qty : qty; if (finalQty > product.stock) { finalQty = product.stock; showToast(`Solo quedan ${product.stock} unidades`, 'warning'); } const cartPrice = (STATE.activePriceListId && typeof getPriceForList === 'function') ? getPriceForList(product, STATE.activePriceListId) : product.sale; if (existing) { existing.qty = finalQty; existing.price = cartPrice; // actualizar precio por si cambió la lista } else { STATE.cart.push({ id: productId, name: product.name, price: cartPrice, qty: finalQty, img: product.img, stock: product.stock }); } updateStoreCartUI(); persistData(); showToast('Agregado al carrito', 'success'); } function removeFromCart(productId) { STATE.cart = STATE.cart.filter(c => c.id !== productId); updateStoreCartUI(); persistData(); } function updateCartQty(productId, delta) { const item = STATE.cart.find(c => c.id === productId); if (!item) return; let newQty = item.qty + delta; if (newQty > item.stock) { showToast('Stock máximo: ' + item.stock, 'warning'); return; } if (newQty <= 0) { removeFromCart(productId); } else { item.qty = newQty; updateStoreCartUI(); } persistData(); } function updateStoreCartUI() { const badge = document.getElementById('cart-badge'); const totalEl = document.getElementById('cart-total'); const itemsContainer = document.getElementById('cart-items'); if (!badge || !totalEl || !itemsContainer) return; const total = STATE.cart.reduce((s, c) => s + c.price * c.qty, 0); const count = STATE.cart.reduce((s, c) => s + c.qty, 0); badge.textContent = count; badge.classList.toggle('hidden', count === 0); totalEl.textContent = fmt(total); if (!STATE.cart.length) { itemsContainer.innerHTML = '

Tu carrito está vacío

'; return; } itemsContainer.innerHTML = STATE.cart.map(c => `
${c.name}

${escapeHtml(c.name)}

${fmt(c.price)} × ${c.qty}

${fmt(c.price * c.qty)}

${c.qty}
`).join(''); } function changeStoreQty(productId, delta) { const product = STATE.products.find(p => p.id === productId); const input = document.getElementById('qty-' + productId); if (!input || !product) return; let v = parseInt(input.value) + delta; if (v < 1) v = 1; if (v > product.stock) { v = product.stock; showToast('Stock máximo: ' + product.stock, 'warning'); } input.value = v; } // PRODUCT MODAL function openStoreProduct(productId) { const product = STATE.products.find(p => p.id === productId); if (!product) return; const modalImg = document.getElementById('modal-img'); const modalName = document.getElementById('modal-name'); const modalDesc = document.getElementById('modal-desc'); const modalPriceSale = document.getElementById('modal-price-sale'); const modalPriceRegular = document.getElementById('modal-price-regular'); const modalBadge = document.getElementById('modal-badge'); const modalQty = document.getElementById('modal-qty'); const modalAddBtn = document.getElementById('modal-add-btn'); const modalPrice = (STATE.activePriceListId && typeof getPriceForList === 'function') ? getPriceForList(product, STATE.activePriceListId) : product.sale; if (modalImg) modalImg.src = product.img; if (modalName) modalName.textContent = escapeHtml(product.name); if (modalDesc) modalDesc.textContent = escapeHtml(product.short); if (modalPriceSale) modalPriceSale.textContent = fmt(modalPrice); if (modalPriceRegular) { if (product.price !== modalPrice) { modalPriceRegular.textContent = fmt(product.price); modalPriceRegular.classList.remove('hidden'); } else { modalPriceRegular.classList.add('hidden'); } } if (modalBadge) { if (product.badge) { modalBadge.textContent = calculateDiscount(product.price, modalPrice) + '% OFF'; modalBadge.classList.remove('hidden'); } else { modalBadge.classList.add('hidden'); } } if (modalQty) { modalQty.value = 1; modalQty.max = product.stock; } if (modalAddBtn) { modalAddBtn.onclick = () => { const qty = parseInt(modalQty?.value) || 1; addToCart(productId, qty); closeModal('product-modal'); }; } openModal('product-modal'); } // CATEGORY & SEARCH function filterByCategory(category, btnElement = null) { STATE.currentFilter = category; STATE.storeSearchActiveIndex = -1; if (btnElement) { document.querySelectorAll('.cat-btn').forEach(b => b.classList.remove('active')); btnElement.classList.add('active'); } renderProducts(); } function searchProducts(query) { STATE.currentSearch = query.trim(); STATE.storeSearchActiveIndex = -1; switchTab('catalogo'); renderProducts(); } function handleStoreSearchKeydown(event) { const { key } = event; if (!['ArrowDown', 'ArrowUp', 'Enter'].includes(key)) return; const filtered = getFilteredProducts(); if (!filtered.length) return; if (key === 'Enter') { if (STATE.storeSearchActiveIndex >= 0) { event.preventDefault(); const selected = filtered[STATE.storeSearchActiveIndex]; if (selected) { addToCart(selected.id); } } return; } event.preventDefault(); const lastIndex = filtered.length - 1; if (key === 'ArrowDown') { STATE.storeSearchActiveIndex = Math.min( STATE.storeSearchActiveIndex + 1, lastIndex ); } else { STATE.storeSearchActiveIndex = Math.max( STATE.storeSearchActiveIndex - 1, 0 ); } renderProducts(); } function setupStoreSearchInputListeners() { const searchInput = document.getElementById('search-input'); if (!searchInput) return; searchInput.addEventListener('keydown', handleStoreSearchKeydown); } // SLIDER function getActiveSlides() { const sourceSlides = Array.isArray(STATE.slides) && STATE.slides.length ? STATE.slides : DEFAULT_SLIDES; return sourceSlides .filter(slide => Number(slide.is_active ?? 1) === 1) .sort((a, b) => Number(a.sort_order || 0) - Number(b.sort_order || 0)); } function renderSlider() { const container = document.getElementById('slider-container'); const track = document.getElementById('slider-track'); const dots = document.getElementById('slider-dots'); if (!track || !dots || !container) { console.error('Slider elementos no encontrados'); return; } const slides = getActiveSlides(); const resolvedSlides = slides.length ? slides : DEFAULT_SLIDES; track.style.transition = 'transform 0.5s cubic-bezier(0.4, 0, 0.2, 1)'; track.innerHTML = resolvedSlides.map((slide, index) => ` ${escapeHtml(slide.title || `Banner ${index + 1}`)} `).join(''); dots.innerHTML = resolvedSlides.map((_, index) => ` `).join(''); _sliderSetWidths(container, track); STATE.sliderIdx = 0; goToSlide(0); // Recalcular anchos al redimensionar window.removeEventListener('resize', _sliderOnResize); window.addEventListener('resize', _sliderOnResize); console.log(`Slider renderizado con ${resolvedSlides.length} slides`); } function _sliderSetWidths(container, track) { const slideWidth = container.offsetWidth; const slides = track.querySelectorAll('img'); slides.forEach(s => { s.style.width = slideWidth + 'px'; }); track.style.width = (slides.length * slideWidth) + 'px'; } function _sliderOnResize() { const container = document.getElementById('slider-container'); const track = document.getElementById('slider-track'); if (!container || !track) return; _sliderSetWidths(container, track); // Reposicionar sin animación const slideWidth = container.offsetWidth; track.style.transition = 'none'; track.style.transform = `translateX(-${STATE.sliderIdx * slideWidth}px)`; requestAnimationFrame(() => { track.style.transition = 'transform 0.5s cubic-bezier(0.4, 0, 0.2, 1)'; }); } function changeSlide(direction) { const totalSlides = Math.max(getActiveSlides().length, 1); STATE.sliderIdx = (STATE.sliderIdx + direction + totalSlides) % totalSlides; goToSlide(STATE.sliderIdx); } function goToSlide(index) { STATE.sliderIdx = index; const container = document.getElementById('slider-container'); const track = document.getElementById('slider-track'); if (track && container) { const slideWidth = container.offsetWidth; track.style.transform = `translateX(-${index * slideWidth}px)`; } document.querySelectorAll('#slider-dots button').forEach((btn, i) => { if (i === index) { btn.className = 'w-3 h-3 bg-yellow-400 rounded-full transition-all duration-300'; } else { btn.className = 'w-2.5 h-2.5 bg-white/40 rounded-full transition-all duration-300 hover:bg-white/70'; } }); resetSliderTimer(); } function resetSliderTimer() { clearInterval(STATE.sliderTimer); if (Math.max(getActiveSlides().length, 1) <= 1) { return; } STATE.sliderTimer = setInterval(() => changeSlide(1), 4500); } // WEB ORDER function submitWebOrder(sendViaWhatsApp = false) { if (!STATE.cart.length) { showToast('El carrito está vacío', 'error'); return; } const isAuthenticated = document.body.dataset.authenticated === '1'; let name, address; if (isAuthenticated) { // Get client from authenticated selector const clientSelect = document.getElementById('store-authenticated-client'); const selectedClientId = clientSelect?.value; if (!selectedClientId) { showToast('Por favor selecciona un cliente', 'warning'); return; } const client = STATE.clients?.find(c => c.id === selectedClientId); if (!client) { showToast('Error: cliente no encontrado', 'error'); return; } name = client.name; address = client.address || ''; } else { // Get client from regular inputs const nameInput = document.getElementById('store-client-name'); const addressInput = document.getElementById('store-client-address'); if (!nameInput || !addressInput) { showToast('Error: formulario no encontrado', 'error'); return; } name = nameInput.value.trim(); address = addressInput.value.trim(); if (!name) { showToast('Por favor ingresa tu Nombre / Negocio', 'warning'); return; } } const total = STATE.cart.reduce((s, c) => s + c.price * c.qty, 0); const date = formatDate(new Date()); const time = formatTime(new Date()); // Save to admin orders const newOrder = { id: generateId('ord'), orderNumber: nextOrderNumber(), date: `${date} ${time}`, client: name, address: address || '—', items: [...STATE.cart], total: total, source: 'web', status: 'pending' }; STATE.adminOrders.unshift(newOrder); persistData(); if (sendViaWhatsApp) { let msg = `*NUEVO PEDIDO*\n`; msg += `Cliente: ${name}\n`; if (address) msg += `Envio a: ${address}\n`; msg += `${date} ${time}\n\n`; STATE.cart.forEach(c => { msg += `- ${c.qty}x ${c.name} (${fmt(c.price)}) = *${fmt(c.price * c.qty)}*\n`; }); msg += `\n*TOTAL: ${fmt(total)}*\n`; const encoded = encodeURIComponent(msg); window.open(`https://wa.me/5492262556648?text=${encoded}`, '_blank'); } // Clear cart & form STATE.cart = []; if (!isAuthenticated) { const nameInput = document.getElementById('store-client-name'); const addressInput = document.getElementById('store-client-address'); if (nameInput) nameInput.value = ''; if (addressInput) addressInput.value = ''; } else { const clientSelect = document.getElementById('store-authenticated-client'); if (clientSelect && STATE.clients?.length > 0) { clientSelect.value = STATE.clients[0].id; } } updateStoreCartUI(); closeDrawer('cart-drawer'); showToast('Pedido guardado con éxito', 'success'); } // Legacy compatibility for inline handlers in index.html function filterCat(category, btnElement = null) { filterByCategory(category, btnElement); } function slideChange(direction) { changeSlide(direction); } function enviarPedidoWeb(sendViaWhatsApp = false) { submitWebOrder(sendViaWhatsApp); } function changeModalQty(delta) { const input = document.getElementById('modal-qty'); if (!input) return; let qty = parseInt(input.value, 10) || 1; const max = parseInt(input.max, 10) || Number.MAX_SAFE_INTEGER; qty += delta; if (qty < 1) qty = 1; if (qty > max) qty = max; input.value = qty; } // Initialize client selector if authenticated function initializeAuthenticatedClientSelector() { const isAuthenticated = document.body.dataset.authenticated === '1'; const clientSelect = document.getElementById('store-authenticated-client'); if (!isAuthenticated || !clientSelect) return; // Check if clients are already loaded const checkAndLoad = setInterval(() => { if (STATE && STATE.clients && STATE.clients.length > 0) { clearInterval(checkAndLoad); populateClientSelector(); } }, 100); // Timeout after 5 seconds setTimeout(() => clearInterval(checkAndLoad), 5000); } function populateClientSelector() { const clientSelect = document.getElementById('store-authenticated-client'); if (!clientSelect || !STATE?.clients) return; clientSelect.innerHTML = '' + STATE.clients.map(c => ``).join(''); // Auto-select first client (usually "Consumidor Final") if (STATE.clients.length > 0) { clientSelect.value = STATE.clients[0].id; } clientSelect.addEventListener('change', (e) => { STATE.storeSelectedClient = e.target.value; }); } document.addEventListener('DOMContentLoaded', initializeAuthenticatedClientSelector);