feat(design): add 16 new components + update 5 pages (ronda 2)

Bloqueantes POS: modal-pago, ticket-termico, banner-cliente, fkeys-footer, columnas-costo-margen
Componentes nuevos: calculadora-cambio, panel-deslizante, badge-cfdi, arbol-colapsable, tarjeta-metrica, grafica-barras, selector-periodo, etiqueta-codigo-barras
Estados: estado-vacio, banner-offline, modal-confirmacion
Páginas actualizadas: facturación, contabilidad, dashboard, configuración, reportes
This commit is contained in:
Lucy
2026-04-01 07:06:34 +00:00
parent 380698258a
commit ccd3962458
25 changed files with 7153 additions and 129 deletions

View File

@@ -890,92 +890,8 @@
<!-- USER SELECTION -->
<section class="users-section" aria-labelledby="users-label">
<div class="section-label" id="users-label">Seleccionar usuario</div>
<div class="users-grid" role="radiogroup" aria-label="Usuarios disponibles">
<button
class="user-avatar-btn"
data-user="JR"
data-name="J. Ramírez"
data-role="Vendedor"
role="radio"
aria-checked="false"
aria-label="Jorge Ramírez, Vendedor"
>
<div class="user-initials" aria-hidden="true">JR</div>
<div class="user-name">J. Ramírez</div>
<div class="user-role">Vendedor</div>
</button>
<button
class="user-avatar-btn"
data-user="ML"
data-name="M. López"
data-role="Cajero"
role="radio"
aria-checked="false"
aria-label="María López, Cajero"
>
<div class="user-initials" aria-hidden="true">ML</div>
<div class="user-name">M. López</div>
<div class="user-role">Cajero</div>
</button>
<button
class="user-avatar-btn"
data-user="AP"
data-name="A. Peña"
data-role="Almacén"
role="radio"
aria-checked="false"
aria-label="Alejandro Peña, Almacén"
>
<div class="user-initials" aria-hidden="true">AP</div>
<div class="user-name">A. Peña</div>
<div class="user-role">Almacén</div>
</button>
<button
class="user-avatar-btn"
data-user="SC"
data-name="S. Cruz"
data-role="Supervisor"
role="radio"
aria-checked="false"
aria-label="Sara Cruz, Supervisor"
>
<div class="user-initials" aria-hidden="true">SC</div>
<div class="user-name">S. Cruz</div>
<div class="user-role">Supervisor</div>
</button>
<button
class="user-avatar-btn"
data-user="HG"
data-name="H. García"
data-role="Gerente"
role="radio"
aria-checked="false"
aria-label="Hugo García, Gerente"
>
<div class="user-initials" aria-hidden="true">HG</div>
<div class="user-name">H. García</div>
<div class="user-role">Gerente</div>
</button>
<button
class="user-avatar-btn"
data-user="RT"
data-name="R. Torres"
data-role="Vendedor"
role="radio"
aria-checked="false"
aria-label="Roberto Torres, Vendedor"
>
<div class="user-initials" aria-hidden="true">RT</div>
<div class="user-name">R. Torres</div>
<div class="user-role">Vendedor</div>
</button>
<div class="users-grid" id="users-grid" role="radiogroup" aria-label="Usuarios disponibles">
<!-- Empleados cargados dinámicamente desde el tenant -->
</div>
</section>
@@ -1071,12 +987,61 @@
maxPinLength: 6,
};
/* ------------------------------------------------------------------
EMPLEADOS DEL TENANT (dinámico — en producción viene de la API)
------------------------------------------------------------------ */
const tenantEmployees = [
{ id: 'JR', initials: 'JR', name: 'J. Ramírez', fullName: 'Jorge Ramírez', role: 'Vendedor' },
{ id: 'ML', initials: 'ML', name: 'M. López', fullName: 'María López', role: 'Cajero' },
{ id: 'AP', initials: 'AP', name: 'A. Peña', fullName: 'Alejandro Peña', role: 'Almacén' },
{ id: 'SC', initials: 'SC', name: 'S. Cruz', fullName: 'Sara Cruz', role: 'Supervisor' },
{ id: 'HG', initials: 'HG', name: 'H. García', fullName: 'Hugo García', role: 'Gerente' },
{ id: 'RT', initials: 'RT', name: 'R. Torres', fullName: 'Roberto Torres', role: 'Vendedor' },
];
function renderEmployees(employees) {
const grid = document.getElementById('users-grid');
grid.innerHTML = employees.map(emp => `
<button
class="user-avatar-btn"
data-user="${emp.id}"
data-name="${emp.name}"
data-role="${emp.role}"
role="radio"
aria-checked="false"
aria-label="${emp.fullName}, ${emp.role}"
>
<div class="user-initials" aria-hidden="true">${emp.initials}</div>
<div class="user-name">${emp.name}</div>
<div class="user-role">${emp.role}</div>
</button>
`).join('');
bindUserButtons();
}
function bindUserButtons() {
document.querySelectorAll('.user-avatar-btn').forEach(btn => {
btn.addEventListener('click', () => {
document.querySelectorAll('.user-avatar-btn').forEach(b => {
b.classList.remove('selected');
b.setAttribute('aria-checked', 'false');
});
btn.classList.add('selected');
btn.setAttribute('aria-checked', 'true');
state.selectedUser = btn.dataset.user;
state.pin = [];
enablePinPad();
updatePinDisplay();
updateLoginButton();
});
});
}
/* ------------------------------------------------------------------
DOM REFS
------------------------------------------------------------------ */
const html = document.documentElement;
const themeBtns = document.querySelectorAll('.theme-btn');
const userBtns = document.querySelectorAll('.user-avatar-btn');
const pinDisplay = document.getElementById('pin-display');
const pinPlaceholder = document.getElementById('pin-placeholder');
const pinDots = document.querySelectorAll('.pin-dot');
@@ -1105,28 +1070,9 @@
});
/* ------------------------------------------------------------------
USER SELECTION
RENDER EMPLOYEES ON INIT
------------------------------------------------------------------ */
userBtns.forEach(btn => {
btn.addEventListener('click', () => {
// Deselect all
userBtns.forEach(b => {
b.classList.remove('selected');
b.setAttribute('aria-checked', 'false');
});
// Select clicked
btn.classList.add('selected');
btn.setAttribute('aria-checked', 'true');
state.selectedUser = btn.dataset.user;
state.pin = [];
// Enable PIN pad
enablePinPad();
updatePinDisplay();
updateLoginButton();
});
});
renderEmployees(tenantEmployees);
/* ------------------------------------------------------------------
PIN PAD LOGIC