diff --git a/pos/static/css/catalog.css b/pos/static/css/catalog.css index b27c8a1..882186b 100644 --- a/pos/static/css/catalog.css +++ b/pos/static/css/catalog.css @@ -88,7 +88,8 @@ .breadcrumb__link:hover { color: var(--color-primary); } .breadcrumb__sep { color: var(--color-text-disabled); } .breadcrumb__current { color: var(--color-text-primary); font-weight: var(--font-weight-semibold); } - + .breadcrumb__back { display: inline-flex; align-items: center; gap: 4px; padding: 2px 10px; background: transparent; border: 1px solid var(--color-border); border-radius: var(--radius-sm); color: var(--color-text-muted); font-size: var(--text-body-sm); cursor: pointer; transition: var(--transition-fast); } + .breadcrumb__back:hover { background: var(--color-primary-muted); color: var(--color-primary); } .header-actions { display: flex; align-items: center; gap: var(--space-3); } /* ── Catalog mode toggle (OEM / Local) ── */ @@ -362,13 +363,29 @@ .bodega-table th { text-align: left; font-weight: var(--font-weight-semibold); color: var(--color-text-muted); font-size: var(--text-caption); text-transform: uppercase; letter-spacing: var(--tracking-wider); padding: var(--space-2) var(--space-2); border-bottom: 1px solid var(--color-border); } .bodega-table td { padding: var(--space-2); border-bottom: 1px solid var(--color-border); color: var(--color-text-primary); } - /* Alternatives list */ +/* Alternatives list */ .alt-item { display: flex; align-items: center; justify-content: space-between; padding: var(--space-2) 0; border-bottom: 1px solid var(--color-border); } .alt-item:last-child { border-bottom: none; } .alt-item__pn { font-weight: var(--font-weight-semibold); color: var(--color-text-primary); font-size: var(--text-body-sm); } .alt-item__mfr { font-size: var(--text-caption); color: var(--color-text-muted); } .alt-item__stock { font-size: var(--text-caption); } + /* Compatible vehicles pagination */ + .compat-pager { + display: flex; align-items: center; justify-content: space-between; + gap: var(--space-2); margin-top: var(--space-4); padding-top: var(--space-3); + border-top: 1px solid var(--color-border); + } + .compat-pager__btn { + font-family: inherit; font-size: var(--text-caption); font-weight: var(--font-weight-semibold); + color: var(--color-text-primary); background: var(--color-surface-2); + border: 1px solid var(--color-border); border-radius: var(--radius-sm); + padding: var(--space-2) var(--space-3); cursor: pointer; white-space: nowrap; + } + .compat-pager__btn:hover:not(:disabled) { background: var(--color-surface-3, var(--color-surface-2)); } + .compat-pager__btn:disabled { opacity: 0.4; cursor: not-allowed; } + .compat-pager__info { font-size: var(--text-caption); color: var(--color-text-muted); text-align: center; flex: 1; } + /* Add to cart section */ .detail-footer { padding: var(--space-4) var(--space-5); border-top: 1px solid var(--color-border); diff --git a/pos/static/js/brand-catalog.js b/pos/static/js/brand-catalog.js index 8b32ade..5eeac00 100644 --- a/pos/static/js/brand-catalog.js +++ b/pos/static/js/brand-catalog.js @@ -52,7 +52,7 @@ return; } this.el('brandCatalogOverlay').style.display = 'block'; - document.body.style.overflow = 'hidden'; + //document.body.style.overflow = 'hidden'; this.loadBrands(); }, @@ -61,7 +61,7 @@ document.body.style.overflow = ''; this.reset(); }, - + reset: function() { this.state = 'brands'; this.nav = { brand: null, brandId: null, model: null, modelId: null, modelVariantIds: null, year: null, yearId: null, engine: null, myeId: null, category: null, categoryId: null }; diff --git a/pos/static/js/catalog.js b/pos/static/js/catalog.js index 4ebc938..32746ca 100644 --- a/pos/static/js/catalog.js +++ b/pos/static/js/catalog.js @@ -276,7 +276,20 @@ if (nav.nxPartType) parts.push({ label: nav.nxPartType.name, action: null }); else if (nav.partType) parts.push({ label: nav.partType.name, action: null }); + //Botón para retroceder + var backAction = null; + if (parts.length > 1) { + var prev = parts[parts.length - 2]; + backAction = prev ? prev.action : 'loadBrands'; + } + var html = ''; + //Botón para retroceder + if (backAction) { + html += ''; + html += ''; + } + //-------- for (var i = 0; i < parts.length; i++) { if (i > 0) html += ''; if (i < parts.length - 1 && parts[i].action) { @@ -303,6 +316,7 @@ else if (action === 'loadNxPartTypes') { resetNavFrom('part_types'); loadNexpartPartTypes(); } }); }); + } function resetNav() { @@ -1272,10 +1286,8 @@ html += ''; } - // Compatibilities — deduplicate by (make, model, year, engine) +// Compatibilities — deduplicate by (make, model, year, engine) if (p.compatibilities && p.compatibilities.length) { - html += '
'; - html += '
Vehiculos compatibles
'; var seenCompat = {}; var uniqCompat = []; p.compatibilities.forEach(function(c) { @@ -1284,22 +1296,69 @@ seenCompat[key] = true; uniqCompat.push(c); }); - var currentMake = ''; - uniqCompat.forEach(function(c) { - if (c.make !== currentMake) { - currentMake = c.make; - html += '
' + esc(c.make) + '
'; - } - html += '
' + - esc(c.model) + ' ' + c.year + ' ' + esc(c.engine || '') + '
'; - }); + html += '
'; + html += '
Vehiculos compatibles
'; + html += '
'; + html += '
'; html += '
'; + compatFullList = uniqCompat; + } else { + compatFullList = []; } detailBody.innerHTML = html; + + if (compatFullList.length) { + renderCompatPage(1); + } }); } + // --- Pagination for "Vehiculos compatibles" --- + var compatFullList = []; + var compatPageSize = 15; + var compatCurrentPage = 1; + + function renderCompatPage(page) { + var listEl = document.getElementById('compat-list'); + var pagerEl = document.getElementById('compat-pager'); + if (!listEl || !pagerEl) return; + + var totalItems = compatFullList.length; + var totalPages = Math.max(1, Math.ceil(totalItems / compatPageSize)); + page = Math.min(Math.max(1, page), totalPages); + compatCurrentPage = page; + + var start = (page - 1) * compatPageSize; + var pageItems = compatFullList.slice(start, start + compatPageSize); + + var listHtml = ''; + var currentMake = ''; + pageItems.forEach(function(c) { + if (c.make !== currentMake) { + currentMake = c.make; + listHtml += '
' + esc(c.make) + '
'; + } + listHtml += '
' + + esc(c.model) + ' ' + c.year + ' ' + esc(c.engine || '') + '
'; + }); + listEl.innerHTML = listHtml; + + if (totalPages > 1) { + pagerEl.innerHTML = + '' + + 'Pagina ' + page + ' de ' + totalPages + ' (' + totalItems + ' vehiculos)' + + ''; + + var prevBtn = document.getElementById('compat-prev'); + var nextBtn = document.getElementById('compat-next'); + if (prevBtn) prevBtn.addEventListener('click', function () { renderCompatPage(compatCurrentPage - 1); }); + if (nextBtn) nextBtn.addEventListener('click', function () { renderCompatPage(compatCurrentPage + 1); }); + } else { + pagerEl.innerHTML = ''; + } + } + function closeDetail() { detailPanel.classList.remove('is-open'); detailOverlay.classList.remove('is-visible'); @@ -1802,20 +1861,22 @@ }); } + function vsYearChanged() { var yearId = vsYear.value; - vsBrand.innerHTML = ''; - vsModel.innerHTML = ''; - vsEngine.innerHTML = ''; - vsBrand.disabled = true; - vsModel.disabled = true; - vsEngine.disabled = true; - vsClear.style.display = yearId ? '' : 'none'; + vsClear.style.display = (yearId || vsBrand.value || vsModel.value) ? '' : 'none'; - if (!yearId) return; - - // Load brands filtered by year + // Resetear marca solo si no hay nada seleccionado en ella vsBrand.disabled = false; + vsEngine.innerHTML = ''; + vsEngine.disabled = true; + + if (!yearId) { + //carga todo + vsLoadAllBrands(); + return; + } + // Filtrar marcas por año apiFetch(API + '/brands?year_id=' + yearId + '&mode=' + catalogMode).then(function (data) { var brands = data.data || data; if (!brands) return; @@ -1823,21 +1884,39 @@ brands.map(function (b) { return ''; }).join(''); + // si hay marca seleccionada despliega modelos + if (vsBrand.value) vsBrandChanged(); + }); + } + function vsLoadAllBrands() { + apiFetch(API + '/brands?mode=' + catalogMode).then(function (data) { + var brands = data.data || data; + if (!brands) return; + var current = vsBrand.value; + vsBrand.innerHTML = '' + + brands.map(function (b) { + return ''; + }).join(''); + if (current) vsBrand.value = current; }); } function vsBrandChanged() { var brandId = vsBrand.value; var yearId = vsYear.value; + vsClear.style.display = (yearId || brandId) ? '' : 'none'; + vsModel.innerHTML = ''; vsEngine.innerHTML = ''; - vsModel.disabled = true; + vsModel.disabled = false; // cambio vsEngine.disabled = true; - if (!brandId) return; + if (!brandId) { + vsModel.disabled = true; + return; + } - // Load models filtered by brand AND year - vsModel.disabled = false; + // Cargar modelos con o sin año apiFetch(API + '/models?brand_id=' + brandId + (yearId ? '&year_id=' + yearId : '')).then(function (data) { var models = data.data || data; if (!models) return; @@ -1846,9 +1925,12 @@ var variants = (m.variant_ids || [m.id_model]).join(','); return ''; }).join(''); + // si hay modelo seleccionado despliega año + if (vsModel.value) vsModelChanged(); }); } + function vsModelChanged() { var modelId = vsModel.value; var yearVal = vsYear.value; @@ -1856,27 +1938,45 @@ var variantIds = selectedOption && selectedOption.dataset.variantIds ? selectedOption.dataset.variantIds.split(',').map(function(x){ return parseInt(x); }) : (modelId ? [parseInt(modelId)] : []); + vsEngine.innerHTML = ''; vsEngine.disabled = true; - if (!modelId || !yearVal || !variantIds.length) return; + if (!modelId || !variantIds.length) return; - vsEngine.disabled = false; - apiFetch(API + '/engines?model_id=' + variantIds.join(',') + '&year_id=' + yearVal).then(function (data) { - var engines = data.data || data; - if (!engines) return; - vsEngine.innerHTML = '' + - engines.map(function (e) { - var name = (e.name_engine && e.name_engine !== 'N/A') ? e.name_engine : 'Sin especificar'; - var label = name + (e.trim_level ? ' (' + e.trim_level + ')' : ''); - return ''; - }).join(''); - // If only 1 engine, auto-select - if (engines.length === 1) { - vsEngine.value = engines[0].id_mye; - vsEngineChanged(); - } - }); + // Si hay año carga motores usando todas las variantes del modelo + if (yearVal) { + vsEngine.disabled = false; + apiFetch(API + '/engines?model_id=' + variantIds.join(',') + '&year_id=' + yearVal).then(function (data) { + var engines = data.data || data; + if (!engines) return; + vsEngine.innerHTML = '' + + engines.map(function (e) { + var name = (e.name_engine && e.name_engine !== 'N/A') ? e.name_engine : 'Sin especificar'; + var label = name + (e.trim_level ? ' (' + e.trim_level + ')' : ''); + return ''; + }).join(''); + if (engines.length === 1) { + vsEngine.value = engines[0].id_mye; + vsEngineChanged(); + } + }); + } else { + // Sin año: cargar categorías a nivel modelo + var brandId = vsBrand.value; + if (!brandId) return; + nav.brand = { id: parseInt(brandId), name: vsBrand.options[vsBrand.selectedIndex].text }; + nav.model = { id: parseInt(modelId), name: vsModel.options[vsModel.selectedIndex].text }; + nav.year = null; + nav.engine = null; + nav.level = 'categories'; + pushNavState(); + loadCategoriesForMode(); + setTimeout(function () { + var body = document.getElementById('pageBody'); + if (body) body.scrollIntoView({ behavior: 'smooth', block: 'start' }); + }, 300); + } } function vsEngineChanged() { diff --git a/pos/static/js/config.js b/pos/static/js/config.js index 836dbd2..e61ca6f 100644 --- a/pos/static/js/config.js +++ b/pos/static/js/config.js @@ -57,7 +57,7 @@ const Config = (() => { // ------------------------------------------------------------------------- function setTheme(theme) { document.documentElement.setAttribute('data-theme', theme); - try { localStorage.setItem('nexus-theme', theme); } catch(e) {} + try { localStorage.setItem('pos_theme', theme); } catch(e) {} document.querySelectorAll('.theme-btn').forEach(function(btn) { btn.classList.toggle('is-active', btn.dataset.themeTarget === theme); @@ -802,7 +802,7 @@ const Config = (() => { // Restore theme try { - var saved = localStorage.getItem('nexus-theme'); + var saved = localStorage.getItem('pos_theme'); if (saved === 'industrial' || saved === 'modern') { setTheme(saved); } diff --git a/pos/static/js/dashboard.js b/pos/static/js/dashboard.js index 96c8591..4309215 100644 --- a/pos/static/js/dashboard.js +++ b/pos/static/js/dashboard.js @@ -48,7 +48,7 @@ const Dashboard = (() => { // ------------------------------------------------------------------------- function setTheme(theme) { document.documentElement.setAttribute('data-theme', theme); - try { localStorage.setItem('nexus-theme', theme); } catch(e) {} + try { localStorage.setItem('pos_theme', theme); } catch(e) {} const btnInd = document.getElementById('btn-industrial'); const btnMod = document.getElementById('btn-modern'); if (btnInd) btnInd.classList.toggle('active', theme === 'industrial'); @@ -689,7 +689,7 @@ const Dashboard = (() => { // Restore theme try { - const saved = localStorage.getItem('nexus-theme'); + const saved = localStorage.getItem('pos_theme'); if (saved === 'industrial' || saved === 'modern') { setTheme(saved); } diff --git a/pos/static/js/reports.js b/pos/static/js/reports.js index 7e78c2d..4b9cf3e 100644 --- a/pos/static/js/reports.js +++ b/pos/static/js/reports.js @@ -61,7 +61,7 @@ const Reports = (() => { // ------------------------------------------------------------------------- function setTheme(theme) { document.documentElement.setAttribute('data-theme', theme); - try { localStorage.setItem('nexus-theme', theme); } catch(e) {} + try { localStorage.setItem('pos_theme', theme); } catch(e) {} var btnInd = document.getElementById('btn-industrial'); var btnMod = document.getElementById('btn-modern'); if (btnInd) btnInd.classList.toggle('is-active', theme === 'industrial'); @@ -745,7 +745,7 @@ const Reports = (() => { // Restore theme try { - var saved = localStorage.getItem('nexus-theme') || 'industrial'; + var saved = localStorage.getItem('pos_theme') || 'industrial'; setTheme(saved); } catch(e) {} diff --git a/pos/templates/catalog.html b/pos/templates/catalog.html index c203525..911209d 100644 --- a/pos/templates/catalog.html +++ b/pos/templates/catalog.html @@ -145,14 +145,14 @@
-
-
@@ -302,7 +302,7 @@
-