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

@@ -0,0 +1,477 @@
<!DOCTYPE html>
<html lang="es" data-theme="industrial">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Nexus Autoparts — Modal de Confirmación</title>
<link rel="stylesheet" href="../tokens/tokens.css">
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: var(--font-body); background: var(--color-bg-base);
color: var(--color-text-primary); padding: var(--space-8);
transition: var(--transition-normal);
}
.theme-switcher {
position: sticky; top: 0; z-index: calc(var(--z-modal) + 10);
display: flex; gap: var(--space-2);
padding: var(--space-3) var(--space-4);
background: var(--color-bg-elevated);
border-bottom: 1px solid var(--color-border);
margin: calc(-1 * var(--space-8)); margin-bottom: var(--space-8);
}
.theme-switcher button {
padding: var(--space-2) var(--space-4);
border: 1px solid var(--color-border);
background: var(--color-bg-base); color: var(--color-text-primary);
border-radius: var(--radius-md); cursor: pointer;
font-family: var(--font-body); font-size: var(--text-body-sm);
transition: var(--transition-fast);
}
.theme-switcher button.active {
background: var(--color-primary); color: var(--color-text-inverse);
border-color: var(--color-primary);
}
h1 { font-family: var(--font-heading); font-size: var(--text-h2);
font-weight: var(--heading-weight-primary); margin-bottom: var(--space-2);
color: var(--color-text-accent); }
.subtitle { color: var(--color-text-muted); font-size: var(--text-body); margin-bottom: var(--space-8); }
section { margin-bottom: var(--space-10); }
section > h2 {
font-family: var(--font-heading); font-size: var(--text-h4);
font-weight: var(--heading-weight-secondary); color: var(--color-text-secondary);
margin-bottom: var(--space-4); padding-bottom: var(--space-2);
border-bottom: 1px solid var(--color-border);
}
/* === Modal Component === */
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes scaleIn {
from { opacity: 0; transform: translate(-50%, -50%) scale(0.92); }
to { opacity: 1; transform: translate(-50%, -50%) scale(1); }
}
@keyframes fadeOut {
from { opacity: 1; }
to { opacity: 0; }
}
@keyframes scaleOut {
from { opacity: 1; transform: translate(-50%, -50%) scale(1); }
to { opacity: 0; transform: translate(-50%, -50%) scale(0.92); }
}
.modal-overlay {
position: fixed;
inset: 0;
background: var(--overlay-backdrop, rgba(0, 0, 0, 0.6));
z-index: var(--z-modal, 1000);
display: none;
animation: fadeIn 0.2s ease-out forwards;
}
.modal-overlay.active {
display: block;
}
.modal-overlay.closing {
animation: fadeOut 0.2s ease-in forwards;
}
.modal-dialog {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: calc(var(--z-modal, 1000) + 1);
background: var(--color-bg-elevated);
border: 1px solid var(--color-border);
border-radius: var(--radius-xl);
box-shadow: var(--shadow-xl);
width: 90%;
max-width: 420px;
padding: var(--space-6);
text-align: center;
display: none;
animation: scaleIn 0.25s ease-out forwards;
}
.modal-dialog.active {
display: block;
}
.modal-dialog.closing {
animation: scaleOut 0.2s ease-in forwards;
}
.modal__icon {
width: 56px;
height: 56px;
border-radius: var(--radius-full);
display: flex;
align-items: center;
justify-content: center;
font-size: 26px;
margin: 0 auto var(--space-4);
}
.modal__icon--warning {
background: var(--color-warning-light);
}
.modal__icon--info {
background: var(--color-primary-muted);
}
.modal__icon--danger {
background: var(--color-error-light);
}
.modal__title {
font-family: var(--font-heading);
font-size: var(--text-h4);
font-weight: var(--heading-weight-primary);
color: var(--color-text-primary);
margin-bottom: var(--space-2);
}
.modal__desc {
font-size: var(--text-body-sm);
color: var(--color-text-muted);
line-height: 1.6;
margin-bottom: var(--space-6);
padding: 0 var(--space-2);
}
.modal__actions {
display: flex;
gap: var(--space-3);
justify-content: center;
}
.modal__btn {
flex: 1;
max-width: 180px;
padding: var(--space-3) var(--space-4);
font-family: var(--font-body);
font-size: var(--text-body-sm);
font-weight: 600;
border-radius: var(--radius-md);
cursor: pointer;
transition: var(--transition-fast);
border: 1px solid transparent;
}
.modal__btn--secondary {
background: var(--btn-secondary-bg);
color: var(--btn-secondary-text);
border-color: var(--btn-secondary-border);
}
.modal__btn--secondary:hover {
background: var(--btn-secondary-bg-hover);
}
.modal__btn--primary {
background: var(--btn-primary-bg);
color: var(--btn-primary-text);
border-color: var(--btn-primary-border);
}
.modal__btn--primary:hover {
background: var(--btn-primary-bg-hover);
box-shadow: var(--shadow-sm);
}
.modal__btn--danger {
background: var(--btn-danger-bg);
color: var(--btn-danger-text);
border-color: var(--btn-danger-bg);
}
.modal__btn--danger:hover {
opacity: 0.9;
box-shadow: var(--shadow-sm);
}
/* === Demo trigger buttons === */
.trigger-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: var(--space-4);
}
.trigger-card {
background: var(--color-surface-1);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
padding: var(--space-5);
display: flex;
flex-direction: column;
align-items: center;
gap: var(--space-3);
}
.trigger-card__label {
font-family: var(--font-mono);
font-size: var(--text-body-sm);
color: var(--color-text-muted);
background: var(--color-bg-overlay);
padding: var(--space-1) var(--space-3);
border-radius: var(--radius-full);
}
.trigger-card__desc {
font-size: var(--text-body-sm);
color: var(--color-text-muted);
text-align: center;
line-height: 1.5;
}
.trigger-btn {
padding: var(--space-3) var(--space-5);
font-family: var(--font-body);
font-size: var(--text-body-sm);
font-weight: 600;
border-radius: var(--radius-md);
cursor: pointer;
transition: var(--transition-fast);
border: 1px solid var(--color-border);
background: var(--color-bg-elevated);
color: var(--color-text-primary);
}
.trigger-btn:hover {
background: var(--color-surface-2);
box-shadow: var(--shadow-sm);
}
.trigger-btn--warning { border-color: var(--color-warning); }
.trigger-btn--primary { border-color: var(--color-primary); }
.trigger-btn--danger { border-color: var(--color-error); }
/* === Static previews === */
.static-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
gap: var(--space-6);
}
.static-preview {
background: var(--color-surface-1);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
padding: var(--space-5);
}
.static-preview .variant-label {
font-family: var(--font-mono);
font-size: var(--text-body-sm);
color: var(--color-text-muted);
background: var(--color-bg-overlay);
padding: var(--space-1) var(--space-3);
border-radius: var(--radius-full);
display: inline-block;
margin-bottom: var(--space-4);
}
.static-modal {
background: var(--color-bg-elevated);
border: 1px solid var(--color-border);
border-radius: var(--radius-xl);
box-shadow: var(--shadow-lg);
padding: var(--space-6);
text-align: center;
}
</style>
</head>
<body>
<div class="theme-switcher">
<button class="active" onclick="document.documentElement.dataset.theme='industrial'; this.classList.add('active'); this.nextElementSibling.classList.remove('active')">🔧 Industrial</button>
<button onclick="document.documentElement.dataset.theme='modern'; this.classList.add('active'); this.previousElementSibling.classList.remove('active')">⚡ Moderno</button>
</div>
<h1>Modal de Confirmación</h1>
<p class="subtitle">Diálogos modales para confirmar acciones destructivas o importantes del sistema POS.</p>
<!-- ========== Interactive triggers ========== -->
<section>
<h2>Demo interactivo — Abrir modales</h2>
<div class="trigger-grid">
<div class="trigger-card">
<span class="trigger-card__label">a) Cancelar venta</span>
<p class="trigger-card__desc">Confirma antes de descartar una venta en curso</p>
<button class="trigger-btn trigger-btn--warning" onclick="openModal('cancelar-venta')">Abrir modal</button>
</div>
<div class="trigger-card">
<span class="trigger-card__label">b) Cerrar caja</span>
<p class="trigger-card__desc">Genera corte de caja al final del turno</p>
<button class="trigger-btn trigger-btn--primary" onclick="openModal('cerrar-caja')">Abrir modal</button>
</div>
<div class="trigger-card">
<span class="trigger-card__label">c) Ajustar stock</span>
<p class="trigger-card__desc">Ajusta inventario de múltiples artículos</p>
<button class="trigger-btn trigger-btn--warning" onclick="openModal('ajustar-stock')">Abrir modal</button>
</div>
<div class="trigger-card">
<span class="trigger-card__label">d) Cerrar periodo</span>
<p class="trigger-card__desc">Cierra periodo fiscal contable</p>
<button class="trigger-btn trigger-btn--danger" onclick="openModal('cerrar-periodo')">Abrir modal</button>
</div>
</div>
</section>
<!-- ========== Static previews ========== -->
<section>
<h2>Variantes estáticas</h2>
<div class="static-grid">
<div class="static-preview">
<span class="variant-label">a) Cancelar venta</span>
<div class="static-modal">
<div class="modal__icon modal__icon--warning">⚠️</div>
<div class="modal__title">¿Cancelar venta?</div>
<div class="modal__desc">Se perderán los artículos agregados al ticket actual.</div>
<div class="modal__actions">
<button class="modal__btn modal__btn--secondary">No, continuar</button>
<button class="modal__btn modal__btn--danger">Sí, cancelar</button>
</div>
</div>
</div>
<div class="static-preview">
<span class="variant-label">b) Cerrar caja</span>
<div class="static-modal">
<div class="modal__icon modal__icon--info"></div>
<div class="modal__title">¿Cerrar caja?</div>
<div class="modal__desc">Se generará el corte de caja con total <strong style="color: var(--color-text-primary);">$15,800.00</strong></div>
<div class="modal__actions">
<button class="modal__btn modal__btn--secondary">Cancelar</button>
<button class="modal__btn modal__btn--primary">Cerrar caja</button>
</div>
</div>
</div>
<div class="static-preview">
<span class="variant-label">c) Ajustar stock</span>
<div class="static-modal">
<div class="modal__icon modal__icon--warning">⚠️</div>
<div class="modal__title">¿Ajustar stock?</div>
<div class="modal__desc">Se ajustarán <strong style="color: var(--color-text-primary);">5 artículos</strong> del inventario. Esta acción no se puede deshacer.</div>
<div class="modal__actions">
<button class="modal__btn modal__btn--secondary">Cancelar</button>
<button class="modal__btn modal__btn--primary">Ajustar</button>
</div>
</div>
</div>
<div class="static-preview">
<span class="variant-label">d) Cerrar periodo fiscal</span>
<div class="static-modal">
<div class="modal__icon modal__icon--danger">🛑</div>
<div class="modal__title">¿Cerrar periodo fiscal?</div>
<div class="modal__desc">Se cerrará el periodo <strong style="color: var(--color-text-primary);">Marzo 2026</strong>. No podrás registrar pólizas en este periodo.</div>
<div class="modal__actions">
<button class="modal__btn modal__btn--secondary">Cancelar</button>
<button class="modal__btn modal__btn--danger">Cerrar periodo</button>
</div>
</div>
</div>
</div>
</section>
<!-- ========== Actual Modal Overlays (hidden by default) ========== -->
<!-- a) Cancelar venta -->
<div class="modal-overlay" id="overlay-cancelar-venta" onclick="if(event.target===this) closeModal('cancelar-venta')"></div>
<div class="modal-dialog" id="modal-cancelar-venta">
<div class="modal__icon modal__icon--warning">⚠️</div>
<div class="modal__title">¿Cancelar venta?</div>
<div class="modal__desc">Se perderán los artículos agregados al ticket actual.</div>
<div class="modal__actions">
<button class="modal__btn modal__btn--secondary" onclick="closeModal('cancelar-venta')">No, continuar</button>
<button class="modal__btn modal__btn--danger" onclick="closeModal('cancelar-venta')">Sí, cancelar</button>
</div>
</div>
<!-- b) Cerrar caja -->
<div class="modal-overlay" id="overlay-cerrar-caja" onclick="if(event.target===this) closeModal('cerrar-caja')"></div>
<div class="modal-dialog" id="modal-cerrar-caja">
<div class="modal__icon modal__icon--info"></div>
<div class="modal__title">¿Cerrar caja?</div>
<div class="modal__desc">Se generará el corte de caja con total <strong style="color: var(--color-text-primary);">$15,800.00</strong></div>
<div class="modal__actions">
<button class="modal__btn modal__btn--secondary" onclick="closeModal('cerrar-caja')">Cancelar</button>
<button class="modal__btn modal__btn--primary" onclick="closeModal('cerrar-caja')">Cerrar caja</button>
</div>
</div>
<!-- c) Ajustar stock -->
<div class="modal-overlay" id="overlay-ajustar-stock" onclick="if(event.target===this) closeModal('ajustar-stock')"></div>
<div class="modal-dialog" id="modal-ajustar-stock">
<div class="modal__icon modal__icon--warning">⚠️</div>
<div class="modal__title">¿Ajustar stock?</div>
<div class="modal__desc">Se ajustarán <strong style="color: var(--color-text-primary);">5 artículos</strong> del inventario. Esta acción no se puede deshacer.</div>
<div class="modal__actions">
<button class="modal__btn modal__btn--secondary" onclick="closeModal('ajustar-stock')">Cancelar</button>
<button class="modal__btn modal__btn--primary" onclick="closeModal('ajustar-stock')">Ajustar</button>
</div>
</div>
<!-- d) Cerrar periodo fiscal -->
<div class="modal-overlay" id="overlay-cerrar-periodo" onclick="if(event.target===this) closeModal('cerrar-periodo')"></div>
<div class="modal-dialog" id="modal-cerrar-periodo">
<div class="modal__icon modal__icon--danger">🛑</div>
<div class="modal__title">¿Cerrar periodo fiscal?</div>
<div class="modal__desc">Se cerrará el periodo <strong style="color: var(--color-text-primary);">Marzo 2026</strong>. No podrás registrar pólizas en este periodo.</div>
<div class="modal__actions">
<button class="modal__btn modal__btn--secondary" onclick="closeModal('cerrar-periodo')">Cancelar</button>
<button class="modal__btn modal__btn--danger" onclick="closeModal('cerrar-periodo')">Cerrar periodo</button>
</div>
</div>
<script>
function openModal(id) {
const overlay = document.getElementById('overlay-' + id);
const dialog = document.getElementById('modal-' + id);
overlay.classList.remove('closing');
dialog.classList.remove('closing');
overlay.classList.add('active');
dialog.classList.add('active');
document.body.style.overflow = 'hidden';
}
function closeModal(id) {
const overlay = document.getElementById('overlay-' + id);
const dialog = document.getElementById('modal-' + id);
overlay.classList.add('closing');
dialog.classList.add('closing');
setTimeout(() => {
overlay.classList.remove('active', 'closing');
dialog.classList.remove('active', 'closing');
document.body.style.overflow = '';
}, 200);
}
// Close on Escape key
document.addEventListener('keydown', (e) => {
if (e.key === 'Escape') {
document.querySelectorAll('.modal-overlay.active').forEach(overlay => {
const id = overlay.id.replace('overlay-', '');
closeModal(id);
});
}
});
</script>
</body>
</html>