// /home/Autopartes/pos/static/js/catalog.js // Catalog UI: browsable inventory with cart, barcode scanner, external lookup (function () { 'use strict'; const API = '/pos/api'; const token = localStorage.getItem('pos_token'); if (!token) { window.location.href = '/pos/login'; return; } const headers = { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' }; // ─── State ─── let currentPage = 1; let currentFilters = {}; let cartItems = JSON.parse(localStorage.getItem('pos_cart') || '[]'); let barcodeBuffer = ''; let barcodeTimeout = null; // ─── API helpers ─── async function apiFetch(url, opts) { const resp = await fetch(url, Object.assign({ headers: headers }, opts || {})); if (resp.status === 401) { localStorage.removeItem('pos_token'); window.location.href = '/pos/login'; return null; } return resp.json(); } // ─── Catalog loading ─── async function loadCatalog(page, filters) { currentPage = page || 1; currentFilters = filters || currentFilters; const params = new URLSearchParams({ page: currentPage, per_page: 30 }); if (currentFilters.q) params.set('q', currentFilters.q); if (currentFilters.category) params.set('category', currentFilters.category); if (currentFilters.brand) params.set('brand', currentFilters.brand); if (currentFilters.vehicle_brand) params.set('vehicle_brand', currentFilters.vehicle_brand); const data = await apiFetch(API + '/catalog/search?' + params.toString()); if (!data) return; renderGrid(data.data || []); renderPagination(data.pagination || {}); renderActiveFilters(); } function renderGrid(items) { const grid = document.getElementById('catalogGrid'); if (!items.length) { grid.innerHTML = '

No se encontraron productos

'; return; } grid.innerHTML = items.map(function (it) { var stockClass = it.stock <= 0 ? 'stock-badge--zero' : (it.low_stock ? 'stock-badge--low' : 'stock-badge--ok'); var stockLabel = it.stock <= 0 ? 'Agotado' : it.stock + ' ' + (it.unit || 'PZA'); return '
' + (it.image_url ? '' : '
Sin imagen
') + '
' + escHtml(it.name) + '
' + '
' + escHtml(it.part_number) + (it.brand ? ' · ' + escHtml(it.brand) : '') + '
' + '
' + '$' + fmt(it.price_1) + '' + '' + stockLabel + '' + '
'; }).join(''); // Store items for cart lookup window._catalogItems = {}; items.forEach(function (it) { window._catalogItems[it.id] = it; }); } function renderPagination(pg) { var el = document.getElementById('pagination'); if (!pg || pg.total_pages <= 1) { el.innerHTML = ''; return; } var html = ''; html += '' + pg.page + ' / ' + pg.total_pages + ''; html += ''; el.innerHTML = html; } function renderActiveFilters() { var el = document.getElementById('activeFilters'); var chips = []; if (currentFilters.category) chips.push('Cat: ' + currentFilters.category + ' ×'); if (currentFilters.brand) chips.push('' + escHtml(currentFilters.brand) + ' ×'); if (currentFilters.vehicle_brand) chips.push('Vehiculo: ' + escHtml(currentFilters.vehicle_brand) + ' ×'); el.innerHTML = chips.join(''); } // ─── Sidebar filters ─── async function loadCategories() { var data = await apiFetch(API + '/catalog/categories'); if (!data) return; var ul = document.getElementById('categoryList'); var cats = data.data || []; if (!cats.length) { ul.innerHTML = '
  • Sin categorias
  • '; return; } ul.innerHTML = '
  • Todas
  • ' + cats.map(function (c) { return '
  • Cat #' + c.id + ' (' + c.count + ')
  • '; }).join(''); } async function loadBrands() { var data = await apiFetch(API + '/catalog/brands'); if (!data) return; var ul = document.getElementById('brandList'); var brands = data.data || []; if (!brands.length) { ul.innerHTML = '
  • Sin marcas
  • '; return; } ul.innerHTML = '
  • Todas
  • ' + brands.map(function (b) { return '
  • ' + escHtml(b.name) + ' (' + b.count + ')
  • '; }).join(''); } // ─── Barcode scanner ─── async function lookupBarcode(code) { var data = await apiFetch(API + '/catalog/barcode/' + encodeURIComponent(code)); if (!data || data.error) { alert('Parte no encontrada: ' + code); return; } addToCart(data); } // Listen for rapid keypress (barcode scanners type fast, then Enter) document.addEventListener('keydown', function (e) { if (e.key === 'F1') { e.preventDefault(); document.getElementById('searchInput').focus(); return; } if (document.activeElement.tagName === 'INPUT' || document.activeElement.tagName === 'TEXTAREA') return; if (e.key === 'Enter' && barcodeBuffer.length >= 4) { lookupBarcode(barcodeBuffer.trim()); barcodeBuffer = ''; return; } if (e.key.length === 1) { barcodeBuffer += e.key; clearTimeout(barcodeTimeout); barcodeTimeout = setTimeout(function () { barcodeBuffer = ''; }, 200); } }); // ─── Cart ─── function addToCart(item) { var existing = cartItems.find(function (c) { return c.id === item.id; }); if (existing) { existing.quantity += 1; } else { cartItems.push({ id: item.id, part_number: item.part_number, name: item.name, brand: item.brand, price: item.price_1, tax_rate: item.tax_rate || 0.16, unit: item.unit || 'PZA', stock: item.stock, quantity: 1 }); } saveCart(); renderCart(); } function removeFromCart(index) { cartItems.splice(index, 1); saveCart(); renderCart(); } function updateQuantity(index, qty) { qty = parseInt(qty); if (qty <= 0) { removeFromCart(index); return; } cartItems[index].quantity = qty; saveCart(); renderCart(); } function clearCartFn() { cartItems = []; saveCart(); renderCart(); } function saveCart() { localStorage.setItem('pos_cart', JSON.stringify(cartItems)); } function renderCart() { var badge = document.getElementById('cartBadge'); var total = cartItems.reduce(function (s, c) { return s + c.quantity; }, 0); badge.textContent = total; badge.style.display = total > 0 ? 'flex' : 'none'; var container = document.getElementById('cartItems'); var empty = document.getElementById('cartEmpty'); var checkoutBtn = document.getElementById('checkoutBtn'); if (!cartItems.length) { container.innerHTML = ''; empty.style.display = 'block'; checkoutBtn.disabled = true; document.getElementById('cartSubtotal').textContent = '$0.00'; document.getElementById('cartTax').textContent = '$0.00'; document.getElementById('cartTotal').textContent = '$0.00'; return; } empty.style.display = 'none'; checkoutBtn.disabled = false; var subtotal = 0; var tax = 0; container.innerHTML = cartItems.map(function (c, i) { var lineTotal = c.price * c.quantity; var lineTax = lineTotal * c.tax_rate; subtotal += lineTotal; tax += lineTax; return '
    ' + '
    ' + '
    ' + escHtml(c.name) + '
    ' + '
    ' + escHtml(c.part_number) + '
    ' + '
    ' + '' + '' + c.quantity + '' + '' + '
    ' + '
    ' + '
    $' + fmt(lineTotal) + '
    ' + '' + '
    '; }).join(''); document.getElementById('cartSubtotal').textContent = '$' + fmt(subtotal); document.getElementById('cartTax').textContent = '$' + fmt(tax); document.getElementById('cartTotal').textContent = '$' + fmt(subtotal + tax); } function toggleCart() { document.getElementById('cartSidebar').classList.toggle('open'); } function goToCheckout() { localStorage.setItem('pos_cart', JSON.stringify(cartItems)); window.location.href = '/pos/sale'; } // ─── External availability ─── async function checkExternalAvailability(partNumber) { var pn = partNumber || currentFilters.q || ''; if (!pn) return; var section = document.getElementById('externalSection'); var results = document.getElementById('externalResults'); section.style.display = 'block'; results.innerHTML = '

    Buscando en bodegas...

    '; var data = await apiFetch(API + '/catalog/external-availability/' + encodeURIComponent(pn)); if (!data || !data.data || !data.data.length) { results.innerHTML = '

    No se encontraron resultados externos para "' + escHtml(pn) + '"

    '; return; } results.innerHTML = ''; } // ─── Helpers ─── function fmt(n) { return (parseFloat(n) || 0).toFixed(2); } function escHtml(s) { if (!s) return ''; var d = document.createElement('div'); d.textContent = s; return d.innerHTML; } // ─── Search input ─── var searchInput = document.getElementById('searchInput'); var searchTimeout = null; searchInput.addEventListener('input', function () { clearTimeout(searchTimeout); searchTimeout = setTimeout(function () { currentFilters.q = searchInput.value.trim(); loadCatalog(1, currentFilters); }, 350); }); searchInput.addEventListener('keydown', function (e) { if (e.key === 'Enter') { e.preventDefault(); clearTimeout(searchTimeout); currentFilters.q = searchInput.value.trim(); loadCatalog(1, currentFilters); } }); // ─── Expose globals for inline handlers ─── window._addToCart = function (id) { var it = window._catalogItems && window._catalogItems[id]; if (it) addToCart(it); }; window._loadPage = function (p) { loadCatalog(p); }; window._removeFilter = function (key) { delete currentFilters[key]; loadCatalog(1); loadCategories(); loadBrands(); }; window._filterCat = function (id) { if (id) currentFilters.category = id; else delete currentFilters.category; loadCatalog(1); loadCategories(); }; window._filterBrand = function (name) { if (name) currentFilters.brand = name; else delete currentFilters.brand; loadCatalog(1); loadBrands(); }; window._removeFromCart = removeFromCart; window._updateQty = updateQuantity; window.toggleCart = toggleCart; window.goToCheckout = goToCheckout; window.clearCart = clearCartFn; window.checkExternalAvailability = checkExternalAvailability; // ─── Init ─── renderCart(); loadCatalog(1, {}); loadCategories(); loadBrands(); })();