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:
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user