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 @@
|