diff --git a/pos/static/css/pos-ui.css b/pos/static/css/pos-ui.css index 32f6652..0bf19ea 100644 --- a/pos/static/css/pos-ui.css +++ b/pos/static/css/pos-ui.css @@ -819,3 +819,43 @@ input:disabled, select:disabled, textarea:disabled { background: var(--color-surface-1, #1a1a1a); border-bottom: 2px solid var(--color-primary, #F5A623); } + +/* Enhanced depth layers for both themes */ +[data-theme="modern"] { + --shadow-sm: 0 1px 2px rgba(0,0,0,0.3); + --shadow-md: 0 4px 12px rgba(0,0,0,0.4); + --shadow-lg: 0 8px 24px rgba(0,0,0,0.5); + --shadow-xl: 0 16px 48px rgba(0,0,0,0.6); +} +[data-theme="industrial"] { + --shadow-sm: 0 1px 2px rgba(0,0,0,0.4); + --shadow-md: 0 4px 12px rgba(0,0,0,0.5); + --shadow-lg: 0 8px 24px rgba(0,0,0,0.6); + --shadow-xl: 0 16px 48px rgba(0,0,0,0.7); +} + +/* Smooth theme transition (only on properties that don't cause flash) */ +.card, .glass-card, .btn, .icon-btn, .kpi-card, .alert-card, .meli-card { + transition: background-color 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease; +} + +/* Improved text rendering in dark */ +body { + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +/* Focus visible for accessibility */ +*:focus-visible { + outline: 2px solid var(--color-primary, #F5A623); + outline-offset: 2px; +} + +/* Reduced motion preference */ +@media (prefers-reduced-motion: reduce) { + *, *::before, *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } +} diff --git a/pos/static/js/customers.js b/pos/static/js/customers.js index 34caada..ec22861 100644 --- a/pos/static/js/customers.js +++ b/pos/static/js/customers.js @@ -94,6 +94,8 @@ const Customers = (() => { } } + var selectedCustomers = new Set(); + function renderCustomerRow(c) { const tier = tierMap[c.price_tier] || 'Mostrador'; const tClass = tierClass[c.price_tier] || 'mostrador'; @@ -104,7 +106,9 @@ const Customers = (() => { const creditClass = usedPct >= 80 ? 'none' : usedPct >= 60 ? 'low' : ''; const num = String(c.id).padStart(5, '0'); const selClass = (currentCustomer && currentCustomer.id === c.id) ? 'selected' : ''; - return '' + + const isChecked = selectedCustomers.has(c.id) ? 'checked' : ''; + return '' + + '' + '' + num + '' + '' + '
' + (c.name || '') + '
' + @@ -801,6 +805,41 @@ const Customers = (() => { showPaymentModal, closePayment, recordPayment, }; + // Bulk selection + publicApi.toggleCustomerSelection = function(id) { + if (selectedCustomers.has(id)) selectedCustomers.delete(id); + else selectedCustomers.add(id); + updateBulkToolbar(); + }; + publicApi.toggleSelectAll = function() { + var cb = document.getElementById('selectAllCustomers'); + var allChecked = cb.checked; + if (customersVS && customersVS.data) { + customersVS.data.forEach(function(c) { + if (allChecked) selectedCustomers.add(c.id); + else selectedCustomers.delete(c.id); + }); + customersVS.refresh(); + } + updateBulkToolbar(); + }; + function updateBulkToolbar() { + var container = document.getElementById('customersBulkToolbar'); + if (!container) return; + var count = selectedCustomers.size; + if (count === 0) { container.innerHTML = ''; return; } + container.innerHTML = renderBulkToolbar(count, + '' + + '' + ); + } + publicApi.clearSelection = function() { + selectedCustomers.clear(); + document.getElementById('selectAllCustomers').checked = false; + if (customersVS) customersVS.refresh(); + updateBulkToolbar(); + }; + // Expose globally for inline HTML onclick handlers window.Customers = publicApi; return publicApi; diff --git a/pos/static/js/inventory.js b/pos/static/js/inventory.js index 847e569..e898132 100644 --- a/pos/static/js/inventory.js +++ b/pos/static/js/inventory.js @@ -1314,6 +1314,15 @@ html += ''; html += ''; + // Action buttons + html += '
'; + html += ''; + if (data.image_url) { + html += ''; + } + html += ''; + html += '
'; + // Product info header html += '
'; html += '
ID Inventario' + data.id + '
'; @@ -1535,9 +1544,76 @@ window.autoMatchCompat = autoMatchCompat; window.removeCompat = removeCompat; + // โ”€โ”€โ”€ Product Timeline โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + window.showProductTimeline = function(itemId) { + var modal = document.getElementById('productTimelineModal'); + var body = document.getElementById('productTimelineBody'); + body.innerHTML = '
'; + modal.classList.add('is-open'); + + apiFetch(API + '/items/' + itemId + '/history').then(function(data) { + var history = (data && data.data) ? data.data : []; + var html = '
'; + html += '
Producto creado
Registro inicial en inventario
'; + history.forEach(function(h) { + var color = h.quantity > 0 ? 'timeline__dot--green' : (h.quantity < 0 ? 'timeline__dot--red' : 'timeline__dot--blue'); + var title = (h.type || 'Movimiento') + ' ยท ' + (h.quantity > 0 ? '+' : '') + h.quantity + ' unidades'; + html += '
' + + '
' + esc(h.date) + ' ยท ' + esc(h.employee) + '
' + + '
' + esc(title) + '
' + + (h.notes ? '
' + esc(h.notes) + '
' : '') + + '
'; + }); + html += '
'; + body.innerHTML = html; + }); + }; + + // โ”€โ”€โ”€ Image Comparator โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + window.showImageCompare = function(imageUrl) { + var modal = document.getElementById('imageCompareModal'); + document.getElementById('imgCompareNew').src = imageUrl + '?t=' + Date.now(); + document.getElementById('imgCompareOld').src = imageUrl + '?t=' + (Date.now() - 1); + modal.classList.add('is-open'); + setTimeout(function() { if (typeof initImageComparator === 'function') initImageComparator('#imgCompareContainer'); }, 100); + }; + + // โ”€โ”€โ”€ Infinite Scroll โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + var _infiniteScrollInstance = null; + function setupInfiniteScroll() { + if (_infiniteScrollInstance) _infiniteScrollInstance.disconnect(); + var sentinel = document.createElement('div'); + sentinel.id = 'inventoryScrollSentinel'; + sentinel.style.cssText = 'height:1px;'; + var wrapper = document.querySelector('.table-wrapper'); + if (wrapper) wrapper.appendChild(sentinel); + _infiniteScrollInstance = new InfiniteScroll({ + sentinelParent: wrapper, + onLoad: function(done) { + if (!currentSearch && currentPage < (window._inventoryTotalPages || 999)) { + loadItems(currentPage + 1); + } + if (done) done(); + } + }); + } + + // โ”€โ”€โ”€ Saved Filters โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ + function renderSavedFilters() { + var container = document.getElementById('savedFiltersContainer'); + if (!container) return; + SavedFilters.renderChips('savedFiltersContainer', function(filters) { + if (filters.search) { + var el = document.getElementById('productSearch'); + if (el) { el.value = filters.search; loadItems(1, filters.search); } + } + }); + } + // ===================================================================== // INIT โ€” load stock on page load // ===================================================================== loadItems(1); + renderSavedFilters(); })(); diff --git a/pos/templates/customers.html b/pos/templates/customers.html index a0a6a92..c3e1c03 100644 --- a/pos/templates/customers.html +++ b/pos/templates/customers.html @@ -306,9 +306,11 @@
+
+ diff --git a/pos/templates/inventory.html b/pos/templates/inventory.html index ffaf1bf..2e1dd67 100644 --- a/pos/templates/inventory.html +++ b/pos/templates/inventory.html @@ -280,6 +280,7 @@ =================================================================== -->
+
# Nombre RFC