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) <noreply@anthropic.com>
This commit is contained in:
46
pos/static/js/offline-banner.js
Normal file
46
pos/static/js/offline-banner.js
Normal file
@@ -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 = '<strong>Conexion perdida</strong> — 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 = '<strong>Conexion restaurada</strong> — 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);
|
||||||
|
})();
|
||||||
@@ -1388,6 +1388,101 @@
|
|||||||
.sidebar-overlay.is-open {
|
.sidebar-overlay.is-open {
|
||||||
display: block;
|
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; }
|
||||||
</style>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
@@ -1871,6 +1966,14 @@
|
|||||||
</div>
|
</div>
|
||||||
<!-- /product-grid -->
|
<!-- /product-grid -->
|
||||||
|
|
||||||
|
<!-- Empty State (shown when no results) -->
|
||||||
|
<div class="empty-state" id="emptyState">
|
||||||
|
<div class="empty-state__icon">🔎</div>
|
||||||
|
<div class="empty-state__title" id="emptyStateTitle">No se encontraron productos</div>
|
||||||
|
<div class="empty-state__subtitle" id="emptyStateSubtitle">Intenta con otro termino de busqueda o verifica el numero de parte</div>
|
||||||
|
<button class="empty-state__action" onclick="document.querySelector('.search-box input').value=''; document.querySelector('.search-box input').focus();">Limpiar filtros</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- Pagination -->
|
<!-- Pagination -->
|
||||||
<nav class="pagination" aria-label="Paginación de resultados">
|
<nav class="pagination" aria-label="Paginación de resultados">
|
||||||
<button class="page-item page-item--wide is-disabled" aria-label="Página anterior" disabled>
|
<button class="page-item page-item--wide is-disabled" aria-label="Página anterior" disabled>
|
||||||
@@ -2069,5 +2172,13 @@
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!-- Offline Banner -->
|
||||||
|
<div id="offlineBanner" class="banner banner--warning" style="display:none;position:fixed;top:0;left:0;right:0;z-index:9999;border-radius:0;animation:none;">
|
||||||
|
<span class="banner__icon"></span>
|
||||||
|
<span class="banner__text" id="offlineBannerText"><strong>Modo offline</strong> — Funciones limitadas. Solo consultas en cache disponibles.</span>
|
||||||
|
<button class="banner__dismiss" onclick="document.getElementById('offlineBanner').style.display='none'" aria-label="Cerrar">×</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<script src="/pos/static/js/offline-banner.js"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user