Files
Autoparts-DB/pos/templates/customers.html
consultoria-as 32581739ad feat(pos): PWA con Service Worker + sync offline
- Add manifest.json, sw.js with cache-first/network-first strategies
- Add sync-engine.js with IndexedDB queue for offline operations
- Add /pos/api/sync/inventory endpoint for offline inventory cache
- Add /pos/sw.js route for proper SW scope registration
- Generate PWA icons (192x192, 512x512)
- Update all 10 HTML templates with manifest link, theme-color meta,
  SW registration, and sync-engine script

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 07:18:01 +00:00

2158 lines
69 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="es" data-theme="industrial">
<head>
<script>/*pos_theme_early*/(function(){var t=localStorage.getItem("pos_theme")||"industrial";document.documentElement.setAttribute("data-theme",t);})()</script>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Nexus Autoparts — Clientes</title>
<link rel="stylesheet" href="/pos/static/css/tokens.css" />
<link rel="manifest" href="/pos/static/pwa/manifest.json" />
<meta name="theme-color" content="#F5A623" />
<style>
/* =====================================================================
BASE RESET & BODY
===================================================================== */
*, *::before, *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
}
html, body {
height: 100%;
}
body {
font-family: var(--font-body);
font-size: var(--text-body-sm);
font-weight: var(--font-weight-regular);
background-color: var(--color-bg-base);
color: var(--color-text-primary);
transition: background-color var(--duration-normal) var(--ease-in-out),
color var(--duration-normal) var(--ease-in-out);
display: flex;
flex-direction: column;
overflow: hidden;
}
/* Dot-grid on body for modern theme */
[data-theme="modern"] body {
background-color: var(--color-bg-base);
background-image: radial-gradient(
circle,
var(--dot-grid-color) 1px,
transparent 1px
);
background-size: var(--dot-grid-size) var(--dot-grid-size);
}
/* =====================================================================
THEME SWITCHER BAR (fixed, 36px)
===================================================================== */
.theme-bar {
display: flex;
align-items: center;
justify-content: space-between;
height: 36px;
padding: 0 var(--space-5);
background-color: var(--color-surface-3);
border-bottom: 1px solid var(--color-border);
font-size: var(--text-caption);
font-weight: var(--font-weight-semibold);
letter-spacing: var(--tracking-wider);
text-transform: uppercase;
color: var(--color-text-muted);
flex-shrink: 0;
z-index: var(--z-sticky);
}
[data-theme="industrial"] .theme-bar {
background-color: #111111;
border-bottom-color: var(--color-primary-muted);
}
.theme-bar__brand {
display: flex;
align-items: center;
gap: var(--space-3);
color: var(--color-text-accent);
font-family: var(--font-heading);
font-weight: var(--heading-weight-primary);
font-size: 0.8rem;
letter-spacing: var(--tracking-widest);
}
.theme-bar__brand-dot {
width: 7px;
height: 7px;
background-color: var(--color-success);
border-radius: var(--radius-full);
box-shadow: 0 0 6px var(--color-success);
animation: pulse-dot 2.5s ease-in-out infinite;
}
@keyframes pulse-dot {
0%, 100% { opacity: 1; }
50% { opacity: 0.4; }
}
.theme-bar__center {
display: flex;
align-items: center;
gap: var(--space-6);
color: var(--color-text-muted);
font-size: var(--text-caption);
}
.theme-bar__right {
display: flex;
align-items: center;
gap: var(--space-4);
}
.theme-bar__user {
display: flex;
align-items: center;
gap: var(--space-2);
color: var(--color-text-secondary);
}
.theme-bar__user-avatar {
width: 20px;
height: 20px;
border-radius: var(--radius-full);
background-color: var(--color-primary);
color: var(--color-text-inverse);
display: flex;
align-items: center;
justify-content: center;
font-size: 0.6rem;
font-weight: var(--font-weight-bold);
}
[data-theme="industrial"] .theme-bar__user-avatar {
color: #000;
}
/* Theme switcher pill */
.theme-switcher {
display: flex;
align-items: center;
background-color: var(--color-surface-1);
border: 1px solid var(--color-border);
border-radius: var(--radius-full);
padding: 2px;
gap: 2px;
}
.theme-btn {
display: flex;
align-items: center;
gap: var(--space-1);
padding: 3px var(--space-3);
border: none;
border-radius: var(--radius-full);
font-family: var(--font-body);
font-size: 0.68rem;
font-weight: var(--font-weight-semibold);
letter-spacing: var(--tracking-wide);
text-transform: uppercase;
cursor: pointer;
transition: var(--transition-fast);
background: transparent;
color: var(--color-text-muted);
}
.theme-btn.active {
background-color: var(--color-primary);
color: var(--color-text-inverse);
box-shadow: var(--shadow-sm);
}
[data-theme="industrial"] .theme-btn.active {
color: #000;
}
/* =====================================================================
APP SHELL — below theme bar
===================================================================== */
.app-shell {
display: flex;
flex: 1;
overflow: hidden;
}
/* =====================================================================
SIDEBAR NAVIGATION (~220px)
===================================================================== */
.sidebar {
width: 260px;
flex-shrink: 0;
display: flex;
flex-direction: column;
background-color: var(--color-bg-elevated);
border-right: 1px solid var(--color-border);
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
}
[data-theme="industrial"] .sidebar {
background-color: #161616;
border-right-color: var(--color-border);
}
.sidebar__logo {
display: flex;
align-items: center;
gap: var(--space-3);
padding: var(--space-5) var(--space-5) var(--space-4);
border-bottom: 1px solid var(--color-border);
}
.sidebar__logo-icon {
width: 32px;
height: 32px;
background-color: var(--color-primary);
border-radius: var(--radius-md);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
[data-theme="industrial"] .sidebar__logo-icon {
border-radius: 0;
clip-path: polygon(0 0, calc(100% - 6px) 0, 100% 6px, 100% 100%, 0 100%);
}
.sidebar__logo-icon svg {
color: var(--color-text-inverse);
}
[data-theme="industrial"] .sidebar__logo-icon svg {
color: #000;
}
.sidebar__logo-text {
font-family: var(--font-heading);
font-weight: var(--heading-weight-primary);
font-size: 0.95rem;
letter-spacing: var(--tracking-widest);
text-transform: uppercase;
color: var(--color-text-primary);
line-height: 1.1;
}
.sidebar__logo-sub {
font-size: var(--text-caption);
color: var(--color-text-muted);
font-weight: var(--font-weight-regular);
letter-spacing: var(--tracking-wide);
}
.sidebar__section-label {
padding: var(--space-4) var(--space-5) var(--space-2);
font-size: 0.65rem;
font-weight: var(--font-weight-semibold);
letter-spacing: var(--tracking-widest);
text-transform: uppercase;
color: var(--color-text-disabled);
}
.sidebar__nav {
list-style: none;
padding: var(--space-2) 0;
flex: 1;
}
.nav-item {
display: block;
}
.nav-link {
display: flex;
align-items: center;
gap: var(--space-3);
padding: var(--space-2) var(--space-5);
height: 38px;
color: var(--color-text-secondary);
text-decoration: none;
font-size: var(--text-body-sm);
font-weight: var(--font-weight-regular);
border-left: 3px solid transparent;
transition: var(--transition-fast);
white-space: nowrap;
}
.nav-link:hover {
background-color: var(--color-primary-muted);
color: var(--color-text-primary);
}
.nav-link.active {
background-color: var(--color-primary-muted);
color: var(--color-primary);
border-left-color: var(--color-primary);
font-weight: var(--font-weight-semibold);
}
[data-theme="industrial"] .nav-link.active {
background-color: rgba(245, 166, 35, 0.10);
}
.nav-link__icon {
width: 16px;
height: 16px;
flex-shrink: 0;
opacity: 0.7;
}
.nav-link.active .nav-link__icon {
opacity: 1;
}
.nav-link__badge {
margin-left: auto;
background-color: var(--color-primary);
color: var(--color-text-inverse);
font-size: 0.6rem;
font-weight: var(--font-weight-bold);
padding: 1px 6px;
border-radius: var(--radius-full);
min-width: 18px;
text-align: center;
}
[data-theme="industrial"] .nav-link__badge {
color: #000;
}
.sidebar__footer {
padding: var(--space-4) var(--space-5);
border-top: 1px solid var(--color-border);
font-size: var(--text-caption);
color: var(--color-text-muted);
}
/* =====================================================================
MAIN CONTENT AREA
===================================================================== */
.main-content {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
min-width: 0;
}
/* Page header */
.page-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--space-5) var(--space-6);
background-color: var(--color-bg-elevated);
border-bottom: 1px solid var(--color-border);
flex-shrink: 0;
}
[data-theme="industrial"] .page-header {
background-color: var(--color-surface-1);
border-bottom-color: var(--color-border);
}
[data-theme="modern"] .page-header {
background-color: rgba(255,255,255,0.92);
backdrop-filter: blur(8px);
}
.page-header__title {
font-family: var(--font-heading);
font-size: var(--text-h5);
font-weight: var(--heading-weight-primary);
letter-spacing: var(--heading-tracking-h5);
color: var(--color-text-primary);
text-transform: uppercase;
}
.page-header__subtitle {
font-size: var(--text-caption);
color: var(--color-text-muted);
margin-top: 2px;
font-weight: var(--font-weight-regular);
}
.page-header__actions {
display: flex;
align-items: center;
gap: var(--space-3);
}
/* Buttons */
.btn {
display: inline-flex;
align-items: center;
gap: var(--space-2);
padding: 0 var(--space-5);
height: 36px;
border-radius: var(--radius-md);
font-family: var(--font-body);
font-size: var(--text-body-sm);
font-weight: var(--font-weight-semibold);
cursor: pointer;
border: 1.5px solid transparent;
transition: var(--transition-fast);
text-decoration: none;
white-space: nowrap;
letter-spacing: var(--tracking-normal);
}
.btn-primary {
background-color: var(--btn-primary-bg);
color: var(--btn-primary-text);
border-color: var(--btn-primary-border);
}
.btn-primary:hover {
background-color: var(--btn-primary-bg-hover);
box-shadow: var(--shadow-md);
transform: translateY(-1px);
}
.btn-primary:active {
background-color: var(--btn-primary-bg-active);
transform: translateY(0);
}
.btn-secondary {
background-color: var(--btn-secondary-bg);
color: var(--btn-secondary-text);
border-color: var(--btn-secondary-border);
}
.btn-secondary:hover {
background-color: var(--btn-secondary-bg-hover);
}
.btn-ghost {
background-color: var(--btn-ghost-bg);
color: var(--btn-ghost-text);
border-color: var(--btn-ghost-border);
}
.btn-ghost:hover {
background-color: var(--color-primary-muted);
color: var(--color-primary);
border-color: var(--color-primary);
}
.btn-sm {
height: 30px;
padding: 0 var(--space-4);
font-size: var(--text-caption);
}
[data-theme="modern"] .btn {
border-radius: var(--radius-lg);
}
/* =====================================================================
SUMMARY CARDS
===================================================================== */
.summary-bar {
display: flex;
gap: var(--space-4);
padding: var(--space-5) var(--space-6);
background-color: transparent;
flex-shrink: 0;
overflow-x: auto;
}
.summary-card {
flex: 1;
min-width: 160px;
background-color: var(--color-bg-elevated);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
padding: var(--space-4) var(--space-5);
display: flex;
flex-direction: column;
gap: var(--space-2);
transition: var(--transition-normal);
}
[data-theme="industrial"] .summary-card {
background-color: var(--color-surface-1);
border-radius: 0;
clip-path: polygon(0 0, calc(100% - 10px) 0, 100% 10px, 100% 100%, 0 100%);
}
.summary-card:hover {
border-color: var(--color-border-accent);
box-shadow: var(--shadow-md);
}
[data-theme="industrial"] .summary-card:hover {
box-shadow: var(--shadow-accent);
}
.summary-card__label {
font-size: var(--text-caption);
color: var(--color-text-muted);
text-transform: uppercase;
letter-spacing: var(--tracking-wider);
font-weight: var(--font-weight-semibold);
}
.summary-card__value {
font-family: var(--font-heading);
font-size: var(--text-h4);
font-weight: var(--heading-weight-primary);
color: var(--color-text-primary);
letter-spacing: var(--tracking-snug);
line-height: 1;
}
.summary-card__delta {
display: flex;
align-items: center;
gap: var(--space-1);
font-size: var(--text-caption);
color: var(--color-success);
}
.summary-card__delta.neg {
color: var(--color-error);
}
.summary-card__icon {
width: 32px;
height: 32px;
border-radius: var(--radius-md);
background-color: var(--color-primary-muted);
display: flex;
align-items: center;
justify-content: center;
color: var(--color-primary);
align-self: flex-start;
margin-bottom: var(--space-1);
}
[data-theme="industrial"] .summary-card__icon {
border-radius: 0;
}
/* =====================================================================
CONTENT SPLIT (list + detail)
===================================================================== */
.content-split {
display: flex;
flex: 1;
overflow: hidden;
gap: 0;
}
/* =====================================================================
LEFT PANEL — Customer List (60%)
===================================================================== */
.panel-list {
display: flex;
flex-direction: column;
width: 60%;
min-width: 0;
border-right: 1px solid var(--color-border);
overflow: hidden;
}
/* Search & filter row */
.list-toolbar {
display: flex;
align-items: center;
gap: var(--space-3);
padding: var(--space-4) var(--space-5);
background-color: var(--color-surface-1);
border-bottom: 1px solid var(--color-border);
flex-shrink: 0;
}
[data-theme="modern"] .list-toolbar {
background-color: rgba(248, 249, 255, 0.95);
}
.search-wrap {
position: relative;
flex: 1;
}
.search-icon {
position: absolute;
left: var(--space-3);
top: 50%;
transform: translateY(-50%);
color: var(--color-text-muted);
pointer-events: none;
}
.search-input {
width: 100%;
height: 38px;
padding: 0 var(--space-4) 0 38px;
background-color: var(--color-bg-overlay);
border: 1.5px solid var(--color-border);
border-radius: var(--radius-md);
font-family: var(--font-body);
font-size: var(--text-body-sm);
color: var(--color-text-primary);
outline: none;
transition: var(--transition-fast);
}
.search-input::placeholder {
color: var(--color-text-muted);
}
.search-input:focus {
border-color: var(--color-border-focus);
box-shadow: var(--shadow-focus);
}
[data-theme="modern"] .search-input {
border-radius: var(--radius-lg);
background-color: #fff;
}
.filter-select {
height: 38px;
padding: 0 var(--space-4);
background-color: var(--color-bg-overlay);
border: 1.5px solid var(--color-border);
border-radius: var(--radius-md);
font-family: var(--font-body);
font-size: var(--text-body-sm);
color: var(--color-text-primary);
outline: none;
cursor: pointer;
transition: var(--transition-fast);
}
.filter-select:focus {
border-color: var(--color-border-focus);
box-shadow: var(--shadow-focus);
}
[data-theme="modern"] .filter-select {
border-radius: var(--radius-lg);
background-color: #fff;
}
/* Customer Table */
.table-wrap {
flex: 1;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
}
.customers-table {
width: 100%;
border-collapse: collapse;
font-size: var(--text-body-sm);
}
.customers-table thead {
position: sticky;
top: 0;
z-index: 2;
}
.customers-table thead tr {
background-color: var(--color-surface-2);
border-bottom: 2px solid var(--color-border-strong);
}
[data-theme="industrial"] .customers-table thead tr {
background-color: #1d1d1d;
}
.customers-table th {
padding: var(--space-3) var(--space-4);
text-align: left;
font-size: 0.65rem;
font-weight: var(--font-weight-semibold);
letter-spacing: var(--tracking-widest);
text-transform: uppercase;
color: var(--color-text-muted);
white-space: nowrap;
}
.customers-table th:first-child { padding-left: var(--space-5); }
.customers-table th:last-child { padding-right: var(--space-5); }
.customers-table tbody tr {
border-bottom: 1px solid var(--color-border);
cursor: pointer;
transition: var(--transition-fast);
}
.customers-table tbody tr:hover {
background-color: var(--color-primary-muted);
}
.customers-table tbody tr.selected {
background-color: var(--color-primary-muted);
border-left: 3px solid var(--color-primary);
}
.customers-table td {
padding: var(--space-3) var(--space-4);
vertical-align: middle;
color: var(--color-text-primary);
white-space: nowrap;
}
.customers-table td:first-child { padding-left: var(--space-5); }
.customers-table td:last-child { padding-right: var(--space-5); }
.cell-num {
color: var(--color-text-muted);
font-size: var(--text-caption);
}
.cell-name {
font-weight: var(--font-weight-semibold);
color: var(--color-text-primary);
}
.cell-name-sub {
font-size: var(--text-caption);
color: var(--color-text-muted);
font-weight: var(--font-weight-regular);
}
.cell-rfc {
font-family: var(--font-mono);
font-size: var(--text-caption);
color: var(--color-text-secondary);
letter-spacing: 0.03em;
}
.cell-credit {
font-family: var(--font-mono);
font-size: var(--text-body-sm);
color: var(--color-success);
}
.cell-credit.low {
color: var(--color-warning);
}
.cell-credit.none {
color: var(--color-error);
}
.cell-date {
font-size: var(--text-caption);
color: var(--color-text-muted);
}
/* Status badges */
.badge {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 2px 8px;
border-radius: var(--radius-full);
font-size: 0.65rem;
font-weight: var(--font-weight-semibold);
letter-spacing: var(--tracking-wider);
text-transform: uppercase;
white-space: nowrap;
}
.badge--active {
background-color: rgba(34, 197, 94, 0.15);
color: var(--color-success);
border: 1px solid rgba(34, 197, 94, 0.3);
}
.badge--inactive {
background-color: rgba(163, 163, 163, 0.15);
color: var(--color-text-muted);
border: 1px solid var(--color-border);
}
.badge--warning {
background-color: rgba(234, 179, 8, 0.15);
color: var(--color-warning);
border: 1px solid rgba(234, 179, 8, 0.3);
}
.badge-dot {
width: 5px;
height: 5px;
border-radius: var(--radius-full);
background-color: currentColor;
}
/* Tipo chip */
.tipo-chip {
display: inline-block;
padding: 2px 7px;
border-radius: var(--radius-sm);
font-size: 0.62rem;
font-weight: var(--font-weight-semibold);
letter-spacing: var(--tracking-wide);
text-transform: uppercase;
}
.tipo-chip--taller {
background-color: rgba(245, 166, 35, 0.12);
color: var(--color-warning);
border: 1px solid rgba(245, 166, 35, 0.3);
}
[data-theme="modern"] .tipo-chip--taller {
background-color: rgba(255, 107, 53, 0.10);
color: var(--color-primary);
border-color: rgba(255, 107, 53, 0.25);
}
.tipo-chip--mostrador {
background-color: rgba(99, 102, 241, 0.12);
color: #818cf8;
border: 1px solid rgba(99, 102, 241, 0.3);
}
.tipo-chip--mayoreo {
background-color: rgba(34, 197, 94, 0.10);
color: var(--color-success);
border: 1px solid rgba(34, 197, 94, 0.3);
}
/* Table pagination footer */
.table-footer {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--space-3) var(--space-5);
background-color: var(--color-surface-1);
border-top: 1px solid var(--color-border);
flex-shrink: 0;
}
.table-footer__info {
font-size: var(--text-caption);
color: var(--color-text-muted);
}
.pagination {
display: flex;
align-items: center;
gap: var(--space-1);
}
.page-btn {
width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid var(--color-border);
border-radius: var(--radius-sm);
background: transparent;
color: var(--color-text-secondary);
font-size: var(--text-caption);
cursor: pointer;
transition: var(--transition-fast);
}
.page-btn:hover {
background-color: var(--color-primary-muted);
border-color: var(--color-primary);
color: var(--color-primary);
}
.page-btn.active {
background-color: var(--color-primary);
border-color: var(--color-primary);
color: var(--color-text-inverse);
font-weight: var(--font-weight-semibold);
}
[data-theme="industrial"] .page-btn.active {
color: #000;
}
/* =====================================================================
RIGHT PANEL — Customer Detail (40%)
===================================================================== */
.panel-detail {
width: 40%;
min-width: 0;
display: flex;
flex-direction: column;
background-color: var(--color-bg-elevated);
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
}
[data-theme="industrial"] .panel-detail {
background-color: var(--color-surface-1);
}
[data-theme="modern"] .panel-detail {
background-color: rgba(248, 249, 255, 0.95);
}
/* Empty state */
.detail-empty {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: var(--space-4);
color: var(--color-text-disabled);
padding: var(--space-10);
text-align: center;
}
.detail-empty__icon {
width: 56px;
height: 56px;
border-radius: var(--radius-xl);
border: 2px dashed var(--color-border-strong);
display: flex;
align-items: center;
justify-content: center;
color: var(--color-text-disabled);
}
.detail-empty__text {
font-size: var(--text-body-sm);
color: var(--color-text-muted);
}
/* Detail header */
.detail-header {
padding: var(--space-5) var(--space-5) var(--space-4);
border-bottom: 1px solid var(--color-border);
flex-shrink: 0;
background-color: var(--color-surface-2);
display: flex;
align-items: flex-start;
gap: var(--space-4);
}
[data-theme="industrial"] .detail-header {
background-color: #1d1d1d;
}
[data-theme="modern"] .detail-header {
background-color: rgba(240, 242, 255, 0.8);
}
/* Customer Avatar */
.customer-avatar {
width: 56px;
height: 56px;
border-radius: var(--radius-lg);
background-color: var(--color-primary);
display: flex;
align-items: center;
justify-content: center;
font-family: var(--font-heading);
font-weight: var(--heading-weight-primary);
font-size: 1.3rem;
color: var(--color-text-inverse);
flex-shrink: 0;
letter-spacing: -0.02em;
box-shadow: var(--shadow-md);
}
[data-theme="industrial"] .customer-avatar {
border-radius: 0;
color: #000;
clip-path: polygon(0 0, calc(100% - 10px) 0, 100% 10px, 100% 100%, 0 100%);
}
.detail-header__info {
flex: 1;
min-width: 0;
}
.detail-header__name {
font-family: var(--font-heading);
font-size: var(--text-h5);
font-weight: var(--heading-weight-primary);
color: var(--color-text-primary);
letter-spacing: var(--heading-tracking-h5);
text-transform: uppercase;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.detail-header__rfc {
font-family: var(--font-mono);
font-size: var(--text-caption);
color: var(--color-text-accent);
letter-spacing: 0.06em;
margin-top: 2px;
}
.detail-header__meta {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: var(--space-2);
margin-top: var(--space-2);
}
.detail-header__actions {
display: flex;
flex-direction: column;
gap: var(--space-2);
align-items: flex-end;
flex-shrink: 0;
}
/* Detail body sections */
.detail-section {
padding: var(--space-4) var(--space-5);
border-bottom: 1px solid var(--color-border);
}
.detail-section:last-child {
border-bottom: none;
}
.detail-section__title {
font-size: 0.65rem;
font-weight: var(--font-weight-semibold);
letter-spacing: var(--tracking-widest);
text-transform: uppercase;
color: var(--color-text-muted);
margin-bottom: var(--space-3);
}
/* Info rows */
.info-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--space-3);
}
.info-row {
display: flex;
flex-direction: column;
gap: 2px;
}
.info-row--full {
grid-column: 1 / -1;
}
.info-label {
font-size: var(--text-caption);
color: var(--color-text-muted);
text-transform: uppercase;
letter-spacing: var(--tracking-wider);
font-weight: var(--font-weight-semibold);
}
.info-value {
font-size: var(--text-body-sm);
color: var(--color-text-primary);
font-weight: var(--font-weight-regular);
}
.info-value--mono {
font-family: var(--font-mono);
font-size: var(--text-caption);
letter-spacing: 0.04em;
}
.info-value--accent {
color: var(--color-text-accent);
font-weight: var(--font-weight-semibold);
}
/* Credit block */
.credit-block {
display: flex;
flex-direction: column;
gap: var(--space-3);
}
.credit-metrics {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--space-3);
}
.credit-metric {
background-color: var(--color-surface-2);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
padding: var(--space-3) var(--space-3);
text-align: center;
}
[data-theme="industrial"] .credit-metric {
border-radius: 0;
}
.credit-metric__label {
font-size: 0.62rem;
color: var(--color-text-muted);
text-transform: uppercase;
letter-spacing: var(--tracking-wider);
font-weight: var(--font-weight-semibold);
}
.credit-metric__value {
font-family: var(--font-mono);
font-size: var(--text-body-sm);
font-weight: var(--font-weight-bold);
color: var(--color-text-primary);
margin-top: 3px;
}
.credit-metric__value.available { color: var(--color-success); }
.credit-metric__value.used { color: var(--color-warning); }
/* Credit progress bar */
.credit-progress {
display: flex;
flex-direction: column;
gap: var(--space-1);
}
.credit-progress__labels {
display: flex;
justify-content: space-between;
font-size: var(--text-caption);
color: var(--color-text-muted);
}
.progress-bar {
height: 8px;
background-color: var(--color-surface-3);
border-radius: var(--radius-full);
overflow: hidden;
}
.progress-bar__fill {
height: 100%;
background-color: var(--color-warning);
border-radius: var(--radius-full);
transition: width var(--duration-slow) var(--ease-out);
}
.progress-bar__fill.low { background-color: var(--color-success); }
.progress-bar__fill.high { background-color: var(--color-error); }
/* Quick action buttons */
.quick-actions {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: var(--space-2);
}
.action-btn {
display: flex;
flex-direction: column;
align-items: center;
gap: var(--space-1);
padding: var(--space-3) var(--space-2);
background-color: var(--color-surface-2);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
color: var(--color-text-secondary);
cursor: pointer;
transition: var(--transition-fast);
font-family: var(--font-body);
font-size: var(--text-caption);
font-weight: var(--font-weight-semibold);
text-align: center;
}
[data-theme="industrial"] .action-btn {
border-radius: 0;
}
.action-btn:hover {
background-color: var(--color-primary-muted);
border-color: var(--color-primary);
color: var(--color-primary);
}
.action-btn--primary {
background-color: var(--btn-primary-bg);
border-color: var(--btn-primary-bg);
color: var(--btn-primary-text);
}
[data-theme="industrial"] .action-btn--primary {
color: #000;
}
.action-btn--primary:hover {
background-color: var(--btn-primary-bg-hover);
border-color: var(--btn-primary-bg-hover);
color: var(--btn-primary-text);
}
[data-theme="industrial"] .action-btn--primary:hover {
color: #000;
}
.action-btn__icon {
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
}
/* Purchase history mini table */
.history-table {
width: 100%;
border-collapse: collapse;
font-size: var(--text-caption);
}
.history-table thead tr {
background-color: var(--color-surface-2);
border-bottom: 1px solid var(--color-border-strong);
}
.history-table th {
padding: var(--space-2) var(--space-3);
text-align: left;
font-size: 0.6rem;
font-weight: var(--font-weight-semibold);
letter-spacing: var(--tracking-widest);
text-transform: uppercase;
color: var(--color-text-muted);
white-space: nowrap;
}
.history-table tbody tr {
border-bottom: 1px solid var(--color-border);
transition: var(--transition-fast);
}
.history-table tbody tr:hover {
background-color: var(--color-primary-muted);
}
.history-table td {
padding: var(--space-2) var(--space-3);
vertical-align: middle;
}
.history-table td.folio {
font-family: var(--font-mono);
font-size: 0.7rem;
color: var(--color-text-accent);
letter-spacing: 0.04em;
}
.history-table td.total {
font-family: var(--font-mono);
font-weight: var(--font-weight-semibold);
color: var(--color-text-primary);
}
.history-table td.date {
color: var(--color-text-muted);
}
/* Status mini badges */
.mbadge {
display: inline-flex;
align-items: center;
padding: 1px 6px;
border-radius: var(--radius-full);
font-size: 0.6rem;
font-weight: var(--font-weight-semibold);
text-transform: uppercase;
letter-spacing: 0.03em;
}
.mbadge--paid {
background-color: rgba(34,197,94,0.12);
color: var(--color-success);
}
.mbadge--pending {
background-color: rgba(234,179,8,0.12);
color: var(--color-warning);
}
.mbadge--overdue {
background-color: rgba(239,68,68,0.12);
color: var(--color-error);
}
/* =====================================================================
RESPONSIVE
===================================================================== */
@media (max-width: 1024px) {
.sidebar {
width: 56px;
overflow: visible;
}
.sidebar__logo-text,
.sidebar__logo-sub,
.sidebar__section-label,
.nav-link__badge {
display: none;
}
.nav-link {
justify-content: center;
padding: var(--space-2);
border-left: none;
border-bottom: 3px solid transparent;
}
.nav-link.active {
border-left: none;
border-bottom-color: var(--color-primary);
}
.sidebar__logo {
justify-content: center;
padding: var(--space-4) var(--space-2);
}
.summary-card__delta { display: none; }
}
@media (max-width: 900px) {
.content-split {
flex-direction: column;
}
.panel-list {
width: 100%;
border-right: none;
border-bottom: 1px solid var(--color-border);
max-height: 55%;
}
.panel-detail {
width: 100%;
max-height: 45%;
}
.summary-bar {
padding: var(--space-3) var(--space-4);
gap: var(--space-3);
}
}
@media (max-width: 640px) {
.page-header__actions .btn:not(.btn-primary) {
display: none;
}
.summary-card {
min-width: 140px;
}
.customers-table th.hide-mobile,
.customers-table td.hide-mobile {
display: none;
}
.info-grid {
grid-template-columns: 1fr;
}
.credit-metrics {
grid-template-columns: 1fr 1fr;
}
}
/* =====================================================================
SLIDE PANEL (panel deslizante)
===================================================================== */
.panel-overlay {
position: fixed; inset: 0;
background: var(--overlay-backdrop, rgba(0,0,0,0.5));
z-index: var(--z-modal, 1000);
opacity: 0; visibility: hidden;
transition: opacity var(--duration-normal, .25s) var(--ease-out, ease-out),
visibility var(--duration-normal, .25s) var(--ease-out, ease-out);
}
.panel-overlay.open { opacity: 1; visibility: visible; }
.slide-panel {
position: fixed; top: 0; right: 0;
width: 380px; max-width: 90vw; height: 100vh;
background: var(--color-bg-elevated);
border-left: 1px solid var(--color-border);
box-shadow: var(--shadow-xl);
z-index: calc(var(--z-modal, 1000) + 1);
transform: translateX(100%);
transition: transform var(--duration-slow, .35s) var(--ease-out, ease-out);
display: flex; flex-direction: column; overflow: hidden;
}
.slide-panel.open { transform: translateX(0); }
.panel-header {
display: flex; align-items: center; justify-content: space-between;
padding: var(--space-4) var(--space-5);
border-bottom: 1px solid var(--color-border); flex-shrink: 0;
}
.panel-header h3 {
font-family: var(--font-heading); font-size: var(--text-h5);
font-weight: var(--heading-weight-secondary); color: var(--color-text-primary);
}
.btn-close {
width: 32px; height: 32px; display: flex; align-items: center; justify-content: center;
background: transparent; border: 1px solid var(--color-border);
border-radius: var(--radius-md); color: var(--color-text-muted);
cursor: pointer; font-size: 18px; transition: var(--transition-fast);
}
.btn-close:hover { background: var(--color-surface-2); color: var(--color-text-primary); }
.panel-body {
flex: 1; overflow-y: auto; padding: var(--space-5);
scrollbar-width: thin;
scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
}
.client-avatar-row { display: flex; align-items: center; gap: var(--space-4); margin-bottom: var(--space-5); }
.client-avatar {
width: 56px; height: 56px; border-radius: var(--radius-full);
background: var(--color-primary-muted);
display: flex; align-items: center; justify-content: center;
font-family: var(--font-heading); font-size: var(--text-h4);
font-weight: var(--heading-weight-primary); color: var(--color-text-accent);
border: 2px solid var(--color-border-accent); flex-shrink: 0;
}
.client-info-main .client-name {
font-family: var(--font-heading); font-size: var(--text-h5);
font-weight: var(--heading-weight-primary); color: var(--color-text-primary);
}
.client-info-main .client-rfc {
font-family: var(--font-mono); font-size: var(--text-body-sm); color: var(--color-text-muted);
}
.panel-section { margin-bottom: var(--space-5); }
.panel-section-title {
font-size: var(--text-caption); text-transform: uppercase;
letter-spacing: var(--tracking-widest); color: var(--color-text-muted);
font-weight: var(--font-weight-semibold); margin-bottom: var(--space-3);
}
.credit-grid { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: var(--space-3); margin-bottom: var(--space-3); }
.credit-item {
text-align: center; padding: var(--space-3);
background: var(--color-surface-1); border-radius: var(--radius-md);
border: 1px solid var(--color-border);
}
.credit-item .credit-label { font-size: var(--text-caption); color: var(--color-text-muted); margin-bottom: var(--space-1); }
.credit-item .credit-value {
font-family: var(--font-mono); font-size: var(--text-body);
font-weight: var(--font-weight-bold); color: var(--color-text-primary);
}
.credit-item .credit-value.success { color: var(--color-success); }
.credit-item .credit-value.warning { color: var(--color-warning); }
.credit-bar-track { height: 8px; background: var(--color-surface-2); border-radius: var(--radius-full); overflow: hidden; }
.credit-bar-fill { height: 100%; border-radius: var(--radius-full); background: var(--color-primary); transition: var(--transition-normal); }
.vehicle-item {
display: flex; align-items: center; gap: var(--space-3); padding: var(--space-3);
background: var(--color-surface-1); border: 1px solid var(--color-border);
border-radius: var(--radius-md); margin-bottom: var(--space-2);
}
.panel-footer {
padding: var(--space-4) var(--space-5);
border-top: 1px solid var(--color-border);
display: flex; gap: var(--space-2); flex-shrink: 0;
}
.panel-footer button {
flex: 1; padding: var(--space-3); border-radius: var(--radius-md);
font-family: var(--font-body); font-size: var(--text-body-sm);
font-weight: var(--font-weight-semibold); cursor: pointer;
transition: var(--transition-fast); border: 1px solid;
}
.panel-footer .btn-edit {
background: var(--btn-secondary-bg); color: var(--btn-secondary-text);
border-color: var(--btn-secondary-border);
}
.panel-footer .btn-edit:hover { background: var(--btn-secondary-bg-hover); }
.panel-footer .btn-sale {
background: var(--btn-primary-bg); color: var(--btn-primary-text);
border-color: var(--btn-primary-border);
}
.panel-footer .btn-sale:hover { background: var(--btn-primary-bg-hover); }
/* =====================================================================
OFFLINE BANNER
===================================================================== */
@keyframes slideDown { from { transform: translateY(-100%); opacity: 0; } to { transform: translateY(0); opacity: 1; } }
@keyframes slideUp { from { transform: translateY(0); opacity: 1; } to { transform: translateY(-100%); opacity: 0; } }
.banner {
display: flex; align-items: center; gap: var(--space-3);
padding: var(--space-3) var(--space-4); border-radius: var(--radius-md);
font-size: var(--text-body-sm); font-weight: 500; line-height: 1.4;
}
.banner--warning {
background: var(--color-warning-light, #fef9c3); color: var(--color-warning-dark, #854d0e);
border: 1px solid var(--color-warning, #eab308);
}
.banner--success {
background: var(--color-success-light, #dcfce7); color: var(--color-success-dark, #166534);
border: 1px solid var(--color-success, #22c55e);
}
.banner--error {
background: var(--color-error-light, #fef2f2); color: var(--color-error-dark, #991b1b);
border: 1px solid var(--color-error, #ef4444);
}
.banner--dismissing { animation: slideUp 0.3s ease-in forwards; }
.banner__icon { font-size: 18px; flex-shrink: 0; }
.banner__text { flex: 1; }
.banner__text strong { font-weight: 700; }
.banner__dismiss {
background: none; border: none; cursor: pointer; font-size: 18px;
padding: var(--space-1); opacity: 0.7; color: inherit;
}
.banner__dismiss:hover { opacity: 1; }
.purchases-table {
width: 100%; border-collapse: collapse;
}
.purchases-table th, .purchases-table td {
padding: var(--space-2); text-align: left; font-size: var(--text-caption);
border-bottom: 1px solid var(--color-border);
}
.purchases-table th {
color: var(--color-text-muted); text-transform: uppercase;
letter-spacing: var(--tracking-wide); font-weight: var(--font-weight-semibold);
}
.purchases-table .amount-cell {
font-family: var(--font-mono); text-align: right; font-weight: var(--font-weight-semibold);
}
.purchases-table th:last-child { text-align: right; }
</style>
</head>
<body>
<!-- ====================================================================
THEME SWITCHER BAR
==================================================================== -->
<div class="theme-bar">
<div class="theme-bar__brand">
<span class="theme-bar__brand-dot"></span>
<span>Nexus Autoparts</span>
</div>
<div class="theme-bar__center">
<span>Sucursal Principal</span>
<span>Turno: Matutino — Hugo M.</span>
</div>
<div class="theme-bar__right">
<div class="theme-switcher">
<button class="theme-btn active" id="btn-industrial" onclick="setTheme('industrial')">
<!-- gear icon -->
<svg width="10" height="10" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2"><circle cx="8" cy="8" r="2.5"/><path d="M8 2v1M8 13v1M2 8H1m14 0h-1M3.5 3.5l.7.7M11.8 11.8l.7.7M3.5 12.5l.7-.7M11.8 4.2l.7-.7"/></svg>
Industrial
</button>
<button class="theme-btn" id="btn-modern" onclick="setTheme('modern')">
<!-- sun icon -->
<svg width="10" height="10" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2"><circle cx="8" cy="8" r="3"/><line x1="8" y1="1" x2="8" y2="3"/><line x1="8" y1="13" x2="8" y2="15"/><line x1="1" y1="8" x2="3" y2="8"/><line x1="13" y1="8" x2="15" y2="8"/></svg>
Modern
</button>
</div>
<div class="theme-bar__user">
<div class="theme-bar__user-avatar">HM</div>
<span>Hugo M.</span>
</div>
</div>
</div>
<!-- ====================================================================
APP SHELL
==================================================================== -->
<div class="app-shell">
<!-- SIDEBAR -->
<aside class="sidebar">
<div class="sidebar__logo">
<div class="sidebar__logo-icon">
<!-- car/wrench icon -->
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round">
<path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.77-3.77a6 6 0 0 1-7.94 7.94l-6.91 6.91a2.12 2.12 0 0 1-3-3l6.91-6.91a6 6 0 0 1 7.94-7.94l-3.76 3.76z"/>
</svg>
</div>
<div>
<div class="sidebar__logo-text">Nexus</div>
<div class="sidebar__logo-sub">Autoparts POS</div>
</div>
</div>
<div class="sidebar__section-label">Módulos</div>
<ul class="sidebar__nav">
<li class="nav-item">
<a href="/pos/dashboard" class="nav-link">
<svg class="nav-link__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
<rect x="1" y="1" width="6" height="6" rx="1"/><rect x="9" y="1" width="6" height="6" rx="1"/>
<rect x="1" y="9" width="6" height="6" rx="1"/><rect x="9" y="9" width="6" height="6" rx="1"/>
</svg>
Dashboard
</a>
</li>
<li class="nav-item">
<a href="/pos/sale" class="nav-link">
<svg class="nav-link__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
<rect x="1" y="3" width="14" height="11" rx="1.5"/>
<path d="M5 3V2m6 1V2"/>
<path d="M1 7h14"/>
</svg>
POS
<span class="nav-link__badge">1</span>
</a>
</li>
<li class="nav-item">
<a href="/pos/catalog" class="nav-link">
<svg class="nav-link__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
<path d="M2 4h12M2 8h8M2 12h10"/>
</svg>
Catálogo
</a>
</li>
<li class="nav-item">
<a href="/pos/inventory" class="nav-link">
<svg class="nav-link__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
<rect x="2" y="3" width="12" height="11" rx="1"/><path d="M5 3V1m6 2V1M6 9l2 2 4-4"/>
</svg>
Inventario
</a>
</li>
<li class="nav-item">
<a href="/pos/customers" class="nav-link active">
<svg class="nav-link__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
<circle cx="6" cy="5" r="3"/><path d="M1 14c0-2.76 2.24-5 5-5s5 2.24 5 5"/>
<path d="M13 6l2 2-3.5 3.5"/>
</svg>
Clientes
</a>
</li>
<li class="nav-item">
<a href="/pos/invoicing" class="nav-link">
<svg class="nav-link__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
<rect x="2" y="2" width="12" height="12" rx="1"/><path d="M5 6h6M5 9h4"/>
</svg>
Facturación
<span class="nav-link__badge">3</span>
</a>
</li>
<li class="nav-item">
<a href="/pos/accounting" class="nav-link">
<svg class="nav-link__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
<path d="M8 1v14M1 8h14"/>
<circle cx="8" cy="8" r="7"/>
</svg>
Contabilidad
</a>
</li>
<li class="nav-item">
<a href="/pos/reports" class="nav-link">
<svg class="nav-link__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
<path d="M2 11V14h3l8-8-3-3L2 11z"/><path d="M12 3l1 1"/>
</svg>
Reportes
</a>
</li>
<li class="nav-item">
<a href="/pos/config" class="nav-link">
<svg class="nav-link__icon" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round">
<circle cx="8" cy="8" r="2.5"/>
<path d="M8 2v1M8 13v1M2 8H1m14 0h-1M3.5 3.5l.7.7M11.8 11.8l.7.7M3.5 12.5l.7-.7M11.8 4.2l.7-.7"/>
</svg>
Configuración
</a>
</li>
</ul>
<div class="sidebar__footer">
v1.0.0 — 2026
</div>
</aside>
<!-- MAIN CONTENT -->
<div class="main-content">
<!-- Page Header -->
<div class="page-header">
<div>
<div class="page-header__title">Gestión de Clientes</div>
<div class="page-header__subtitle">Directorio, crédito y historial de compras</div>
</div>
<div class="page-header__actions">
<button class="btn btn-ghost">
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M2 5h12M4 8h8M6 11h4"/></svg>
Filtros
</button>
<button class="btn btn-ghost">
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M14 10v3a1 1 0 01-1 1H3a1 1 0 01-1-1v-3M8 1v9M4 6l4 4 4-4"/></svg>
Exportar
</button>
<button class="btn btn-primary" onclick="openNewCustomerModal()">
<svg width="14" height="14" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2.2"><line x1="8" y1="2" x2="8" y2="14"/><line x1="2" y1="8" x2="14" y2="8"/></svg>
Nuevo Cliente
</button>
</div>
</div>
<!-- Summary Cards -->
<div class="summary-bar">
<div class="summary-card">
<div class="summary-card__icon">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round">
<circle cx="6" cy="5" r="2.5"/><path d="M1 13.5c0-2.5 2.24-4.5 5-4.5s5 2 5 4.5"/>
<path d="M11 4a2.5 2.5 0 010 5M14.5 13.5c0-2-1.5-3.5-3.5-4"/>
</svg>
</div>
<div class="summary-card__label">Total Clientes</div>
<div class="summary-card__value">1,284</div>
<div class="summary-card__delta">
<svg width="10" height="10" viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 9V3M3 6l3-3 3 3"/></svg>
+18 este mes
</div>
</div>
<div class="summary-card">
<div class="summary-card__icon">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round">
<circle cx="6" cy="5" r="2.5"/><path d="M1 13.5c0-2.5 2.24-4.5 5-4.5s5 2 5 4.5"/>
<path d="M12 8l1.5 1.5L16 7"/>
</svg>
</div>
<div class="summary-card__label">Clientes Activos</div>
<div class="summary-card__value">1,047</div>
<div class="summary-card__delta">
<svg width="10" height="10" viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 9V3M3 6l3-3 3 3"/></svg>
81.5% del total
</div>
</div>
<div class="summary-card">
<div class="summary-card__icon">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round">
<circle cx="8" cy="8" r="6.5"/>
<path d="M8 4.5v1.25M8 10.25v1.25"/>
<path d="M5.75 6.5c0-.83.67-1.5 1.5-1.5h1c.83 0 1.5.67 1.5 1.5S9.08 8 8 8s-2 .67-2 1.5.67 1.5 1.5 1.5h1.5c.83 0 1.5-.67 1.5-1.5"/>
</svg>
</div>
<div class="summary-card__label">Crédito Total Otorgado</div>
<div class="summary-card__value">$4.2M</div>
<div class="summary-card__delta">
<svg width="10" height="10" viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 9V3M3 6l3-3 3 3"/></svg>
+$320K vs mes anterior
</div>
</div>
<div class="summary-card">
<div class="summary-card__icon" style="background-color: rgba(239,68,68,0.12); color: var(--color-error);">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round">
<path d="M2 4h12l-1.5 8H3.5L2 4z"/>
<path d="M5 4V3a1 1 0 011-1h4a1 1 0 011 1v1"/>
</svg>
</div>
<div class="summary-card__label">Cuentas por Cobrar</div>
<div class="summary-card__value" style="color: var(--color-error);">$847K</div>
<div class="summary-card__delta neg">
<svg width="10" height="10" viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="2"><path d="M6 3v6M3 6l3 3 3-3"/></svg>
+$65K vs mes anterior
</div>
</div>
</div>
<!-- Content Split -->
<div class="content-split">
<!-- ============================================================
LEFT PANEL — Customer List
============================================================ -->
<div class="panel-list">
<!-- Toolbar / Search -->
<div class="list-toolbar">
<div class="search-wrap">
<span class="search-icon">
<svg width="15" height="15" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
<circle cx="7" cy="7" r="4.5"/><path d="M10.5 10.5L14 14"/>
</svg>
</span>
<input type="text" class="search-input" placeholder="Buscar por nombre, RFC, teléfono…" id="searchInput" oninput="filterCustomers()" />
</div>
<select class="filter-select" onchange="filterCustomers()" id="tipoFilter">
<option value="">Todos los tipos</option>
<option value="Taller">Taller</option>
<option value="Mostrador">Mostrador</option>
<option value="Mayoreo">Mayoreo</option>
</select>
<select class="filter-select" onchange="filterCustomers()" id="estadoFilter">
<option value="">Todos los estados</option>
<option value="Activo">Activo</option>
<option value="Inactivo">Inactivo</option>
<option value="Mora">En mora</option>
</select>
</div>
<!-- Table -->
<div class="table-wrap themed-scrollbar">
<table class="customers-table" id="customersTable">
<thead>
<tr>
<th>#</th>
<th>Nombre</th>
<th class="hide-mobile">RFC</th>
<th class="hide-mobile">Teléfono</th>
<th class="hide-mobile">Email</th>
<th>Tipo</th>
<th>Crédito Disp.</th>
<th class="hide-mobile">Última Compra</th>
<th>Estado</th>
</tr>
</thead>
<tbody id="customersBody">
<!-- Rows rendered by JS -->
</tbody>
</table>
</div>
<!-- Pagination -->
<div class="table-footer">
<div class="table-footer__info" id="tableInfo">Mostrando 115 de 1,284 clientes</div>
<div class="pagination">
<button class="page-btn">&#8249;</button>
<button class="page-btn active">1</button>
<button class="page-btn">2</button>
<button class="page-btn">3</button>
<span style="color:var(--color-text-muted);font-size:var(--text-caption);padding:0 4px;"></span>
<button class="page-btn">86</button>
<button class="page-btn">&#8250;</button>
</div>
</div>
</div>
<!-- ============================================================
RIGHT PANEL — Customer Detail
============================================================ -->
<div class="panel-detail themed-scrollbar" id="detailPanel">
<!-- Empty state (shown when no customer selected) -->
<div class="detail-empty" id="detailEmpty">
<div class="detail-empty__icon">
<svg width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<circle cx="9" cy="7" r="4"/><path d="M3 21v-2a4 4 0 014-4h4a4 4 0 014 4v2"/>
<line x1="19" y1="8" x2="19" y2="14"/><line x1="22" y1="11" x2="16" y2="11"/>
</svg>
</div>
<div class="detail-empty__text">Selecciona un cliente<br>para ver su detalle</div>
</div>
<!-- Detail content (hidden until a row is selected) -->
<div id="detailContent" style="display:none; flex-direction:column; flex:1;">
<!-- Header -->
<div class="detail-header">
<div class="customer-avatar" id="detailAvatar">MA</div>
<div class="detail-header__info">
<div class="detail-header__name" id="detailName">MIGUEL ÁNGEL TORRES</div>
<div class="detail-header__rfc" id="detailRFC">TOAM820115HDF</div>
<div class="detail-header__meta">
<span class="tipo-chip tipo-chip--taller" id="detailTipo">Taller</span>
<span class="badge badge--active" id="detailStatus"><span class="badge-dot"></span>Activo</span>
</div>
</div>
<div class="detail-header__actions">
<button class="btn btn-sm btn-ghost" onclick="closeDetail()">
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 1l10 10M11 1L1 11"/></svg>
</button>
</div>
</div>
<!-- Contact Info -->
<div class="detail-section">
<div class="detail-section__title">Información de Contacto</div>
<div class="info-grid">
<div class="info-row info-row--full">
<span class="info-label">Dirección</span>
<span class="info-value" id="detailAddress">Av. Insurgentes Sur 1602, Col. Crédito Constructor, CDMX</span>
</div>
<div class="info-row">
<span class="info-label">Teléfono</span>
<span class="info-value info-value--mono" id="detailPhone">+52 55 1234-5678</span>
</div>
<div class="info-row">
<span class="info-label">Email</span>
<span class="info-value" id="detailEmail" style="font-size:var(--text-caption);">m.torres@tallerstar.mx</span>
</div>
<div class="info-row">
<span class="info-label">Cliente desde</span>
<span class="info-value info-value--mono" id="detailSince">14 Mar 2019</span>
</div>
<div class="info-row">
<span class="info-label">Últ. Compra</span>
<span class="info-value info-value--mono" id="detailLastPurchase">28 Mar 2026</span>
</div>
</div>
</div>
<!-- Credit Info -->
<div class="detail-section">
<div class="detail-section__title">Crédito</div>
<div class="credit-block">
<div class="credit-metrics">
<div class="credit-metric">
<div class="credit-metric__label">Límite</div>
<div class="credit-metric__value" id="detailCreditLimit">$80,000</div>
</div>
<div class="credit-metric">
<div class="credit-metric__label">Disponible</div>
<div class="credit-metric__value available" id="detailCreditAvail">$48,500</div>
</div>
<div class="credit-metric">
<div class="credit-metric__label">Utilizado</div>
<div class="credit-metric__value used" id="detailCreditUsed">$31,500</div>
</div>
</div>
<div class="credit-progress">
<div class="credit-progress__labels">
<span>Utilización de crédito</span>
<span id="detailCreditPct">39%</span>
</div>
<div class="progress-bar">
<div class="progress-bar__fill low" id="detailCreditBar" style="width: 39%;"></div>
</div>
</div>
</div>
</div>
<!-- Quick Actions -->
<div class="detail-section">
<div class="detail-section__title">Acciones Rápidas</div>
<div class="quick-actions">
<button class="action-btn action-btn--primary">
<span class="action-btn__icon">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round">
<circle cx="5.5" cy="12" r="1.5"/><circle cx="11.5" cy="12" r="1.5"/>
<path d="M1 2h2l1.5 7h7l1.5-5H5"/>
</svg>
</span>
Nueva Venta
</button>
<button class="action-btn">
<span class="action-btn__icon">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round">
<path d="M2 11V14h3l8-8-3-3L2 11z"/>
</svg>
</span>
Editar
</button>
<button class="action-btn">
<span class="action-btn__icon">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round">
<rect x="2" y="2" width="12" height="12" rx="1"/><path d="M5 6h6M5 9h4"/>
</svg>
</span>
Estado de Cuenta
</button>
<button class="action-btn">
<span class="action-btn__icon">
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round">
<path d="M8 1v14M1 8h14"/>
<circle cx="8" cy="8" r="6"/>
</svg>
</span>
Historial
</button>
</div>
</div>
<!-- Purchase History -->
<div class="detail-section" style="flex:1; padding-bottom: var(--space-5);">
<div class="detail-section__title">Historial de Compras (últimas 10)</div>
<table class="history-table">
<thead>
<tr>
<th>Fecha</th>
<th>Folio</th>
<th>Total</th>
<th>Estado</th>
</tr>
</thead>
<tbody id="historyBody">
<!-- rendered by JS -->
</tbody>
</table>
</div>
</div><!-- /detailContent -->
</div><!-- /panel-detail -->
</div><!-- /content-split -->
</div><!-- /main-content -->
</div><!-- /app-shell -->
<!-- ====================================================================
JAVASCRIPT
==================================================================== -->
<script>
/* ------------------------------------------------------------------
THEME SWITCHER
------------------------------------------------------------------ */
function setTheme(theme) {
document.documentElement.setAttribute('data-theme', theme);
document.getElementById('btn-industrial').classList.toggle('active', theme === 'industrial');
document.getElementById('btn-modern').classList.toggle('active', theme === 'modern');
}
/* ------------------------------------------------------------------
SLIDE PANEL
------------------------------------------------------------------ */
const panelOverlay = document.getElementById('panelOverlay');
const slidePanel = document.getElementById('slidePanel');
function openSlidePanel() {
if (panelOverlay && slidePanel) {
panelOverlay.classList.add('open');
slidePanel.classList.add('open');
document.body.style.overflow = 'hidden';
}
}
function closeSlidePanel() {
if (panelOverlay && slidePanel) {
panelOverlay.classList.remove('open');
slidePanel.classList.remove('open');
document.body.style.overflow = '';
}
}
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') {
closeSlidePanel();
if (typeof Customers !== 'undefined') Customers.closeModal();
}
});
/* Placeholder — overridden by customers.js init */
function openNewCustomerModal() {}
function filterCustomers() {}
function closeDetail() {
if (typeof Customers !== 'undefined') Customers.closeDetail();
}
function viewCustomer(id) {
if (typeof Customers !== 'undefined') Customers.showDetail(id);
openSlidePanel();
}
</script>
<!-- Offline Banner -->
<div id="offlineBanner" class="banner banner--warning" style="display:none;position:fixed;top:0;left:0;right:0;z-index:9999;border-radius:0;animation:none;">
<span class="banner__icon"></span>
<span class="banner__text" id="offlineBannerText"><strong>Modo offline</strong> — Funciones limitadas. Solo consultas en cache disponibles.</span>
<button class="banner__dismiss" onclick="document.getElementById('offlineBanner').style.display='none'" aria-label="Cerrar">&times;</button>
</div>
<!-- Slide Panel Overlay -->
<div class="panel-overlay" id="panelOverlay" onclick="closeSlidePanel()"></div>
<!-- Slide Panel -->
<div class="slide-panel" id="slidePanel">
<div class="panel-header">
<h3>Detalle de Cliente</h3>
<button class="btn-close" onclick="closeSlidePanel()" title="Cerrar">&times;</button>
</div>
<div class="panel-body">
<div class="client-avatar-row">
<div class="client-avatar" id="panelAvatar">--</div>
<div class="client-info-main">
<div class="client-name" id="panelName">--</div>
<div class="client-rfc" id="panelRfc">RFC: --</div>
</div>
</div>
<div class="panel-section">
<div class="panel-section-title">Credito</div>
<div class="credit-grid">
<div class="credit-item">
<div class="credit-label">Limite</div>
<div class="credit-value" id="panelCreditLimit">--</div>
</div>
<div class="credit-item">
<div class="credit-label">Utilizado</div>
<div class="credit-value warning" id="panelCreditUsed">--</div>
</div>
<div class="credit-item">
<div class="credit-label">Disponible</div>
<div class="credit-value success" id="panelCreditAvail">--</div>
</div>
</div>
<div class="credit-bar-track">
<div class="credit-bar-fill" id="panelCreditBar" style="width:0%;"></div>
</div>
</div>
<div class="panel-section">
<div class="panel-section-title">Vehiculos Registrados</div>
<div id="panelVehicles" style="color:var(--color-text-muted);font-size:var(--text-body-sm);">Sin vehiculos registrados</div>
</div>
<div class="panel-section">
<div class="panel-section-title">Compras Recientes</div>
<div id="panelPurchases" style="color:var(--color-text-muted);font-size:var(--text-body-sm);">Sin compras recientes</div>
</div>
</div>
<div class="panel-footer">
<button class="btn-edit" onclick="if(typeof Customers!=='undefined') Customers.editCurrent();">Editar Cliente</button>
<button class="btn-sale" onclick="window.location.href='/pos/';">Nueva Venta</button>
</div>
</div>
<script src="/pos/static/js/app-init.js"></script>
<script src="/pos/static/js/sidebar.js"></script>
<script src="/pos/static/js/customers.js"></script>
<script src="/pos/static/js/offline-banner.js"></script>
<script src="/pos/static/js/sync-engine.js"></script>
<script>if('serviceWorker' in navigator){navigator.serviceWorker.register('/pos/sw.js',{scope:'/pos/'});}</script>
</body>
</html>