Files
Autoparts-DB/docs/design/design-system/components/modal-pago.html
Lucy ccd3962458 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
2026-04-01 07:06:34 +00:00

556 lines
23 KiB
HTML

<!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 Pago</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);
}
.label-text {
font-size: var(--text-caption); color: var(--color-text-muted);
text-transform: uppercase; letter-spacing: var(--tracking-widest);
font-weight: var(--font-weight-semibold); margin-bottom: var(--space-3);
}
/* ====== Overlay ====== */
.modal-overlay {
position: fixed; inset: 0;
background: var(--overlay-backdrop);
display: flex; align-items: center; justify-content: center;
z-index: var(--z-modal);
opacity: 0; pointer-events: none;
transition: var(--transition-normal);
}
.modal-overlay.open { opacity: 1; pointer-events: auto; }
/* ====== Modal Container ====== */
.modal-pago {
background: var(--color-bg-elevated);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
box-shadow: var(--shadow-xl);
width: 560px; max-width: 95vw;
max-height: 90vh; overflow-y: auto;
transform: translateY(20px);
transition: var(--transition-normal);
}
.modal-overlay.open .modal-pago { transform: translateY(0); }
/* ====== Header ====== */
.modal-header {
display: flex; align-items: center; justify-content: space-between;
padding: var(--space-5) var(--space-6);
border-bottom: 1px solid var(--color-border);
}
.modal-header h3 {
font-family: var(--font-heading); font-size: var(--text-h4);
font-weight: var(--heading-weight-primary); color: var(--color-text-primary);
}
.modal-close {
width: 36px; height: 36px; display: flex; align-items: center; justify-content: center;
background: transparent; border: 1px solid var(--color-border);
border-radius: var(--radius-md); cursor: pointer;
color: var(--color-text-muted); font-size: 18px;
transition: var(--transition-fast);
}
.modal-close:hover { background: var(--color-surface-2); color: var(--color-text-primary); }
/* ====== Total Banner ====== */
.total-banner {
background: var(--color-primary-muted);
border-bottom: 2px solid var(--color-primary);
padding: var(--space-4) var(--space-6);
display: flex; align-items: center; justify-content: space-between;
}
.total-label {
font-size: var(--text-body); font-weight: var(--font-weight-semibold);
color: var(--color-text-secondary); text-transform: uppercase;
letter-spacing: var(--tracking-wide);
}
.total-amount {
font-family: var(--font-mono); font-size: var(--text-h3);
font-weight: var(--font-weight-bold); color: var(--color-text-accent);
}
/* ====== Items Summary ====== */
.items-summary {
padding: var(--space-3) var(--space-6);
border-bottom: 1px solid var(--color-border);
font-size: var(--text-body-sm); color: var(--color-text-muted);
display: flex; gap: var(--space-4);
}
/* ====== Tabs ====== */
.pago-tabs {
display: flex; border-bottom: 2px solid var(--color-border);
padding: 0 var(--space-6);
}
.pago-tab {
padding: var(--space-3) var(--space-5);
font-family: var(--font-body); font-size: var(--text-body-sm);
font-weight: var(--font-weight-semibold);
background: transparent; border: none;
color: var(--color-text-muted); cursor: pointer;
border-bottom: 2px solid transparent;
margin-bottom: -2px; transition: var(--transition-fast);
display: flex; align-items: center; gap: var(--space-2);
}
.pago-tab:hover { color: var(--color-text-primary); }
.pago-tab.active {
color: var(--color-text-accent);
border-bottom-color: var(--color-primary);
}
.pago-tab .tab-icon { font-size: 16px; }
/* ====== Tab Content ====== */
.tab-content { padding: var(--space-6); display: none; }
.tab-content.active { display: block; }
/* ====== Form Elements ====== */
.form-group { margin-bottom: var(--space-4); }
.form-label {
display: block; font-size: var(--text-caption); font-weight: var(--font-weight-semibold);
color: var(--color-text-secondary); margin-bottom: var(--space-2);
text-transform: uppercase; letter-spacing: var(--tracking-wide);
}
.form-input {
width: 100%; padding: var(--space-3) var(--space-4);
font-family: var(--font-body); font-size: var(--text-body);
background: var(--color-bg-base); color: var(--color-text-primary);
border: 1px solid var(--color-border); border-radius: var(--radius-md);
transition: var(--transition-fast);
}
.form-input:focus { outline: none; border-color: var(--color-border-focus); box-shadow: var(--shadow-focus); }
.form-input-lg {
font-family: var(--font-mono); font-size: var(--text-h3);
font-weight: var(--font-weight-bold); text-align: right;
padding: var(--space-4) var(--space-5);
}
.form-hint {
font-size: var(--text-caption); color: var(--color-text-muted); margin-top: var(--space-1);
}
/* ====== Calculadora de Cambio ====== */
.cambio-display {
background: var(--color-surface-2);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
padding: var(--space-4) var(--space-5);
display: flex; align-items: center; justify-content: space-between;
margin-top: var(--space-4);
}
.cambio-label {
font-size: var(--text-body-sm); color: var(--color-text-secondary);
font-weight: var(--font-weight-semibold);
}
.cambio-amount {
font-family: var(--font-mono); font-size: var(--text-h4);
font-weight: var(--font-weight-bold);
}
.cambio-amount.positive { color: var(--color-success); }
.cambio-amount.negative { color: var(--color-error); }
.quick-amounts {
display: grid; grid-template-columns: repeat(4, 1fr);
gap: var(--space-2); margin-top: var(--space-3);
}
.quick-btn {
padding: var(--space-2) var(--space-3);
font-family: var(--font-mono); font-size: var(--text-body-sm);
font-weight: var(--font-weight-semibold);
background: var(--color-surface-2); color: var(--color-text-primary);
border: 1px solid var(--color-border); border-radius: var(--radius-md);
cursor: pointer; transition: var(--transition-fast); text-align: center;
}
.quick-btn:hover { background: var(--color-primary-muted); border-color: var(--color-primary); }
/* ====== Mixto Split ====== */
.split-row {
display: grid; grid-template-columns: 1fr auto 1fr;
gap: var(--space-3); align-items: end; margin-bottom: var(--space-3);
}
.split-method {
padding: var(--space-2) var(--space-3);
font-size: var(--text-body-sm); font-weight: var(--font-weight-semibold);
background: var(--color-surface-2); color: var(--color-text-secondary);
border-radius: var(--radius-md); text-align: center;
border: 1px solid var(--color-border);
}
.split-remaining {
background: var(--color-surface-2); border-radius: var(--radius-md);
padding: var(--space-3) var(--space-4);
display: flex; align-items: center; justify-content: space-between;
font-size: var(--text-body-sm); margin-top: var(--space-3);
}
.split-remaining .amount {
font-family: var(--font-mono); font-weight: var(--font-weight-bold);
color: var(--color-text-accent);
}
/* ====== Checkbox CFDI ====== */
.cfdi-check {
display: flex; align-items: center; gap: var(--space-3);
padding: var(--space-4) var(--space-6);
border-top: 1px solid var(--color-border);
background: var(--color-surface-1);
}
.cfdi-check input[type="checkbox"] {
width: 20px; height: 20px; accent-color: var(--color-primary);
cursor: pointer;
}
.cfdi-check label {
font-size: var(--text-body-sm); font-weight: var(--font-weight-semibold);
color: var(--color-text-secondary); cursor: pointer;
}
.cfdi-check .cfdi-hint {
font-size: var(--text-caption); color: var(--color-text-muted);
margin-left: auto;
}
/* ====== Footer ====== */
.modal-footer {
padding: var(--space-4) var(--space-6);
border-top: 1px solid var(--color-border);
display: flex; gap: var(--space-3); justify-content: flex-end;
}
.btn {
display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2);
padding: var(--space-3) var(--space-6);
font-family: var(--font-body); font-size: var(--text-body);
font-weight: var(--font-weight-semibold); line-height: 1;
border: 2px solid transparent; border-radius: var(--radius-md);
cursor: pointer; transition: var(--transition-fast); white-space: nowrap;
}
.btn:focus-visible { outline: none; box-shadow: var(--shadow-focus); }
.btn:active { transform: scale(0.97); }
.btn-primary { background: var(--btn-primary-bg); color: var(--btn-primary-text); }
.btn-primary:hover { background: var(--btn-primary-bg-hover); }
.btn-secondary { background: var(--btn-secondary-bg); color: var(--btn-secondary-text); border-color: var(--btn-secondary-border); }
.btn-secondary:hover { background: var(--btn-secondary-bg-hover); }
.btn-ghost { background: var(--btn-ghost-bg); color: var(--btn-ghost-text); border-color: var(--btn-ghost-border); }
.btn-ghost:hover { background: var(--color-surface-2); }
.btn-lg { padding: var(--space-4) var(--space-8); font-size: var(--text-body-lg); }
/* ====== Inline Preview (for showcase) ====== */
.preview-container {
background: var(--color-bg-base);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
overflow: hidden;
margin-bottom: var(--space-8);
}
/* ====== Transfer / Card fields ====== */
.ref-field {
display: flex; gap: var(--space-3); align-items: end;
}
.ref-field .form-group { flex: 1; }
.status-indicator {
display: flex; align-items: center; gap: var(--space-2);
padding: var(--space-3) var(--space-4);
border-radius: var(--radius-md);
font-size: var(--text-body-sm); font-weight: var(--font-weight-semibold);
}
.status-indicator.pending {
background: var(--color-warning-light); color: var(--color-warning-dark);
}
.status-indicator.confirmed {
background: var(--color-success-light); color: var(--color-success-dark);
}
/* ====== Terminal Select ====== */
.terminal-cards {
display: grid; grid-template-columns: repeat(2, 1fr);
gap: var(--space-3);
}
.terminal-card {
padding: var(--space-4);
background: var(--color-surface-2);
border: 2px solid var(--color-border);
border-radius: var(--radius-md);
cursor: pointer; transition: var(--transition-fast);
text-align: center;
}
.terminal-card:hover { border-color: var(--color-primary); }
.terminal-card.selected {
border-color: var(--color-primary);
background: var(--color-primary-muted);
}
.terminal-card .terminal-name {
font-size: var(--text-body-sm); font-weight: var(--font-weight-semibold);
color: var(--color-text-primary);
}
.terminal-card .terminal-type {
font-size: var(--text-caption); color: var(--color-text-muted); margin-top: var(--space-1);
}
</style>
</head>
<body>
<!-- Theme Switcher -->
<div class="theme-switcher">
<button class="active" onclick="setTheme('industrial')">A — Industrial Robusto</button>
<button onclick="setTheme('modern')">B — Técnico Moderno</button>
</div>
<h1>Modal de Pago</h1>
<p class="subtitle">Componente de cobro con 4 métodos: Efectivo, Transferencia, Tarjeta, Mixto.</p>
<!-- ============================================== -->
<!-- SECTION: Full Modal Preview -->
<!-- ============================================== -->
<section>
<h2>Vista Completa del Modal</h2>
<p class="label-text">Preview interactivo — haz click en las tabs para cambiar de método</p>
<div class="preview-container">
<!-- Modal Header -->
<div class="modal-header">
<h3>Cobrar Venta</h3>
<button class="modal-close">&#x2715;</button>
</div>
<!-- Total Banner -->
<div class="total-banner">
<span class="total-label">Total a Cobrar</span>
<span class="total-amount">$4,827.50</span>
</div>
<!-- Items Summary -->
<div class="items-summary">
<span>3 productos</span>
<span>&#x2022;</span>
<span>Cliente: Taller Automotriz García</span>
<span>&#x2022;</span>
<span>Folio: V-2026-0847</span>
</div>
<!-- Payment Tabs -->
<div class="pago-tabs">
<button class="pago-tab active" onclick="switchPagoTab('efectivo')">
<span class="tab-icon">&#x1F4B5;</span> Efectivo
</button>
<button class="pago-tab" onclick="switchPagoTab('transferencia')">
<span class="tab-icon">&#x1F3E6;</span> Transferencia
</button>
<button class="pago-tab" onclick="switchPagoTab('tarjeta')">
<span class="tab-icon">&#x1F4B3;</span> Tarjeta
</button>
<button class="pago-tab" onclick="switchPagoTab('mixto')">
<span class="tab-icon">&#x1F504;</span> Mixto
</button>
</div>
<!-- TAB: Efectivo -->
<div class="tab-content active" id="tab-efectivo">
<div class="form-group">
<label class="form-label">Monto recibido</label>
<input type="text" class="form-input form-input-lg" value="$5,000.00" id="monto-recibido" oninput="calcCambio()">
</div>
<div class="quick-amounts">
<button class="quick-btn" onclick="setMonto(4828)">$4,828</button>
<button class="quick-btn" onclick="setMonto(4850)">$4,850</button>
<button class="quick-btn" onclick="setMonto(5000)">$5,000</button>
<button class="quick-btn" onclick="setMonto(5500)">$5,500</button>
</div>
<div class="cambio-display">
<span class="cambio-label">Cambio</span>
<span class="cambio-amount positive" id="cambio-valor">$172.50</span>
</div>
</div>
<!-- TAB: Transferencia -->
<div class="tab-content" id="tab-transferencia">
<div class="form-group">
<label class="form-label">Monto</label>
<input type="text" class="form-input form-input-lg" value="$4,827.50" readonly>
</div>
<div class="form-group">
<label class="form-label">Banco destino</label>
<select class="form-input">
<option>BBVA — **** 4521</option>
<option>Banorte — **** 7832</option>
</select>
</div>
<div class="ref-field">
<div class="form-group">
<label class="form-label">Referencia / No. operación</label>
<input type="text" class="form-input" placeholder="Ej: 2026040100847">
</div>
</div>
<div class="form-hint">Verificar que la transferencia se haya recibido antes de confirmar</div>
<div style="margin-top: var(--space-4);">
<div class="status-indicator pending">&#x23F3; Pendiente de confirmación</div>
</div>
</div>
<!-- TAB: Tarjeta -->
<div class="tab-content" id="tab-tarjeta">
<div class="form-group">
<label class="form-label">Monto</label>
<input type="text" class="form-input form-input-lg" value="$4,827.50" readonly>
</div>
<div class="form-group">
<label class="form-label">Terminal</label>
<div class="terminal-cards">
<div class="terminal-card selected">
<div class="terminal-name">Terminal 1</div>
<div class="terminal-type">Clip Pro — Visa/MC</div>
</div>
<div class="terminal-card">
<div class="terminal-name">Terminal 2</div>
<div class="terminal-type">Banorte TPV — Todas</div>
</div>
</div>
</div>
<div class="form-group">
<label class="form-label">No. de autorización</label>
<input type="text" class="form-input" placeholder="6 dígitos de autorización">
</div>
<div class="form-group">
<label class="form-label">Últimos 4 dígitos de tarjeta</label>
<input type="text" class="form-input" placeholder="Ej: 4521" maxlength="4" style="width: 120px;">
</div>
</div>
<!-- TAB: Mixto -->
<div class="tab-content" id="tab-mixto">
<div class="form-group">
<label class="form-label">Método 1 — Efectivo</label>
<div class="split-row">
<input type="text" class="form-input" value="$3,000.00" style="font-family: var(--font-mono); text-align: right;">
<div class="split-method">+</div>
<div>
<label class="form-label">Método 2 — Tarjeta</label>
<input type="text" class="form-input" value="$1,827.50" style="font-family: var(--font-mono); text-align: right;">
</div>
</div>
</div>
<div class="split-remaining">
<span>Restante por asignar:</span>
<span class="amount">$0.00</span>
</div>
<div class="form-group" style="margin-top: var(--space-4);">
<label class="form-label">No. de autorización (tarjeta)</label>
<input type="text" class="form-input" placeholder="6 dígitos">
</div>
</div>
<!-- CFDI Checkbox -->
<div class="cfdi-check">
<input type="checkbox" id="cfdi-check" checked>
<label for="cfdi-check">Facturar CFDI</label>
<span class="cfdi-hint">RFC: TAU150301XX1 — Régimen: 601</span>
</div>
<!-- Footer -->
<div class="modal-footer">
<button class="btn btn-ghost">Cancelar</button>
<button class="btn btn-primary btn-lg">&#x2714; Confirmar Pago — $4,827.50</button>
</div>
</div>
</section>
<!-- ============================================== -->
<!-- SECTION: States -->
<!-- ============================================== -->
<section>
<h2>Estados del Cambio</h2>
<p class="label-text">Indicador visual de cambio positivo, exacto y faltante</p>
<div style="display: flex; gap: var(--space-4); flex-wrap: wrap;">
<div class="cambio-display" style="flex: 1; min-width: 200px;">
<span class="cambio-label">Cambio</span>
<span class="cambio-amount positive">$172.50</span>
</div>
<div class="cambio-display" style="flex: 1; min-width: 200px;">
<span class="cambio-label">Cambio</span>
<span class="cambio-amount" style="color: var(--color-text-accent);">$0.00</span>
</div>
<div class="cambio-display" style="flex: 1; min-width: 200px;">
<span class="cambio-label">Faltante</span>
<span class="cambio-amount negative">-$327.50</span>
</div>
</div>
</section>
<!-- ============================================== -->
<!-- SECTION: Transfer Status -->
<!-- ============================================== -->
<section>
<h2>Estados de Transferencia</h2>
<div style="display: flex; gap: var(--space-4); flex-wrap: wrap;">
<div class="status-indicator pending">&#x23F3; Pendiente de confirmación</div>
<div class="status-indicator confirmed">&#x2705; Transferencia confirmada</div>
</div>
</section>
<script>
function setTheme(theme) {
document.documentElement.setAttribute('data-theme', theme);
document.querySelectorAll('.theme-switcher button').forEach(b => b.classList.remove('active'));
event.target.classList.add('active');
}
function switchPagoTab(tabId) {
document.querySelectorAll('.pago-tab').forEach(t => t.classList.remove('active'));
document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
event.target.closest('.pago-tab').classList.add('active');
document.getElementById('tab-' + tabId).classList.add('active');
}
function setMonto(val) {
document.getElementById('monto-recibido').value = '$' + val.toLocaleString('en-US', {minimumFractionDigits: 2});
const cambio = val - 4827.50;
const el = document.getElementById('cambio-valor');
el.textContent = (cambio >= 0 ? '$' : '-$') + Math.abs(cambio).toLocaleString('en-US', {minimumFractionDigits: 2});
el.className = 'cambio-amount ' + (cambio > 0 ? 'positive' : cambio < 0 ? 'negative' : '');
}
function calcCambio() {
const raw = document.getElementById('monto-recibido').value.replace(/[^0-9.]/g, '');
const val = parseFloat(raw) || 0;
const cambio = val - 4827.50;
const el = document.getElementById('cambio-valor');
el.textContent = (cambio >= 0 ? '$' : '-$') + Math.abs(cambio).toLocaleString('en-US', {minimumFractionDigits: 2});
el.className = 'cambio-amount ' + (cambio > 0 ? 'positive' : cambio < 0 ? 'negative' : '');
}
</script>
</body>
</html>