From 9641b0af802a72ff65bfc8beab449096a8bd9999 Mon Sep 17 00:00:00 2001 From: consultoria-as Date: Wed, 1 Apr 2026 07:28:41 +0000 Subject: [PATCH] feat(pos): integrate design system for clients, inventory, catalog + offline banner - Replace customers.html with design system clientes page, add slide panel + wire to customers.js - Replace inventory.html with design system inventario page, load inventory.js - Add empty state component to catalog product grid (hidden, shown when no results) - Add offline banner HTML/CSS to all three pages - Create offline-banner.js: listens online/offline events, auto-dismisses restored banner after 3s Co-Authored-By: Claude Opus 4.6 (1M context) --- pos/static/js/offline-banner.js | 46 + pos/templates/catalog.html | 111 ++ pos/templates/customers.html | 2695 +++++++++++++++++++++++++++++-- pos/templates/inventory.html | 2492 ++++++++++++++++++++++++++-- 4 files changed, 5000 insertions(+), 344 deletions(-) create mode 100644 pos/static/js/offline-banner.js diff --git a/pos/static/js/offline-banner.js b/pos/static/js/offline-banner.js new file mode 100644 index 0000000..d52bb2e --- /dev/null +++ b/pos/static/js/offline-banner.js @@ -0,0 +1,46 @@ +// /home/Autopartes/pos/static/js/offline-banner.js +// Global offline/online banner controller. +// Include this script in any page that has an #offlineBanner element. + +(function () { + 'use strict'; + + var banner = document.getElementById('offlineBanner'); + var bannerText = document.getElementById('offlineBannerText'); + if (!banner || !bannerText) return; + + var dismissTimer = null; + + function showOffline() { + clearTimeout(dismissTimer); + banner.className = 'banner banner--error'; + banner.style.display = 'flex'; + banner.style.animation = 'slideDown 0.35s ease-out forwards'; + bannerText.innerHTML = 'Conexion perdida — Intentando reconectar...'; + } + + function showOnline() { + clearTimeout(dismissTimer); + banner.className = 'banner banner--success'; + banner.style.display = 'flex'; + banner.style.animation = 'slideDown 0.35s ease-out forwards'; + bannerText.innerHTML = 'Conexion restaurada — Sincronizando datos...'; + + // Auto-dismiss after 3 seconds + dismissTimer = setTimeout(function () { + banner.style.animation = 'slideUp 0.3s ease-in forwards'; + banner.addEventListener('animationend', function onEnd() { + banner.style.display = 'none'; + banner.removeEventListener('animationend', onEnd); + }, { once: true }); + }, 3000); + } + + // Show warning immediately if already offline + if (!navigator.onLine) { + showOffline(); + } + + window.addEventListener('offline', showOffline); + window.addEventListener('online', showOnline); +})(); diff --git a/pos/templates/catalog.html b/pos/templates/catalog.html index bd3b9e3..958672e 100644 --- a/pos/templates/catalog.html +++ b/pos/templates/catalog.html @@ -1388,6 +1388,101 @@ .sidebar-overlay.is-open { display: block; } + + /* ========================================================================= + EMPTY STATE COMPONENT + ========================================================================= */ + + .empty-state { + display: none; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + max-width: 320px; + padding: var(--space-8) var(--space-4); + margin: var(--space-10) auto; + width: 100%; + } + + .empty-state.is-visible { + display: flex; + } + + .empty-state__icon { + font-size: 48px; + line-height: 1; + margin-bottom: var(--space-4); + opacity: 0.6; + filter: grayscale(30%); + } + + .empty-state__title { + font-family: var(--font-heading); + font-size: var(--text-h5); + font-weight: var(--heading-weight-secondary); + color: var(--color-text-primary); + margin-bottom: var(--space-2); + } + + .empty-state__subtitle { + font-size: var(--text-body-sm); + color: var(--color-text-muted); + line-height: 1.5; + margin-bottom: var(--space-5); + } + + .empty-state__action { + padding: var(--space-2) var(--space-5); + font-family: var(--font-body); + font-size: var(--text-body-sm); + font-weight: 500; + border-radius: var(--radius-md); + cursor: pointer; + transition: var(--transition-fast); + border: 1px solid var(--btn-secondary-border); + background: var(--btn-secondary-bg); + color: var(--btn-secondary-text); + } + + .empty-state__action:hover { + background: var(--btn-secondary-bg-hover); + box-shadow: var(--shadow-sm); + } + + /* ========================================================================= + OFFLINE BANNER + ========================================================================= */ + + @keyframes slideDown { from { transform: translateY(-100%); opacity: 0; } to { transform: translateY(0); opacity: 1; } } + @keyframes slideUp { from { transform: translateY(0); opacity: 1; } to { transform: translateY(-100%); opacity: 0; } } + + .banner { + display: flex; align-items: center; gap: var(--space-3); + padding: var(--space-3) var(--space-4); border-radius: var(--radius-md); + font-size: var(--text-body-sm); font-weight: 500; line-height: 1.4; + } + .banner--warning { + background: var(--color-warning-light, #fef9c3); color: var(--color-warning-dark, #854d0e); + border: 1px solid var(--color-warning, #eab308); + } + .banner--success { + background: var(--color-success-light, #dcfce7); color: var(--color-success-dark, #166534); + border: 1px solid var(--color-success, #22c55e); + } + .banner--error { + background: var(--color-error-light, #fef2f2); color: var(--color-error-dark, #991b1b); + border: 1px solid var(--color-error, #ef4444); + } + .banner--dismissing { animation: slideUp 0.3s ease-in forwards; } + .banner__icon { font-size: 18px; flex-shrink: 0; } + .banner__text { flex: 1; } + .banner__text strong { font-weight: 700; } + .banner__dismiss { + background: none; border: none; cursor: pointer; font-size: 18px; + padding: var(--space-1); opacity: 0.7; color: inherit; + } + .banner__dismiss:hover { opacity: 1; } @@ -1871,6 +1966,14 @@ + +
+
🔎
+
No se encontraron productos
+
Intenta con otro termino de busqueda o verifica el numero de parte
+ +
+