// ========== CONFIGURACIÓN SUPABASE ========== const SUPABASE_URL = 'https://fcjvdibwnatykbvsisxy.supabase.co'; const SUPABASE_ANON_KEY = 'sb_publishable_23rDcrD57vq4B1ocHWDClg_0tEbleYN'; const supabaseClient = supabase.createClient(SUPABASE_URL, SUPABASE_ANON_KEY); // ========== DATOS POR DEFECTO ========== const DEFAULT_CONFIG = { name: 'Súper Motor Import', tagline: 'Piezas · Motos · Accesorios', logoEmoji: '🏍', color: '#E03A1E', wa: '8499360197', waMsg: '¡Hola! Me gustaría hacer el siguiente pedido:', bannerTitle: 'Súper Gato Motors', bannerSub: 'Potencia y confianza con el Dr. Neiser López', bannerBadge: '🔥 OFERTAS ESPECIALES 🔥', bannerImage: 'https://fcjvdibwnatykbvsisxy.supabase.co/storage/v1/object/public/banners/banner_1779979463581.jpg', shipping: 5, freeShip: 100, pass: 'admin123', socialSlogan: '🏍 Súper Gato Motors - Potencia y confianza con el Dr. Neiser López 🔥' }; let S = { config: {...DEFAULT_CONFIG}, welcomeCard: { icon:'🏍', title:'¡Bienvenido a Súper Motor Import!', message:'Encuentra las mejores motos, repuestos y accesorios al mejor precio.\n\n🚚 Envíos disponibles\n💬 Atención por WhatsApp\n✅ Productos de calidad', showOnLoad: true, lastUpdated: new Date().toISOString() }, categories: [{id:'c1',name:'Motos Completas',emoji:'🏍',desc:'Motos importadas'},{id:'c2',name:'Motor y Transmisión',emoji:'⚙️',desc:'Piezas de motor'},{id:'c3',name:'Frenos y Suspensión',emoji:'🔧',desc:'Sistema de frenos'},{id:'c4',name:'Electricidad',emoji:'⚡',desc:'Sistema eléctrico'},{id:'c5',name:'Carrocería',emoji:'🛡️',desc:'Piezas exteriores'},{id:'c6',name:'Accesorios',emoji:'🎽',desc:'Cascos, guantes'}], products: [], orders: [], totalRevenue: 0 }; let cart = []; let selectedCat = 'all'; let editingProductId = null; let newProductImages = []; let currentEditImages = []; let welcomeCardShown = false; // ========== VARIABLES PARA PAGINACIÓN ========== let allProducts = []; let currentPage = 0; const PRODUCTS_PER_PAGE = 30; // Aumentamos para cargar más de una vez let isLoadingMore = false; let imageObserver = null; // ========== UTILIDADES ========== function showLoader() { document.getElementById('loader').classList.add('show'); } function hideLoader() { document.getElementById('loader').classList.remove('show'); } function toast(msg) { const el = document.getElementById('toast'); el.textContent = msg; el.classList.add('show'); setTimeout(() => el.classList.remove('show'), 2500); } function sanitizeWhatsAppNumber(num) { return String(num || '').replace(/[^\d]/g, '').trim(); } // ========== OPTIMIZACIÓN DE IMÁGENES ========== function getOptimizedImageUrl(url) { if (!url) return null; return url.split('?')[0]; } function getOptimizedModalImage(url) { if (!url) return null; return url.split('?')[0]; } function getOptimizedBannerImage(url) { if (!url) return null; return url.split('?')[0]; } // ========== FUNCIONES GLOBALES ========== window.closeWelcomeCard = function() { document.getElementById('welcomeCard').classList.remove('open'); }; window.previewWelcomeCard = function() { document.getElementById('welcome-icon').textContent = document.getElementById('welcome-icon-input').value || '🏍'; document.getElementById('welcome-title').textContent = document.getElementById('welcome-title-input').value || 'Vista previa'; document.getElementById('welcome-message').innerHTML = (document.getElementById('welcome-message-input').value || '').replace(/\n/g, '
'); document.getElementById('welcomeCard').classList.add('open'); }; window.closeDrawer = function() { document.getElementById('drawerBg').classList.remove('open'); }; window.openDrawer = function() { document.getElementById('drawerBg').classList.add('open'); window.renderCart(); }; window.closeModal = function() { document.getElementById('modalBg').classList.remove('open'); document.getElementById('modal-content').innerHTML = ''; }; window.bgClose = function(e, id) { if(e.target === document.getElementById(id)) { if(id === 'drawerBg') window.closeDrawer(); else window.closeModal(); } }; window.addToCart = function(id) { addToCartFunction(id); }; window.quickWa = function(id) { quickWaFunction(id); }; window.openModal = function(id) { openModalFunction(id); }; window.selectCat = function(id) { selectedCat = id; window.renderCatScroller(); window.renderProducts(); }; window.openAdmin = function() { openAdminFunction(); }; window.closeAdmin = function() { closeAdminFunction(); }; window.showPanel = function(id) { showPanelFunction(id); }; window.saveWelcomeConfig = function() { saveWelcomeConfigFunction(); }; window.saveConfig = function() { saveConfigFunction(); }; window.addProduct = function() { addProductFunction(); }; window.updateProduct = function() { updateProductFunction(); }; window.deleteProduct = function(id) { deleteProductFunction(id); }; window.openEditProduct = function(id) { openEditProductFunction(id); }; window.closeEditModal = function() { closeEditModalFunction(); }; window.addCategory = function() { addCategoryFunction(); }; window.deleteCategory = function(id) { deleteCategoryFunction(id); }; window.setOrderStatus = function(id, status) { setOrderStatusFunction(id, status); }; window.deleteOrder = function(id) { deleteOrderFunction(id); }; window.clearOrders = function() { clearOrdersFunction(); }; window.exportOrders = function() { exportOrdersFunction(); }; window.renderCart = function() { renderCartFunction(); }; window.updateBadge = function() { updateBadgeFunction(); }; window.renderProducts = function() { renderProductsFunction(); }; window.renderCatScroller = function() { renderCatScrollerFunction(); }; window.applyTheme = function() { applyThemeFunction(); }; window.exportData = function() { exportDataFunction(); }; window.importData = function(evt) { importDataFunction(evt); }; window.resetAll = function() { resetAllFunction(); }; window.changeModalImage = function(direction) { changeModalImageFunction(direction); }; window.setModalImage = function(index) { setModalImageFunction(index); }; // ========== FUNCIONES DE SUBIDA DE IMÁGENES ========== async function uploadImagesToSupabase(files) { const uploadedUrls = []; for (const file of files) { const fileExt = file.name.split('.').pop(); const fileName = `${Date.now()}-${Math.random().toString(36).substring(2, 8)}.${fileExt}`; const filePath = `product-images/${fileName}`; try { const { error: uploadError } = await supabaseClient.storage .from('product-images') .upload(filePath, file, { cacheControl: '31536000, immutable', contentType: file.type, upsert: true }); if (uploadError) throw uploadError; const { data: urlData } = supabaseClient.storage .from('product-images') .getPublicUrl(filePath); uploadedUrls.push(urlData.publicUrl); } catch (error) { console.error('Error subiendo imagen:', error); toast(`Error subiendo ${file.name}: ${error.message}`); } } return uploadedUrls; } async function uploadSingleImageToStorage(file) { if (!file) return null; const fileExt = file.name.split('.').pop(); const fileName = `${Date.now()}-${Math.random().toString(36).substring(2, 8)}.${fileExt}`; const filePath = `product-images/${fileName}`; try { const { error: uploadError } = await supabaseClient.storage .from('product-images') .upload(filePath, file, { cacheControl: '31536000, immutable', contentType: file.type, upsert: true }); if (uploadError) throw uploadError; const { data: urlData } = supabaseClient.storage .from('product-images') .getPublicUrl(filePath); return urlData.publicUrl; } catch (error) { console.error('Error subiendo imagen:', error); toast(`Error subiendo ${file.name}: ${error.message}`); return null; } } async function uploadBannerImage(file) { if (!file) return null; try { showLoader(); const fileExt = file.name.split('.').pop(); const fileName = `banner_${Date.now()}.${fileExt}`; const { error } = await supabaseClient.storage.from('banners').upload(fileName, file, { cacheControl: '31536000, immutable', contentType: file.type, upsert: true }); if (error) { console.error(error); hideLoader(); toast('❌ Error: ' + error.message); return null; } const { data: urlData } = supabaseClient.storage.from('banners').getPublicUrl(fileName); hideLoader(); return urlData.publicUrl; } catch (err) { console.error(err); hideLoader(); toast('❌ Error al subir la imagen'); return null; } } // ========== FUNCIONES DE LA TIENDA ========== function applyThemeFunction() { const cfg = S.config; const bannerElement = document.getElementById('store-banner'); if (bannerElement && cfg.bannerImage) { const optimizedBanner = getOptimizedBannerImage(cfg.bannerImage); bannerElement.style.backgroundImage = `linear-gradient(135deg, rgba(17,17,17,0.7), rgba(30,30,30,0.6)), url('${optimizedBanner}')`; } document.documentElement.style.setProperty('--red', cfg.color); document.getElementById('store-brand-name').textContent = (cfg.name || 'Súper Motor Import').toUpperCase(); document.getElementById('store-tagline').textContent = cfg.tagline || ''; document.getElementById('store-logo-icon').textContent = cfg.logoEmoji || '🏍'; document.getElementById('banner-title').textContent = cfg.bannerTitle || ''; document.getElementById('banner-sub').textContent = cfg.bannerSub || ''; document.getElementById('banner-badge').textContent = cfg.bannerBadge || ''; } function renderCatScrollerFunction() { const el = document.getElementById('cat-scroller'); if(!el) return; let html = `
Todos (${S.products.length})
`; S.categories.forEach(c => { const cnt = S.products.filter(p => p.cat === c.id).length; if(cnt>0) html += `
${c.emoji} ${c.name} (${cnt})
`; }); el.innerHTML = html; } // ========== RENDERIZADO CON CARGA INSTANTÁNEA DE TODAS LAS IMÁGENES ========== function renderProductsFunction() { const grid = document.getElementById('products-grid'); if(!grid) return; const q = (document.getElementById('search-inp')?.value || '').toLowerCase(); let prods = [...S.products]; if(selectedCat !== 'all') prods = prods.filter(p => p.cat === selectedCat); if(q) prods = prods.filter(p => p.name.toLowerCase().includes(q)); allProducts = prods; currentPage = 0; grid.innerHTML = ''; isLoadingMore = false; document.getElementById('sec-count-txt').textContent = `${allProducts.length} productos`; if (allProducts.length === 0) { grid.innerHTML = '
🔍
No se encontraron productos
'; document.getElementById('loadMoreTrigger').style.display = 'none'; return; } renderProductsBatch(); setupInfiniteScroll(); // Cargar las imágenes del siguiente lote en segundo plano setTimeout(() => preloadNextBatch(), 300); } function renderProductsBatch() { const grid = document.getElementById('products-grid'); if(!grid) return; const start = currentPage * PRODUCTS_PER_PAGE; const end = Math.min(start + PRODUCTS_PER_PAGE, allProducts.length); const batch = allProducts.slice(start, end); if (batch.length === 0) { document.getElementById('loadMoreTrigger').style.display = 'none'; return; } const isMobile = window.innerWidth < 600; const batchHtml = batch.map(p => { const cat = S.categories.find(c => c.id === p.cat); const images = (p.images && p.images.length > 0) ? p.images : []; const mainImg = images.length > 0 ? images[0] : null; const cleanUrl = mainImg ? getOptimizedImageUrl(mainImg) : null; // Usamos eager para que todas las imágenes se carguen inmediatamente let mainImgHtml = ''; if (cleanUrl) { // Para móvil usamos srcset con imágenes más pequeñas const mobileWidth = isMobile ? 200 : 400; mainImgHtml = `${p.name}`; } else { mainImgHtml = `
${p.emoji||'📦'}
`; } let flagHtml = ''; if(p.status === 'new') flagHtml = 'Nuevo'; else if(p.status === 'sale') flagHtml = 'Oferta'; else if(p.status === 'hot') flagHtml = '🔥 Popular'; else if(p.status === 'out') flagHtml = 'Sin stock'; let carouselHtml = ''; if (images.length > 1) { carouselHtml = ``; } return `
${mainImgHtml} ${carouselHtml}
${flagHtml}
${cat ? cat.emoji+' '+cat.name : ''}
${p.name}
$${parseFloat(p.price).toFixed(2)} ${p.old_price > 0 ? `$${parseFloat(p.old_price).toFixed(2)}` : ''}
${p.stock > 0 ? '✅ '+p.stock+' disponibles' : '❌ Sin stock'}
`; }).join(''); grid.innerHTML += batchHtml; currentPage++; const trigger = document.getElementById('loadMoreTrigger'); if (currentPage * PRODUCTS_PER_PAGE < allProducts.length) { trigger.style.display = 'block'; trigger.innerHTML = '🔄 Desplázate para cargar más productos...'; } else { trigger.style.display = 'none'; } // Cargar imágenes en segundo plano con IntersectionObserver observeImages(); } // ========== INTERSECTION OBSERVER PARA PRECARGAR IMÁGENES ========== function observeImages() { if (imageObserver) imageObserver.disconnect(); imageObserver = new IntersectionObserver((entries) => { entries.forEach(entry => { if (entry.isIntersecting) { const img = entry.target; const src = img.getAttribute('data-src'); if (src) { img.src = src; img.removeAttribute('data-src'); } imageObserver.unobserve(img); } }); }, { rootMargin: '200px' }); document.querySelectorAll('.prod-card img[data-src]').forEach(img => { imageObserver.observe(img); }); } // ========== PRECARGAR SIGUIENTE LOTE EN SEGUNDO PLANO ========== function preloadNextBatch() { const nextStart = currentPage * PRODUCTS_PER_PAGE; const nextBatch = allProducts.slice(nextStart, nextStart + PRODUCTS_PER_PAGE); if (nextBatch.length === 0) return; // Pre-cargar imágenes del siguiente lote en segundo plano nextBatch.forEach(p => { if (p.images && p.images.length > 0) { const img = new Image(); img.src = getOptimizedImageUrl(p.images[0]); } }); } function setupInfiniteScroll() { if (window._scrollListener) { window.removeEventListener('scroll', window._scrollListener); } const scrollHandler = () => { if (isLoadingMore) return; if (currentPage * PRODUCTS_PER_PAGE >= allProducts.length) return; const trigger = document.getElementById('loadMoreTrigger'); if (!trigger) return; const rect = trigger.getBoundingClientRect(); if (rect.top <= window.innerHeight + 100) { isLoadingMore = true; trigger.innerHTML = ' Cargando más productos...'; setTimeout(() => { renderProductsBatch(); preloadNextBatch(); isLoadingMore = false; }, 300); } }; window._scrollListener = scrollHandler; window.addEventListener('scroll', scrollHandler); } // ========== FUNCIONES DEL CARRITO ========== function addToCartFunction(id) { const p = S.products.find(x => x.id === id); if(!p || p.stock === 0) return; const ex = cart.find(i => i.id === id); if(ex) { if(ex.qty < p.stock) { ex.qty++; toast('✓ Cantidad actualizada'); } else { toast('⚠️ Stock máximo'); return; } } else { cart.push({id, name: p.name, price: parseFloat(p.price), qty: 1, emoji: p.emoji || '📦', img: (p.images && p.images.length > 0) ? p.images[0] : null}); toast('✓ Añadido al pedido'); } updateBadgeFunction(); } function removeFromCart(id) { cart = cart.filter(i => i.id !== id); updateBadgeFunction(); renderCartFunction(); } function changeQty(id, d) { const item = cart.find(i => i.id === id); if(!item) return; item.qty += d; if(item.qty <= 0) { removeFromCart(id); return; } const prod = S.products.find(p => p.id === id); if(prod && item.qty > prod.stock) { item.qty = prod.stock; toast('⚠️ Stock máximo'); } renderCartFunction(); } function cartTotal() { return cart.reduce((s,i) => s + i.price * i.qty, 0); } function cartItems() { return cart.reduce((s,i) => s + i.qty, 0); } function updateBadgeFunction() { const badge = document.getElementById('cart-badge'); if(badge) badge.textContent = cartItems(); } function renderCartFunction() { const body = document.getElementById('drawer-body'); const foot = document.getElementById('drawer-foot'); if(!cart.length) { body.innerHTML = '
🛒
Tu pedido está vacío

'; foot.innerHTML = ''; return; } body.innerHTML = cart.map(i => `
${i.img ? `` : `${i.emoji}`}
${i.name}
$${i.price.toFixed(2)} c/u
${i.qty}
$${(i.price * i.qty).toFixed(2)}
`).join(''); const sub = cartTotal(); const freeFrom = S.config.freeShip || 100; const ship = sub >= freeFrom ? 0 : (S.config.shipping || 5); const total = sub + ship; foot.innerHTML = `
Subtotal$${sub.toFixed(2)}
Envío${ship === 0 ? 'GRATIS ✅' : '$'+ship.toFixed(2)}
${sub < freeFrom ? `
Envío gratis desde $${freeFrom}
` : ''}
TOTAL$${total.toFixed(2)}
`; } function sendOrderFunction() { if(!cart.length) { toast('⚠️ El pedido está vacío'); return; } const nombre = document.getElementById('cust-nombre')?.value.trim(); const dni = document.getElementById('cust-dni')?.value.trim(); const direccion = document.getElementById('cust-direccion')?.value.trim(); const telefono = document.getElementById('cust-telefono')?.value.trim(); if(!nombre || !dni || !direccion || !telefono) { toast('⚠️ Complete todos los datos del cliente'); return; } const waNum = sanitizeWhatsAppNumber(S.config.wa); if(!waNum || waNum.length < 8) { toast('⚠️ Configure WhatsApp en Admin'); window.openAdmin(); return; } const sub = cartTotal(), ship = sub >= (S.config.freeShip || 100) ? 0 : (S.config.shipping || 5), total = sub + ship; let msg = `📋 *NUEVO PEDIDO - SÚPER MOTOR IMPORT - DR. NEISER LOPEZ*\n\n👤 *DATOS DEL CLIENTE*\nNombre: ${nombre}\nIdentificación: ${dni}\nDirección: ${direccion}\nTeléfono: ${telefono}\n\n━━━━━━━━━━━━━━━━━━━\n🛒 *PRODUCTOS SOLICITADOS*\n`; cart.forEach(i => { msg += `${i.emoji} *${i.name}*\n ${i.qty} x $${i.price.toFixed(2)} = *$${(i.qty * i.price).toFixed(2)}*\n`; }); msg += `━━━━━━━━━━━━━━━━━━━\n📦 Subtotal: $${sub.toFixed(2)}\n🚚 Envío: ${ship === 0 ? 'GRATIS' : '$'+ship.toFixed(2)}\n💰 *TOTAL: $${total.toFixed(2)}*\n\n📅 Fecha: ${new Date().toLocaleString()}\n¡Gracias por confiar en Súper Motor Import! 🏍`; const order = { id: '#' + Math.floor(1000 + Math.random() * 9000), date: new Date().toLocaleString('es'), items: [...cart], subtotal: sub, shipping: ship, total: total, customer: `${nombre} (${dni}) - ${direccion} - Tel:${telefono}`, status: 'pending' }; S.orders.unshift(order); S.totalRevenue = (S.totalRevenue || 0) + total; localStorage.setItem('smi_orders', JSON.stringify(S.orders)); localStorage.setItem('smi_revenue', S.totalRevenue); window.open(`https://api.whatsapp.com/send?phone=${waNum}&text=${encodeURIComponent(msg)}`, '_blank'); cart = []; updateBadgeFunction(); window.closeDrawer(); toast('✅ Pedido enviado'); } function quickWaFunction(id) { const p = S.products.find(x => x.id === id); if(!p) return; const waNum = sanitizeWhatsAppNumber(S.config.wa); if(!waNum) { toast('⚠️ Configure WhatsApp'); return; } window.open(`https://api.whatsapp.com/send?phone=${waNum}&text=${encodeURIComponent(`¡Hola! 👋\n\nEstoy interesado en: *${p.name}*\nPrecio: $${parseFloat(p.price).toFixed(2)}\n\n¿Disponibilidad y envíos?`)}`, '_blank'); } // ========== MODAL PRODUCTO ========== let currentModalImages = [], currentModalIndex = 0, currentModalProduct = null; function openModalFunction(id) { const p = S.products.find(x => x.id === id); if(!p) return; currentModalProduct = p; const cat = S.categories.find(c => c.id === p.cat); currentModalImages = (p.images && p.images.length > 0) ? p.images : []; currentModalIndex = 0; renderModalContent(cat); document.getElementById('modalBg').classList.add('open'); } function renderModalContent(cat) { const p = currentModalProduct; const hasImages = currentModalImages.length > 0; let carouselHtml = ''; if (hasImages) { const optimizedModalImg = getOptimizedModalImage(currentModalImages[currentModalIndex]); carouselHtml = ` `; } else { carouselHtml = ``; } document.getElementById('modal-content').innerHTML = `${carouselHtml}`; } function changeModalImageFunction(direction) { if(currentModalImages.length === 0) return; currentModalIndex = (currentModalIndex + direction + currentModalImages.length) % currentModalImages.length; const slideDiv = document.querySelector('#modal-content .carousel-slide'); if (slideDiv) { const optimizedImg = getOptimizedModalImage(currentModalImages[currentModalIndex]); slideDiv.innerHTML = `${currentModalProduct?.name}`; } const dots = document.querySelectorAll('#modal-content .carousel-dot'); dots.forEach((dot, idx) => { if (idx === currentModalIndex) dot.classList.add('active'); else dot.classList.remove('active'); }); } function setModalImageFunction(index) { if(currentModalImages.length === 0) return; currentModalIndex = index; const slideDiv = document.querySelector('#modal-content .carousel-slide'); if (slideDiv) { const optimizedImg = getOptimizedModalImage(currentModalImages[currentModalIndex]); slideDiv.innerHTML = `${currentModalProduct?.name}`; } const dots = document.querySelectorAll('#modal-content .carousel-dot'); dots.forEach((dot, idx) => { if (idx === currentModalIndex) dot.classList.add('active'); else dot.classList.remove('active'); }); } // ========== FUNCIONES DE ADMIN ========== async function loadProductsFromAPI(showWelcome = true) { showLoader(); try { const { data, error } = await supabaseClient.from('products').select('*').order('created_at', { ascending: false }); if (error) throw error; S.products = data || []; console.log(`✅ ${S.products.length} productos cargados`); applyThemeFunction(); renderCatScrollerFunction(); renderProductsFunction(); if (showWelcome && !welcomeCardShown && S.welcomeCard.showOnLoad !== false) { setTimeout(() => { document.getElementById('welcomeCard').classList.add('open'); welcomeCardShown = true; }, 500); } } catch (error) { console.error('❌ Error:', error); toast('Error al cargar productos'); } finally { hideLoader(); } } async function openAdminFunction() { const pass = prompt('Contraseña:'); if(pass !== (S.config.pass || 'admin123')) { toast('❌ Incorrecta'); return; } document.getElementById('storeView').style.display = 'none'; document.getElementById('adminView').style.display = 'block'; loadAdminConfigFunction(); loadWelcomeConfigFunction(); renderDashboardFunction(); renderProdManageFunction(); renderCatManageFunction(); renderAllOrdersFunction(); } function closeAdminFunction() { document.getElementById('adminView').style.display = 'none'; document.getElementById('storeView').style.display = 'block'; applyThemeFunction(); renderCatScrollerFunction(); renderProductsFunction(); } function showPanelFunction(id) { document.querySelectorAll('.admin-panel').forEach(p => p.classList.remove('on')); document.querySelectorAll('.admin-nav-btn').forEach(b => b.classList.remove('on')); document.getElementById(id).classList.add('on'); const btn = document.querySelector(`[data-panel="${id}"]`); if(btn) btn.classList.add('on'); } function loadWelcomeConfigFunction() { const wc = S.welcomeCard; document.getElementById('welcome-icon-input').value = wc.icon || '🏍'; document.getElementById('welcome-title-input').value = wc.title || ''; document.getElementById('welcome-message-input').value = wc.message || ''; document.getElementById('welcome-show-input').value = wc.showOnLoad !== false ? 'true' : 'false'; } function saveWelcomeConfigFunction() { S.welcomeCard = { icon: document.getElementById('welcome-icon-input').value, title: document.getElementById('welcome-title-input').value, message: document.getElementById('welcome-message-input').value, showOnLoad: document.getElementById('welcome-show-input').value === 'true', lastUpdated: new Date().toISOString() }; localStorage.setItem('smi_welcome', JSON.stringify(S.welcomeCard)); toast('✅ Tarjeta actualizada'); } function renderProdManageFunction() { const el = document.getElementById('prod-manage-list'); document.getElementById('prod-count-label').textContent = '(' + S.products.length + ')'; if(!S.products.length) { el.innerHTML = '
Sin productos
'; return; } el.innerHTML = S.products.map(p => { const cat = S.categories.find(c => c.id === p.cat); const imgHtml = (p.images && p.images.length > 0) ? `` : `${p.emoji || '📦'}`; return `
${imgHtml}
${p.name}
${cat ? cat.name : ''} · Stock: ${p.stock} · ${p.images?.length || 0} imágenes
$${parseFloat(p.price).toFixed(2)}
`; }).join(''); const sel = document.getElementById('p-cat'); if(sel) sel.innerHTML = S.categories.map(c => ``).join(''); } function renderGalleryPreview(containerId, images, onRemove) { const container = document.getElementById(containerId); if(!container) return; if(!images || images.length === 0) { container.innerHTML = '
Sin imágenes
'; return; } container.innerHTML = images.map((img, idx) => ``).join(''); } function updateNewGalleryPreview() { const previewUrls = newProductImages.map(item => item.previewUrl); renderGalleryPreview('p-gallery-preview', previewUrls, async (idx) => { newProductImages.splice(idx, 1); updateNewGalleryPreview(); }); } document.getElementById('p-gallery-input')?.addEventListener('change', (e) => { const files = Array.from(e.target.files); files.forEach(file => { const reader = new FileReader(); reader.onload = (event) => { newProductImages.push({ file: file, previewUrl: event.target.result }); updateNewGalleryPreview(); }; reader.readAsDataURL(file); }); e.target.value = ''; }); function updateEditGalleryPreview() { renderGalleryPreview('edit-gallery-preview', currentEditImages, (idx) => { currentEditImages.splice(idx, 1); updateEditGalleryPreview(); }); } document.getElementById('edit-gallery-input')?.addEventListener('change', async (e) => { const files = Array.from(e.target.files); for(const file of files) { const url = await uploadSingleImageToStorage(file); if(url) currentEditImages.push(url); } updateEditGalleryPreview(); e.target.value = ''; }); function openEditProductFunction(id) { const p = S.products.find(x => x.id === id); if(!p) return; editingProductId = id; currentEditImages = [...(p.images || [])]; document.getElementById('edit-name').value = p.name; document.getElementById('edit-cat').innerHTML = S.categories.map(c => ``).join(''); document.getElementById('edit-price').value = p.price; document.getElementById('edit-old-price').value = p.old_price || 0; document.getElementById('edit-stock').value = p.stock; document.getElementById('edit-emoji').value = p.emoji || '📦'; document.getElementById('edit-desc').value = p.description || ''; document.getElementById('edit-status').value = p.status || 'active'; updateEditGalleryPreview(); document.getElementById('editModal').classList.add('open'); } function closeEditModalFunction() { document.getElementById('editModal').classList.remove('open'); editingProductId = null; currentEditImages = []; } async function addProductFunction() { const name = document.getElementById('p-name').value.trim(); const catId = document.getElementById('p-cat').value; const price = parseFloat(document.getElementById('p-price').value || 0); const oldPrice = parseFloat(document.getElementById('p-old-price').value || 0); const stock = parseInt(document.getElementById('p-stock').value || 0); const emoji = document.getElementById('p-emoji').value || '📦'; const desc = document.getElementById('p-desc').value || ''; const status = document.getElementById('p-status').value; if (!name) { toast('⚠️ Escribe un nombre'); return; } const imageFiles = newProductImages.map(item => item.file).filter(f => f); let imageUrls = []; if (imageFiles.length > 0) { toast('📤 Subiendo imágenes...'); imageUrls = await uploadImagesToSupabase(imageFiles); } const newId = 'p_' + Date.now() + '_' + Math.random().toString(36).substr(2, 6); const { error } = await supabaseClient.from('products').insert([{ id: newId, name, cat: catId, price, old_price: oldPrice, stock, emoji, description: desc, status, images: imageUrls }]); if (error) { toast('❌ Error: ' + error.message); return; } toast('✅ Producto agregado'); document.getElementById('p-name').value = ''; document.getElementById('p-price').value = ''; document.getElementById('p-old-price').value = ''; document.getElementById('p-stock').value = '1'; document.getElementById('p-emoji').value = '🔧'; document.getElementById('p-desc').value = ''; newProductImages = []; updateNewGalleryPreview(); await loadProductsFromAPI(false); renderProdManageFunction(); renderCatScrollerFunction(); } async function updateProductFunction() { if (!editingProductId) return; const { error } = await supabaseClient.from('products').update({ name: document.getElementById('edit-name').value.trim(), cat: document.getElementById('edit-cat').value, price: parseFloat(document.getElementById('edit-price').value), old_price: parseFloat(document.getElementById('edit-old-price').value) || 0, stock: parseInt(document.getElementById('edit-stock').value) || 0, emoji: document.getElementById('edit-emoji').value.trim() || '📦', description: document.getElementById('edit-desc').value.trim(), status: document.getElementById('edit-status').value, images: currentEditImages }).eq('id', editingProductId); if (error) { toast('❌ Error: ' + error.message); return; } toast('✅ Producto actualizado'); await loadProductsFromAPI(false); renderProdManageFunction(); closeEditModalFunction(); } async function deleteProductFunction(id) { if (!confirm('¿Eliminar producto?')) return; const { error } = await supabaseClient.from('products').delete().eq('id', id); if (error) { toast('❌ Error: ' + error.message); return; } toast('🗑️ Producto eliminado'); await loadProductsFromAPI(false); renderProdManageFunction(); renderCatScrollerFunction(); } function renderDashboardFunction() { document.getElementById('dash-stats').innerHTML = `
$${(S.totalRevenue || 0).toLocaleString('es', {maximumFractionDigits:2})}
Ventas
${S.orders.length}
Pedidos
${S.products.length}
Productos
${S.categories.length}
Categorías
`; const ordEl = document.getElementById('dash-orders'); if(!S.orders.length) { ordEl.innerHTML = '
Sin pedidos
'; } else { ordEl.innerHTML = S.orders.slice(0,5).map(o => `
${o.id}
${o.date}
${o.status || 'Pendiente'}
👤 ${o.customer}
$${(o.total || 0).toFixed(2)}
`).join(''); } } function renderAllOrdersFunction() { const el = document.getElementById('all-orders'); if(!S.orders.length) { el.innerHTML = '
📋
Sin pedidos
'; } else { el.innerHTML = S.orders.map(o => `
${o.id}
${o.date}
${o.status || 'Pendiente'}
👤 ${o.customer}
${o.items.map(i => `${i.emoji} ${i.name} x${i.qty}`).join(' · ')}
$${(o.total || 0).toFixed(2)}
`).join(''); } } function setOrderStatusFunction(id, status) { const o = S.orders.find(x => x.id === id); if(o) { o.status = status; localStorage.setItem('smi_orders', JSON.stringify(S.orders)); renderAllOrdersFunction(); renderDashboardFunction(); toast('Pedido actualizado'); } } function deleteOrderFunction(id) { S.orders = S.orders.filter(x => x.id !== id); localStorage.setItem('smi_orders', JSON.stringify(S.orders)); renderAllOrdersFunction(); renderDashboardFunction(); toast('Pedido eliminado'); } function clearOrdersFunction() { if(confirm('¿Eliminar todos los pedidos?')) { S.orders = []; S.totalRevenue = 0; localStorage.setItem('smi_orders', JSON.stringify(S.orders)); localStorage.setItem('smi_revenue', S.totalRevenue); renderAllOrdersFunction(); renderDashboardFunction(); toast('Pedidos eliminados'); } } function exportOrdersFunction() { const rows = [['ID', 'Fecha', 'Cliente', 'Productos', 'Total', 'Estado']]; S.orders.forEach(o => rows.push([o.id, o.date, o.customer, o.items.map(i => `${i.name} x${i.qty}`).join('; '), o.total, o.status])); const csv = rows.map(r => r.map(c => `"${c}"`).join(',')).join('\n'); const a = document.createElement('a'); a.href = 'data:text/csv;charset=utf-8,' + encodeURIComponent(csv); a.download = 'pedidos.csv'; a.click(); toast('CSV exportado'); } function renderCatManageFunction() { const el = document.getElementById('cat-manage-list'); if(!S.categories.length) { el.innerHTML = '
Sin categorías
'; } else { el.innerHTML = S.categories.map(c => `
${c.emoji}
${c.name}
${S.products.filter(p => p.cat === c.id).length} productos
`).join(''); } } function addCategoryFunction() { const name = document.getElementById('c-name').value.trim(); if(!name) { toast('⚠️ Ingrese nombre'); return; } S.categories.push({ id: 'c' + Date.now(), name, emoji: document.getElementById('c-emoji').value.trim() || '📂', desc: document.getElementById('c-desc').value.trim() }); localStorage.setItem('smi_categories', JSON.stringify(S.categories)); renderCatManageFunction(); renderCatScrollerFunction(); document.getElementById('c-name').value = ''; document.getElementById('c-emoji').value = '🏍'; document.getElementById('c-desc').value = ''; toast('✅ Categoría creada'); } function deleteCategoryFunction(id) { const cnt = S.products.filter(p => p.cat === id).length; if(cnt > 0 && !confirm(`Tiene ${cnt} productos. ¿Eliminar?`)) return; S.categories = S.categories.filter(c => c.id !== id); localStorage.setItem('smi_categories', JSON.stringify(S.categories)); renderCatManageFunction(); renderCatScrollerFunction(); toast('Categoría eliminada'); } function loadAdminConfigFunction() { const cfg = S.config; document.getElementById('cfg-name').value = cfg.name || ''; document.getElementById('cfg-tagline').value = cfg.tagline || ''; document.getElementById('cfg-logo-emoji').value = cfg.logoEmoji || '🏍'; document.getElementById('cfg-color').value = cfg.color || '#E03A1E'; document.getElementById('cfg-wa').value = cfg.wa || ''; document.getElementById('cfg-wa-msg').value = cfg.waMsg || ''; document.getElementById('cfg-banner-title').value = cfg.bannerTitle || ''; document.getElementById('cfg-banner-sub').value = cfg.bannerSub || ''; document.getElementById('cfg-banner-badge').value = cfg.bannerBadge || ''; document.getElementById('cfg-ship').value = cfg.shipping || 5; document.getElementById('cfg-free-ship').value = cfg.freeShip || 100; document.getElementById('cfg-social-slogan').value = cfg.socialSlogan || '🏍 Súper Gato Motors - Potencia y confianza con el Dr. Neiser López 🔥'; } async function saveConfigFunction() { const newPass = document.getElementById('cfg-pass').value; const cleanWa = sanitizeWhatsAppNumber(document.getElementById('cfg-wa').value); const bannerFile = document.getElementById('cfg-banner-file').files[0]; let bannerImageUrl = S.config.bannerImage; if(bannerFile) { toast('📤 Subiendo imagen...'); const uploadedUrl = await uploadBannerImage(bannerFile); if (uploadedUrl) bannerImageUrl = uploadedUrl; } S.config = { ...S.config, name: document.getElementById('cfg-name').value, tagline: document.getElementById('cfg-tagline').value, logoEmoji: document.getElementById('cfg-logo-emoji').value, color: document.getElementById('cfg-color').value, wa: cleanWa, waMsg: document.getElementById('cfg-wa-msg').value, bannerTitle: document.getElementById('cfg-banner-title').value, bannerSub: document.getElementById('cfg-banner-sub').value, bannerBadge: document.getElementById('cfg-banner-badge').value, bannerImage: bannerImageUrl, shipping: parseFloat(document.getElementById('cfg-ship').value) || 5, freeShip: parseFloat(document.getElementById('cfg-free-ship').value) || 100, socialSlogan: document.getElementById('cfg-social-slogan').value }; if(newPass) S.config.pass = newPass; localStorage.setItem('smi_config', JSON.stringify(S.config)); await supabaseClient.from('site_config').upsert({ id: 1, ...S.config }); applyThemeFunction(); toast('✅ Configuración guardada'); } function exportDataFunction() { const a = document.createElement('a'); a.href = 'data:application/json;charset=utf-8,' + encodeURIComponent(JSON.stringify({ config: S.config, welcomeCard: S.welcomeCard, categories: S.categories, products: S.products, orders: S.orders, totalRevenue: S.totalRevenue }, null, 2)); a.download = 'backup.json'; a.click(); toast('✅ Exportado'); } function importDataFunction(evt) { const file = evt.target.files[0]; if(!file) return; const reader = new FileReader(); reader.onload = e => { try { const data = JSON.parse(e.target.result); if(data.config) S.config = data.config; if(data.welcomeCard) S.welcomeCard = data.welcomeCard; if(data.categories) S.categories = data.categories; if(data.products) S.products = data.products; if(data.orders) S.orders = data.orders; if(data.totalRevenue) S.totalRevenue = data.totalRevenue; localStorage.setItem('smi_config', JSON.stringify(S.config)); localStorage.setItem('smi_welcome', JSON.stringify(S.welcomeCard)); localStorage.setItem('smi_categories', JSON.stringify(S.categories)); localStorage.setItem('smi_products', JSON.stringify(S.products)); localStorage.setItem('smi_orders', JSON.stringify(S.orders)); localStorage.setItem('smi_revenue', S.totalRevenue); loadAdminConfigFunction(); loadWelcomeConfigFunction(); renderDashboardFunction(); renderProdManageFunction(); renderCatManageFunction(); renderAllOrdersFunction(); renderCatScrollerFunction(); renderProductsFunction(); applyThemeFunction(); toast('✅ Importado'); } catch(err) { toast('❌ Archivo inválido'); } }; reader.readAsText(file); } function resetAllFunction() { if(confirm('¿RESETEAR TODO?')) { S = { config: { ...DEFAULT_CONFIG }, welcomeCard: { ...DEFAULT_CONFIG.welcomeCard }, categories: [...DEFAULT_CONFIG.categories], products: [], orders: [], totalRevenue: 0 }; localStorage.clear(); loadAdminConfigFunction(); loadWelcomeConfigFunction(); renderDashboardFunction(); renderProdManageFunction(); renderCatManageFunction(); renderAllOrdersFunction(); renderCatScrollerFunction(); renderProductsFunction(); applyThemeFunction(); toast('♻️ Resetado'); } } // ========== EVENT LISTENERS ========== document.getElementById('closeWelcomeBtn')?.addEventListener('click', () => window.closeWelcomeCard()); document.getElementById('welcomeBtn')?.addEventListener('click', () => window.closeWelcomeCard()); document.getElementById('cartBtn')?.addEventListener('click', () => window.openDrawer()); document.getElementById('drawerClose')?.addEventListener('click', () => window.closeDrawer()); document.getElementById('adminLink')?.addEventListener('click', () => window.openAdmin()); document.getElementById('backToStoreBtn')?.addEventListener('click', () => window.closeAdmin()); document.getElementById('modalClose')?.addEventListener('click', () => window.closeModal()); document.getElementById('editCancelBtn')?.addEventListener('click', () => window.closeEditModal()); document.getElementById('editUpdateBtn')?.addEventListener('click', () => window.updateProduct()); document.getElementById('addProductBtn')?.addEventListener('click', () => window.addProduct()); document.getElementById('addCategoryBtn')?.addEventListener('click', () => window.addCategory()); document.getElementById('saveWelcomeBtn')?.addEventListener('click', () => window.saveWelcomeConfig()); document.getElementById('previewWelcomeBtn')?.addEventListener('click', () => window.previewWelcomeCard()); document.getElementById('exportOrdersBtn')?.addEventListener('click', () => window.exportOrders()); document.getElementById('clearOrdersBtn')?.addEventListener('click', () => window.clearOrders()); document.getElementById('exportDataBtn')?.addEventListener('click', () => window.exportData()); document.getElementById('importFileInput')?.addEventListener('change', (e) => window.importData(e)); document.getElementById('resetAllBtn')?.addEventListener('click', () => window.resetAll()); document.getElementById('saveConfigBtn')?.addEventListener('click', () => window.saveConfig()); document.getElementById('saveConfigBtn2')?.addEventListener('click', () => window.saveConfig()); document.getElementById('saveConfigBtn3')?.addEventListener('click', () => window.saveConfig()); document.getElementById('saveConfigBtn4')?.addEventListener('click', () => window.saveConfig()); document.getElementById('editAddImgBtn')?.addEventListener('click', () => document.getElementById('edit-gallery-input').click()); document.getElementById('pAddImgBtn')?.addEventListener('click', () => document.getElementById('p-gallery-input').click()); document.getElementById('search-inp')?.addEventListener('input', () => renderProductsFunction()); document.querySelectorAll('.admin-nav-btn').forEach(btn => { btn.addEventListener('click', () => window.showPanel(btn.getAttribute('data-panel'))); }); document.getElementById('drawerBg')?.addEventListener('click', (e) => window.bgClose(e, 'drawerBg')); document.getElementById('modalBg')?.addEventListener('click', (e) => window.bgClose(e, 'modalBg')); document.getElementById('editModal')?.addEventListener('click', (e) => { if(e.target === document.getElementById('editModal')) window.closeEditModal(); }); // ========== INICIALIZACIÓN ========== async function init() { const savedConfig = localStorage.getItem('smi_config'); if (savedConfig) S.config = { ...S.config, ...JSON.parse(savedConfig) }; const savedWelcome = localStorage.getItem('smi_welcome'); if (savedWelcome) S.welcomeCard = { ...S.welcomeCard, ...JSON.parse(savedWelcome) }; const savedCats = localStorage.getItem('smi_categories'); if (savedCats) S.categories = JSON.parse(savedCats); const savedOrders = localStorage.getItem('smi_orders'); if (savedOrders) S.orders = JSON.parse(savedOrders); const savedRevenue = localStorage.getItem('smi_revenue'); if (savedRevenue) S.totalRevenue = parseFloat(savedRevenue); await loadProductsFromAPI(true); applyThemeFunction(); console.log('🚀 Optimizado para móvil - Carga instantánea de todas las imágenes'); } init();