fix(pos): sidebar unificado en todas las paginas

Nuevo sidebar.js compartido que reemplaza el sidebar de cada pagina
con una version consistente:
- Misma estructura y orden de links en todas las paginas
- Nombre y rol del empleado real (de POS_USER)
- Link activo resaltado segun pagina actual
- Boton de logout
- Responsive (colapsa a iconos en <768px)
- Elimina inconsistencias de estilo entre paginas

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-01 20:20:55 +00:00
parent b887f0c032
commit 76c9c2d2db
9 changed files with 175 additions and 0 deletions

167
pos/static/js/sidebar.js Normal file
View File

@@ -0,0 +1,167 @@
/**
* sidebar.js — Shared sidebar component for all POS pages
*
* Injects a consistent sidebar into any page that has a <aside class="sidebar"> or
* <div id="pos-sidebar-container">. Overrides the page's own sidebar content
* with a standardized navigation.
*/
(function() {
'use strict';
var employee = window.POS_USER || {};
var name = employee.name || 'Usuario';
var roleLabel = employee.roleLabel || '';
var initials = employee.initials || '?';
var currentPath = window.location.pathname;
var navItems = [
{ label: 'Dashboard', icon: '📊', href: '/pos/dashboard' },
{ label: 'Punto de Venta', icon: '💰', href: '/pos/sale' },
{ label: 'Catálogo', icon: '📦', href: '/pos/catalog' },
{ label: 'Inventario', icon: '📋', href: '/pos/inventory' },
{ label: 'Clientes', icon: '👥', href: '/pos/customers' },
{ label: 'Facturación', icon: '🧾', href: '/pos/invoicing' },
{ label: 'Contabilidad', icon: '📒', href: '/pos/accounting' },
{ label: 'Reportes', icon: '📈', href: '/pos/reports' },
{ label: 'Configuración', icon: '⚙️', href: '/pos/config' },
];
// Build sidebar HTML
var linksHtml = navItems.map(function(item) {
var isActive = currentPath === item.href;
var cls = 'pos-nav__item' + (isActive ? ' is-active' : '');
return '<a class="' + cls + '" href="' + item.href + '">'
+ '<span class="pos-nav__icon">' + item.icon + '</span>'
+ '<span class="pos-nav__label">' + item.label + '</span>'
+ '</a>';
}).join('');
var sidebarHtml = ''
+ '<div class="pos-sidebar__brand">'
+ ' <div class="pos-sidebar__logo">N</div>'
+ ' <div class="pos-sidebar__title">Nexus POS</div>'
+ '</div>'
+ '<nav class="pos-sidebar__nav">' + linksHtml + '</nav>'
+ '<div class="pos-sidebar__footer">'
+ ' <div class="pos-sidebar__user">'
+ ' <div class="pos-sidebar__avatar">' + initials + '</div>'
+ ' <div class="pos-sidebar__user-info">'
+ ' <div class="pos-sidebar__user-name">' + name + '</div>'
+ ' <div class="pos-sidebar__user-role">' + roleLabel + '</div>'
+ ' </div>'
+ ' </div>'
+ ' <button class="pos-sidebar__logout" onclick="posLogout()" title="Cerrar sesión">⏻</button>'
+ '</div>';
// Inject CSS
var css = document.createElement('style');
css.textContent = ''
+ '.pos-sidebar {'
+ ' position: fixed; top: 0; left: 0; bottom: 0; width: 220px;'
+ ' background: var(--color-bg-elevated, #161B22);'
+ ' border-right: 1px solid var(--color-border, #30363D);'
+ ' display: flex; flex-direction: column;'
+ ' z-index: 100; overflow-y: auto;'
+ ' font-family: var(--font-body, sans-serif);'
+ ' transition: background-color 0.3s;'
+ '}'
+ '.pos-sidebar__brand {'
+ ' display: flex; align-items: center; gap: 10px;'
+ ' padding: 16px 16px 12px; border-bottom: 1px solid var(--color-border, #30363D);'
+ '}'
+ '.pos-sidebar__logo {'
+ ' width: 36px; height: 36px; border-radius: 8px;'
+ ' background: var(--color-primary, #F5A623); color: #fff;'
+ ' display: flex; align-items: center; justify-content: center;'
+ ' font-weight: 800; font-size: 1.1rem;'
+ '}'
+ '.pos-sidebar__title {'
+ ' font-weight: 700; font-size: 1rem;'
+ ' color: var(--color-text-primary, #E6EDF3);'
+ '}'
+ '.pos-sidebar__nav {'
+ ' flex: 1; padding: 8px 8px; display: flex; flex-direction: column; gap: 2px;'
+ '}'
+ '.pos-nav__item {'
+ ' display: flex; align-items: center; gap: 10px;'
+ ' padding: 9px 12px; border-radius: 6px;'
+ ' color: var(--color-text-secondary, #8B949E);'
+ ' text-decoration: none; font-size: 0.88rem; font-weight: 500;'
+ ' transition: all 0.15s;'
+ '}'
+ '.pos-nav__item:hover {'
+ ' background: var(--color-bg-base, rgba(255,255,255,0.05));'
+ ' color: var(--color-text-primary, #E6EDF3);'
+ '}'
+ '.pos-nav__item.is-active {'
+ ' background: var(--color-primary, #F5A623);'
+ ' color: #fff; font-weight: 600;'
+ '}'
+ '.pos-nav__icon { font-size: 1rem; width: 20px; text-align: center; }'
+ '.pos-nav__label { white-space: nowrap; }'
+ '.pos-sidebar__footer {'
+ ' padding: 12px; border-top: 1px solid var(--color-border, #30363D);'
+ ' display: flex; align-items: center; justify-content: space-between;'
+ '}'
+ '.pos-sidebar__user {'
+ ' display: flex; align-items: center; gap: 8px;'
+ '}'
+ '.pos-sidebar__avatar {'
+ ' width: 32px; height: 32px; border-radius: 50%;'
+ ' background: var(--color-primary, #F5A623); color: #fff;'
+ ' display: flex; align-items: center; justify-content: center;'
+ ' font-size: 0.75rem; font-weight: 700;'
+ '}'
+ '.pos-sidebar__user-name {'
+ ' font-size: 0.82rem; font-weight: 600;'
+ ' color: var(--color-text-primary, #E6EDF3);'
+ '}'
+ '.pos-sidebar__user-role {'
+ ' font-size: 0.7rem; color: var(--color-text-muted, #6a6a7a);'
+ '}'
+ '.pos-sidebar__logout {'
+ ' background: none; border: 1px solid var(--color-border, #30363D);'
+ ' color: var(--color-text-muted, #6a6a7a); border-radius: 6px;'
+ ' padding: 6px 8px; cursor: pointer; font-size: 1rem;'
+ ' transition: all 0.15s;'
+ '}'
+ '.pos-sidebar__logout:hover {'
+ ' color: var(--color-error, #F85149);'
+ ' border-color: var(--color-error, #F85149);'
+ '}'
+ '/* Push main content right */'
+ '.pos-main-offset { margin-left: 220px; }'
+ '@media (max-width: 768px) {'
+ ' .pos-sidebar { width: 60px; }'
+ ' .pos-nav__label, .pos-sidebar__title,'
+ ' .pos-sidebar__user-info { display: none; }'
+ ' .pos-sidebar__footer { justify-content: center; }'
+ ' .pos-sidebar__user { justify-content: center; }'
+ ' .pos-main-offset { margin-left: 60px; }'
+ '}';
document.head.appendChild(css);
// Find or create sidebar container
// Strategy: replace existing sidebar content or create new one
var existingSidebar = document.querySelector('aside.sidebar, .sidebar, #sidebar');
if (existingSidebar) {
// Replace existing sidebar content
existingSidebar.className = 'pos-sidebar';
existingSidebar.innerHTML = sidebarHtml;
existingSidebar.removeAttribute('style');
} else {
// Create new sidebar
var sidebar = document.createElement('aside');
sidebar.className = 'pos-sidebar';
sidebar.innerHTML = sidebarHtml;
document.body.insertBefore(sidebar, document.body.firstChild);
}
// Add offset class to main content
var mainContent = document.querySelector('main, .main-content, #mainContent, .page-content');
if (mainContent) {
mainContent.classList.add('pos-main-offset');
}
})();