Files
Autoparts-DB/pos/templates/catalog.html
consultoria-as f9589f4a4e feat(pos): add inventory-aware chat context (#31) and VIN decoder (#17)
Chat now fetches tenant inventory summary (brands, counts, low-stock)
and injects it into the AI system prompt so responses prioritize local
stock. VIN decoder uses free NHTSA vPIC API to decode 17-char VINs and
auto-fills the vehicle selector dropdowns when a catalog match is found.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 08:14:45 +00:00

750 lines
44 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>Catalogo — Nexus Autoparts POS</title>
<link rel="stylesheet" href="/pos/static/css/tokens.css" />
<link rel="stylesheet" href="/pos/static/css/chat.css" />
<link rel="stylesheet" href="/pos/static/css/onboarding.css" />
<link rel="manifest" href="/pos/static/pwa/manifest.json" />
<meta name="theme-color" content="#F5A623" />
<script src="/pos/static/js/native-bridge.js"></script>
<style>
/* =========================================================================
BASE RESET & SHELL
========================================================================= */
*, *::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);
color: var(--color-text-primary);
background-color: var(--color-bg-base);
transition: background-color var(--duration-normal) var(--ease-in-out),
color var(--duration-normal) var(--ease-in-out);
overflow: hidden;
}
[data-theme="modern"] body {
background-image: radial-gradient(circle, var(--dot-grid-color) 1px, transparent 1px);
background-size: var(--dot-grid-size) var(--dot-grid-size);
}
/* =========================================================================
APP LAYOUT
========================================================================= */
.app-shell { display: flex; height: 100vh; padding-top: 36px; }
/* =========================================================================
SIDEBAR (shared pattern)
========================================================================= */
.sidebar {
width: 260px; flex-shrink: 0; display: flex; flex-direction: column;
background: var(--color-bg-elevated); border-right: 1px solid var(--color-border);
overflow-y: auto; transition: var(--transition-normal);
}
.sidebar__brand {
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); flex-shrink: 0;
}
.brand-logo {
width: 40px; height: 40px; display: flex; align-items: center; justify-content: center;
background: var(--color-primary); color: var(--color-text-inverse);
font-family: var(--font-heading); font-weight: var(--heading-weight-primary);
font-size: 1.375rem; letter-spacing: var(--tracking-tight); flex-shrink: 0;
}
[data-theme="industrial"] .brand-logo { clip-path: polygon(0 0, calc(100% - 10px) 0, 100% 10px, 100% 100%, 0 100%); border-radius: 0; }
[data-theme="modern"] .brand-logo { border-radius: var(--radius-md); }
.brand-name { display: flex; flex-direction: column; line-height: 1; }
.brand-name__primary { font-family: var(--font-heading); font-weight: var(--heading-weight-primary); font-size: 1.125rem; letter-spacing: var(--tracking-wide); color: var(--color-text-primary); text-transform: uppercase; }
.brand-name__sub { font-family: var(--font-body); font-size: var(--text-caption); color: var(--color-text-muted); letter-spacing: var(--tracking-wider); text-transform: uppercase; margin-top: 2px; }
.sidebar__nav { flex: 1; padding: var(--space-3) 0; }
.nav-section-label { padding: var(--space-3) var(--space-5) var(--space-1); font-size: var(--text-caption); font-family: var(--font-body); font-weight: var(--font-weight-semibold); color: var(--color-text-muted); letter-spacing: var(--tracking-widest); text-transform: uppercase; }
.nav-item { display: flex; align-items: center; gap: var(--space-3); padding: var(--space-2) var(--space-5); color: var(--color-text-secondary); font-family: var(--font-body); font-size: var(--text-body-sm); font-weight: var(--font-weight-regular); text-decoration: none; cursor: pointer; border: none; background: none; width: 100%; text-align: left; transition: var(--transition-fast); border-left: 3px solid transparent; }
.nav-item:hover { background: var(--color-primary-muted); color: var(--color-text-primary); border-left-color: var(--color-primary); }
.nav-item.is-active { background: var(--color-primary-muted); color: var(--color-primary); font-weight: var(--font-weight-semibold); border-left-color: var(--color-primary); }
[data-theme="industrial"] .nav-item.is-active { background: rgba(245, 166, 35, 0.12); }
.nav-item__icon { width: 18px; height: 18px; opacity: 0.75; flex-shrink: 0; }
.nav-item.is-active .nav-item__icon, .nav-item:hover .nav-item__icon { opacity: 1; }
.nav-item__badge { margin-left: auto; background: var(--color-primary); color: var(--color-text-inverse); font-size: 10px; font-weight: var(--font-weight-bold); padding: 1px 6px; border-radius: var(--radius-full); line-height: 1.4; }
.sidebar__profile { padding: var(--space-4) var(--space-5); border-top: 1px solid var(--color-border); display: flex; align-items: center; gap: var(--space-3); flex-shrink: 0; }
.profile-avatar { width: 36px; height: 36px; background: var(--color-primary); color: var(--color-text-inverse); display: flex; align-items: center; justify-content: center; font-family: var(--font-heading); font-weight: var(--heading-weight-primary); font-size: 0.9rem; flex-shrink: 0; }
[data-theme="industrial"] .profile-avatar { clip-path: polygon(0 0, calc(100% - 8px) 0, 100% 8px, 100% 100%, 0 100%); }
[data-theme="modern"] .profile-avatar { border-radius: var(--radius-full); }
.profile-info { flex: 1; min-width: 0; }
.profile-info__name { font-size: var(--text-body-sm); font-weight: var(--font-weight-semibold); color: var(--color-text-primary); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.profile-info__role { font-size: var(--text-caption); color: var(--color-text-muted); }
/* =========================================================================
MAIN CONTENT
========================================================================= */
.main-content { flex: 1; display: flex; flex-direction: column; overflow: hidden; min-width: 0; }
/* Header with breadcrumb + search */
.content-header {
display: flex; align-items: center; justify-content: space-between;
padding: 0 var(--space-6); height: 56px; flex-shrink: 0;
background: var(--color-bg-elevated); border-bottom: 1px solid var(--color-border);
}
.breadcrumb { display: flex; align-items: center; gap: var(--space-2); font-size: var(--text-body-sm); color: var(--color-text-muted); flex-wrap: wrap; }
.breadcrumb__link { color: var(--color-text-muted); text-decoration: none; cursor: pointer; transition: var(--transition-fast); }
.breadcrumb__link:hover { color: var(--color-primary); }
.breadcrumb__sep { color: var(--color-text-disabled); }
.breadcrumb__current { color: var(--color-text-primary); font-weight: var(--font-weight-semibold); }
.header-actions { display: flex; align-items: center; gap: var(--space-3); }
/* Search bar */
.search-bar {
display: flex; align-items: center; gap: var(--space-2);
background: var(--color-bg-overlay); border: 1px solid var(--color-border);
padding: var(--space-1) var(--space-3); width: 360px; transition: var(--transition-fast);
}
[data-theme="industrial"] .search-bar { border-radius: 0; }
[data-theme="modern"] .search-bar { border-radius: var(--radius-md); }
.search-bar:focus-within { border-color: var(--color-border-focus); box-shadow: var(--shadow-focus); }
.search-bar svg { color: var(--color-text-muted); flex-shrink: 0; }
.search-bar input {
flex: 1; border: none; background: transparent; color: var(--color-text-primary);
font-family: var(--font-body); font-size: var(--text-body-sm); outline: none;
height: 32px;
}
.search-bar input::placeholder { color: var(--color-text-disabled); }
/* Search dropdown */
.search-dropdown {
position: absolute; top: 100%; left: 0; right: 0;
background: var(--color-bg-elevated); border: 1px solid var(--color-border);
box-shadow: var(--shadow-lg); max-height: 400px; overflow-y: auto;
display: none; z-index: var(--z-dropdown);
}
[data-theme="industrial"] .search-dropdown { border-radius: 0; }
[data-theme="modern"] .search-dropdown { border-radius: var(--radius-md); }
.search-dropdown.is-visible { display: block; }
.search-result-item {
display: flex; align-items: center; gap: var(--space-3);
padding: var(--space-3) var(--space-4); cursor: pointer;
border-bottom: 1px solid var(--color-border); transition: var(--transition-fast);
}
.search-result-item:hover { background: var(--color-primary-muted); }
.search-result-item:last-child { border-bottom: none; }
.search-result__oem { font-weight: var(--font-weight-semibold); color: var(--color-primary); font-size: var(--text-body-sm); }
.search-result__name { color: var(--color-text-primary); font-size: var(--text-body-sm); }
.search-result__vehicle { font-size: var(--text-caption); color: var(--color-text-muted); }
/* =========================================================================
PAGE BODY
========================================================================= */
.page-body {
flex: 1; overflow-y: auto; padding: var(--space-6);
display: flex; flex-direction: column; gap: var(--space-5);
}
[data-theme="modern"] .page-body {
background-image: radial-gradient(circle, var(--dot-grid-color) 1px, transparent 1px);
background-size: var(--dot-grid-size) var(--dot-grid-size);
}
/* Level title */
.level-title {
font-family: var(--font-heading); font-weight: var(--heading-weight-primary);
font-size: var(--text-h4); color: var(--color-text-primary);
letter-spacing: var(--tracking-wide); text-transform: uppercase;
}
[data-theme="industrial"] .level-title { color: var(--color-primary); }
/* Filter input (quick filter within level) */
.level-filter {
padding: var(--space-2) var(--space-3);
background: var(--color-bg-overlay); border: 1px solid var(--color-border);
color: var(--color-text-primary); font-family: var(--font-body);
font-size: var(--text-body-sm); outline: none; width: 100%; max-width: 400px;
transition: var(--transition-fast);
}
[data-theme="industrial"] .level-filter { border-radius: 0; }
[data-theme="modern"] .level-filter { border-radius: var(--radius-md); }
.level-filter:focus { border-color: var(--color-border-focus); box-shadow: var(--shadow-focus); }
/* =========================================================================
NAVIGATION CARDS
========================================================================= */
.nav-grid {
display: grid; gap: var(--space-4);
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
}
.nav-grid--years {
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
}
.nav-grid--parts {
grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
}
.nav-card {
display: flex; flex-direction: column; justify-content: center; align-items: center;
padding: var(--space-5) var(--space-4); gap: var(--space-2);
background: var(--color-bg-elevated); border: 1px solid var(--color-border);
cursor: pointer; transition: var(--transition-fast); text-align: center;
box-shadow: var(--shadow-sm); min-height: 80px;
}
[data-theme="industrial"] .nav-card { border-radius: 0; clip-path: polygon(0 0, calc(100% - 14px) 0, 100% 14px, 100% 100%, 0 100%); }
[data-theme="modern"] .nav-card { border-radius: var(--radius-lg); }
.nav-card:hover { border-color: var(--color-primary); box-shadow: var(--shadow-md); transform: translateY(-2px); }
.nav-card__name { font-family: var(--font-heading); font-weight: var(--heading-weight-secondary); font-size: var(--text-body); color: var(--color-text-primary); letter-spacing: var(--tracking-wide); }
.nav-card__sub { font-size: var(--text-caption); color: var(--color-text-muted); }
.nav-card__count { font-size: var(--text-caption); color: var(--color-primary); font-weight: var(--font-weight-semibold); }
/* Year buttons (compact) */
.nav-card--year { min-height: 48px; padding: var(--space-3); }
.nav-card--year .nav-card__name { font-size: var(--text-h5); }
/* =========================================================================
PART CARDS
========================================================================= */
.part-card {
display: flex; flex-direction: column;
background: var(--color-bg-elevated); border: 1px solid var(--color-border);
box-shadow: var(--shadow-sm); cursor: pointer; transition: var(--transition-fast);
overflow: hidden;
}
[data-theme="industrial"] .part-card { border-radius: 0; clip-path: polygon(0 0, calc(100% - 14px) 0, 100% 14px, 100% 100%, 0 100%); }
[data-theme="modern"] .part-card { border-radius: var(--radius-lg); }
.part-card:hover { border-color: var(--color-primary); box-shadow: var(--shadow-md); }
.part-card__image {
height: 120px; display: flex; align-items: center; justify-content: center;
background: var(--color-bg-overlay); position: relative; overflow: hidden;
color: var(--color-text-disabled);
}
.part-card__image img { width: 100%; height: 100%; object-fit: contain; }
.part-card__body { padding: var(--space-3) var(--space-4); flex: 1; }
.part-card__oem { font-family: var(--font-mono, monospace); font-size: var(--text-caption); color: var(--color-primary); font-weight: var(--font-weight-semibold); margin-bottom: var(--space-1); }
.part-card__name { font-size: var(--text-body-sm); font-weight: var(--font-weight-semibold); color: var(--color-text-primary); line-height: 1.3; }
.part-card__footer {
padding: var(--space-3) var(--space-4);
border-top: 1px solid var(--color-border);
display: flex; align-items: center; justify-content: space-between;
}
.part-card__price { font-weight: var(--font-weight-bold); color: var(--color-text-primary); }
/* Stock badges */
.stock-badge {
display: inline-flex; align-items: center; gap: 4px;
font-size: 11px; font-weight: var(--font-weight-bold);
padding: 2px 8px; border-radius: var(--radius-full);
}
.stock-badge--local { background: var(--color-success-light); color: var(--color-success-dark); }
.stock-badge--bodega { background: var(--color-warning-light); color: var(--color-warning-dark); }
.stock-badge--none { background: var(--color-neutral-200); color: var(--color-neutral-600); }
[data-theme="industrial"] .stock-badge--none { background: var(--color-neutral-700); color: var(--color-neutral-400); }
/* =========================================================================
DETAIL PANEL (slide-in from right)
========================================================================= */
.detail-overlay {
position: fixed; inset: 0; z-index: calc(var(--z-modal) - 2);
background: rgba(0,0,0,0.3); backdrop-filter: blur(2px); display: none;
}
.detail-overlay.is-visible { display: block; }
.detail-panel {
position: fixed; top: 0; right: 0; bottom: 0;
width: 440px; max-width: 100vw; z-index: calc(var(--z-modal) - 1);
background: var(--color-bg-elevated); border-left: 1px solid var(--color-border);
box-shadow: var(--shadow-xl); display: flex; flex-direction: column;
transform: translateX(100%); transition: transform var(--duration-normal) var(--ease-in-out);
}
.detail-panel.is-open { transform: translateX(0); }
.detail-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;
}
.detail-header h3 { font-family: var(--font-heading); font-size: var(--text-h5); font-weight: var(--heading-weight-secondary); color: var(--color-text-primary); }
.detail-close {
background: none; border: none; cursor: pointer; font-size: 1.4rem;
color: var(--color-text-secondary); padding: var(--space-1);
}
.detail-close:hover { color: var(--color-text-primary); }
.detail-body { flex: 1; overflow-y: auto; padding: var(--space-5); }
.detail-section { margin-bottom: var(--space-5); }
.detail-section__title {
font-family: var(--font-heading); font-size: var(--text-body-sm);
font-weight: var(--heading-weight-secondary); color: var(--color-text-muted);
text-transform: uppercase; letter-spacing: var(--tracking-wider);
margin-bottom: var(--space-3); padding-bottom: var(--space-2);
border-bottom: 1px solid var(--color-border);
}
.detail-oem { font-family: var(--font-mono, monospace); font-size: var(--text-h5); color: var(--color-primary); font-weight: var(--font-weight-bold); margin-bottom: var(--space-2); }
.detail-name { font-size: var(--text-body); font-weight: var(--font-weight-semibold); color: var(--color-text-primary); margin-bottom: var(--space-2); }
.detail-desc { font-size: var(--text-body-sm); color: var(--color-text-secondary); line-height: 1.5; }
/* Stock info */
.stock-row { display: flex; align-items: center; justify-content: space-between; padding: var(--space-2) 0; }
.stock-label { font-size: var(--text-body-sm); color: var(--color-text-secondary); }
.stock-value { font-weight: var(--font-weight-bold); font-size: var(--text-body-sm); }
.stock-value--ok { color: var(--color-success); }
.stock-value--zero { color: var(--color-text-muted); }
/* Bodega table */
.bodega-table { width: 100%; font-size: var(--text-body-sm); border-collapse: collapse; }
.bodega-table th { text-align: left; font-weight: var(--font-weight-semibold); color: var(--color-text-muted); font-size: var(--text-caption); text-transform: uppercase; letter-spacing: var(--tracking-wider); padding: var(--space-2) var(--space-2); border-bottom: 1px solid var(--color-border); }
.bodega-table td { padding: var(--space-2); border-bottom: 1px solid var(--color-border); color: var(--color-text-primary); }
/* Alternatives list */
.alt-item { display: flex; align-items: center; justify-content: space-between; padding: var(--space-2) 0; border-bottom: 1px solid var(--color-border); }
.alt-item:last-child { border-bottom: none; }
.alt-item__pn { font-weight: var(--font-weight-semibold); color: var(--color-text-primary); font-size: var(--text-body-sm); }
.alt-item__mfr { font-size: var(--text-caption); color: var(--color-text-muted); }
.alt-item__stock { font-size: var(--text-caption); }
/* Add to cart section */
.detail-footer {
padding: var(--space-4) var(--space-5); border-top: 1px solid var(--color-border);
flex-shrink: 0; background: var(--color-bg-elevated);
}
.qty-row { display: flex; align-items: center; gap: var(--space-3); margin-bottom: var(--space-3); }
.qty-btn {
width: 36px; height: 36px; display: flex; align-items: center; justify-content: center;
border: 1px solid var(--color-border); background: var(--color-bg-base);
color: var(--color-text-primary); cursor: pointer; font-size: 1.2rem;
transition: var(--transition-fast);
}
[data-theme="industrial"] .qty-btn { border-radius: 0; }
[data-theme="modern"] .qty-btn { border-radius: var(--radius-sm); }
.qty-btn:hover { border-color: var(--color-primary); color: var(--color-primary); }
.qty-display { font-weight: var(--font-weight-bold); font-size: var(--text-body); min-width: 30px; text-align: center; }
/* Buttons */
.btn { display: inline-flex; align-items: center; justify-content: center; gap: var(--space-2); padding: 0 var(--space-5); height: 40px; font-family: var(--font-body); font-size: var(--text-body-sm); font-weight: var(--font-weight-semibold); border: 1px solid transparent; cursor: pointer; transition: var(--transition-fast); white-space: nowrap; letter-spacing: var(--tracking-wide); }
[data-theme="industrial"] .btn { border-radius: 0; clip-path: polygon(0 0, calc(100% - 10px) 0, 100% 10px, 100% 100%, 0 100%); text-transform: uppercase; }
[data-theme="modern"] .btn { border-radius: var(--radius-md); }
.btn-primary { background: var(--btn-primary-bg); color: var(--btn-primary-text); border-color: var(--btn-primary-border); }
.btn-primary:hover { background: var(--btn-primary-bg-hover); }
.btn-primary:disabled { opacity: 0.5; cursor: not-allowed; }
.btn-ghost { background: var(--btn-ghost-bg); color: var(--btn-ghost-text); border-color: var(--btn-ghost-border); }
.btn-ghost:hover { border-color: var(--color-primary); color: var(--color-primary); }
/* =========================================================================
PAGINATION
========================================================================= */
.pagination { display: flex; align-items: center; justify-content: center; gap: var(--space-2); padding: var(--space-4) 0; }
.page-item { min-width: 36px; height: 36px; display: flex; align-items: center; justify-content: center; border: 1px solid var(--color-border); background: var(--color-bg-elevated); color: var(--color-text-secondary); font-size: var(--text-body-sm); font-weight: var(--font-weight-semibold); cursor: pointer; transition: var(--transition-fast); }
[data-theme="industrial"] .page-item { border-radius: 0; }
[data-theme="modern"] .page-item { border-radius: var(--radius-md); }
.page-item:hover { border-color: var(--color-primary); color: var(--color-primary); }
.page-item.is-active { background: var(--color-primary); border-color: var(--color-primary); color: var(--color-text-inverse); }
.page-item.is-disabled { opacity: 0.4; cursor: not-allowed; }
.page-item--wide { padding: 0 var(--space-4); gap: var(--space-2); }
/* =========================================================================
EMPTY STATE
========================================================================= */
.empty-state {
display: none; flex-direction: column; align-items: center; justify-content: center;
padding: var(--space-10) var(--space-6); text-align: center; gap: var(--space-3);
}
.empty-state.is-visible { display: flex; }
.empty-state__title { font-family: var(--font-heading); font-weight: var(--heading-weight-secondary); font-size: var(--text-h4); color: var(--color-text-primary); }
.empty-state__subtitle { font-size: var(--text-body-sm); color: var(--color-text-muted); max-width: 400px; }
/* =========================================================================
LOADING SPINNER
========================================================================= */
.loading { display: none; justify-content: center; padding: var(--space-10); }
.loading.is-visible { display: flex; }
.spinner { width: 40px; height: 40px; border: 3px solid var(--color-border); border-top-color: var(--color-primary); border-radius: 50%; animation: spin 0.8s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
/* =========================================================================
CART FAB + SIDEBAR
========================================================================= */
.cart-fab {
position: fixed; bottom: var(--space-6); right: var(--space-6);
width: 56px; height: 56px; z-index: var(--z-sticky);
display: flex; align-items: center; justify-content: center;
background: var(--color-primary); color: var(--color-text-inverse);
border: none; cursor: pointer; box-shadow: var(--shadow-lg);
transition: var(--transition-fast);
}
[data-theme="industrial"] .cart-fab { border-radius: 0; clip-path: polygon(0 0, calc(100% - 12px) 0, 100% 12px, 100% 100%, 0 100%); }
[data-theme="modern"] .cart-fab { border-radius: var(--radius-full); }
.cart-fab:hover { transform: scale(1.08); }
.cart-fab__badge {
position: absolute; top: -4px; right: -4px; min-width: 20px; height: 20px;
padding: 0 5px; border-radius: var(--radius-full);
background: var(--color-error); color: #fff; font-size: 11px;
font-weight: var(--font-weight-bold); display: none;
align-items: center; justify-content: center; line-height: 1;
}
.cart-sidebar {
position: fixed; top: 0; right: 0; bottom: 0; width: 360px; max-width: 100vw;
z-index: var(--z-modal); background: var(--color-bg-elevated);
border-left: 1px solid var(--color-border); box-shadow: var(--shadow-xl);
display: flex; flex-direction: column;
transform: translateX(100%); transition: transform var(--duration-normal) var(--ease-in-out);
}
.cart-sidebar.open { transform: translateX(0); }
.cart-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; }
.cart-header h3 { font-family: var(--font-heading); font-size: var(--text-h5); font-weight: var(--heading-weight-secondary); color: var(--color-text-primary); }
.cart-items { flex: 1; overflow-y: auto; padding: var(--space-3) var(--space-4); }
.cart-item { display: flex; gap: var(--space-3); padding: var(--space-3) 0; border-bottom: 1px solid var(--color-border); }
.cart-item:last-child { border-bottom: none; }
.cart-footer { padding: var(--space-4) var(--space-5); border-top: 1px solid var(--color-border); flex-shrink: 0; background: var(--color-bg-elevated); }
.cart-totals { display: flex; flex-direction: column; gap: var(--space-1); font-size: var(--text-body-sm); color: var(--color-text-secondary); }
.cart-overlay { position: fixed; inset: 0; z-index: calc(var(--z-modal) - 1); background: rgba(0,0,0,0.3); backdrop-filter: blur(2px); display: none; }
.cart-overlay.open { display: block; }
/* =========================================================================
THEME BAR
========================================================================= */
.theme-bar { position: fixed; top: 0; left: 0; right: 0; z-index: var(--z-toast); display: flex; align-items: center; justify-content: flex-end; gap: var(--space-2); padding: var(--space-2) var(--space-4); background: var(--color-bg-overlay); border-bottom: 1px solid var(--color-border); backdrop-filter: blur(8px); height: 36px; }
.theme-bar__label { font-size: var(--text-caption); color: var(--color-text-muted); font-family: var(--font-body); letter-spacing: var(--tracking-wide); text-transform: uppercase; }
.theme-btn { display: inline-flex; align-items: center; gap: var(--space-1); padding: 3px var(--space-3); border: 1px solid var(--color-border); border-radius: var(--radius-full); background: transparent; color: var(--color-text-secondary); font-family: var(--font-body); font-size: var(--text-caption); font-weight: var(--font-weight-semibold); cursor: pointer; transition: var(--transition-fast); }
.theme-btn:hover { border-color: var(--color-primary); color: var(--color-primary); }
.theme-btn.is-active { background: var(--color-primary); border-color: var(--color-primary); color: var(--color-text-inverse); }
/* =========================================================================
RESPONSIVE
========================================================================= */
/* ── Vehicle Selector ── */
.vehicle-selector {
background: var(--color-bg-elevated);
border-bottom: 2px solid var(--color-primary-muted, rgba(245,166,35,0.2));
padding: var(--space-3) var(--space-5);
flex-shrink: 0;
}
.vehicle-selector__inner {
display: flex;
align-items: flex-end;
gap: var(--space-3);
flex-wrap: wrap;
}
.vs-group { display: flex; flex-direction: column; gap: 4px; flex: 1; min-width: 140px; }
.vs-label {
font-size: var(--text-caption, 0.75rem);
font-weight: var(--font-weight-semibold, 600);
color: var(--color-text-muted);
text-transform: uppercase;
letter-spacing: var(--tracking-wider, 0.04em);
}
.vs-select {
padding: var(--space-2) var(--space-3);
background: var(--color-bg-base);
color: var(--color-text-primary);
border: 1px solid var(--color-border);
font-family: var(--font-body);
font-size: var(--text-body-sm, 0.875rem);
cursor: pointer;
transition: var(--transition-fast);
appearance: auto;
}
[data-theme="industrial"] .vs-select { border-radius: 0; }
[data-theme="modern"] .vs-select { border-radius: var(--radius-md); }
.vs-select:focus { border-color: var(--color-primary); outline: none; box-shadow: var(--shadow-focus); }
.vs-select:disabled { opacity: 0.4; cursor: not-allowed; }
.vs-arrow {
color: var(--color-primary);
font-size: 1.4rem;
font-weight: 700;
padding-bottom: 6px;
flex-shrink: 0;
}
.vs-clear {
background: none;
border: 1px solid var(--color-border);
color: var(--color-text-muted);
padding: var(--space-2) var(--space-3);
cursor: pointer;
font-size: 1rem;
transition: var(--transition-fast);
}
[data-theme="industrial"] .vs-clear { border-radius: 0; }
[data-theme="modern"] .vs-clear { border-radius: var(--radius-md); }
.vs-clear:hover { color: var(--color-error); border-color: var(--color-error); }
@media (max-width: 768px) {
.sidebar { position: fixed; left: -260px; z-index: var(--z-modal); transition: left var(--duration-normal) var(--ease-in-out); height: 100vh; }
.sidebar.is-open { left: 0; }
.search-bar { width: 200px; }
.detail-panel { width: 100%; }
.nav-grid { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); }
.vehicle-selector__inner { flex-direction: column; }
.vs-arrow { display: none; }
}
</style>
</head>
<body>
<!-- Theme switcher bar -->
<div class="theme-bar" role="toolbar" aria-label="Cambiar tema">
<span class="theme-bar__label">Tema:</span>
<button class="theme-btn is-active" data-theme-switch="industrial" aria-pressed="true">
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M12 2l2.4 1.6L17 3l1 2.6 2.4 1.6-.2 2.8L22 12l-1.8 2 .2 2.8-2.4 1.6-1 2.6-2.6-.2L12 22l-2.4-1.6L7 21l-1-2.6-2.4-1.6.2-2.8L2 12l1.8-2-.2-2.8L6 5.6 7 3l2.6.2z"/><circle cx="12" cy="12" r="3"/></svg>
Industrial
</button>
<button class="theme-btn" data-theme-switch="modern" aria-pressed="false">
<svg width="11" height="11" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M12 2v4M12 18v4M4.93 4.93l2.83 2.83M16.24 16.24l2.83 2.83M2 12h4M18 12h4M4.93 19.07l2.83-2.83M16.24 7.76l2.83-2.83"/></svg>
Moderno
</button>
</div>
<!-- Mobile sidebar overlay -->
<div class="sidebar-overlay" id="sidebarOverlay" role="presentation"></div>
<!-- APP SHELL -->
<div class="app-shell">
<!-- SIDEBAR -->
<aside class="sidebar themed-scrollbar" id="sidebar" role="navigation" aria-label="Menu principal">
<div class="sidebar__brand">
<div class="brand-logo" aria-hidden="true">N</div>
<div class="brand-name">
<span class="brand-name__primary">Nexus</span>
<span class="brand-name__sub">Autoparts POS</span>
</div>
</div>
<nav class="sidebar__nav">
<div class="nav-section-label">Principal</div>
<a class="nav-item" href="/pos/dashboard" role="menuitem">
<svg class="nav-item__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><rect x="3" y="3" width="7" height="7" rx="1"/><rect x="14" y="3" width="7" height="7" rx="1"/><rect x="3" y="14" width="7" height="7" rx="1"/><rect x="14" y="14" width="7" height="7" rx="1"/></svg>
Dashboard
</a>
<a class="nav-item" href="/pos/sale" role="menuitem">
<svg class="nav-item__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><path d="M6 2L3 6v14a2 2 0 002 2h14a2 2 0 002-2V6l-3-4z"/><path d="M3 6h18"/><path d="M16 10a4 4 0 01-8 0"/></svg>
Punto de Venta
</a>
<a class="nav-item" href="/pos/inventory" role="menuitem">
<svg class="nav-item__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><path d="M21 16V8a2 2 0 00-1-1.73l-7-4a2 2 0 00-2 0l-7 4A2 2 0 003 8v8a2 2 0 001 1.73l7 4a2 2 0 002 0l7-4A2 2 0 0021 16z"/></svg>
Inventario
</a>
<a class="nav-item is-active" href="/pos/catalog" role="menuitem" aria-current="page">
<svg class="nav-item__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M8 7h8M8 12h8M8 17h5"/></svg>
Catalogo
</a>
<div class="nav-section-label" style="margin-top: var(--space-2);">Gestion</div>
<a class="nav-item" href="/pos/customers" role="menuitem">
<svg class="nav-item__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 00-3-3.87"/><path d="M16 3.13a4 4 0 010 7.75"/></svg>
Clientes
</a>
<a class="nav-item" href="/pos/invoicing" role="menuitem">
<svg class="nav-item__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8z"/><polyline points="14,2 14,8 20,8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/></svg>
Facturacion
</a>
<a class="nav-item" href="/pos/accounting" role="menuitem">
<svg class="nav-item__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><line x1="12" y1="1" x2="12" y2="23"/><path d="M17 5H9.5a3.5 3.5 0 000 7h5a3.5 3.5 0 010 7H6"/></svg>
Contabilidad
</a>
<a class="nav-item" href="/pos/reports" role="menuitem">
<svg class="nav-item__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><polyline points="22,12 18,12 15,21 9,3 6,12 2,12"/></svg>
Reportes
</a>
<div class="nav-section-label" style="margin-top: var(--space-2);">Sistema</div>
<a class="nav-item" href="/pos/config" role="menuitem">
<svg class="nav-item__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75"><circle cx="12" cy="12" r="3"/><path d="M19.07 4.93l-1.41 1.41M6.34 17.66l-1.41 1.41M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M12 2v2M12 20v2M2 12h2M20 12h2"/></svg>
Configuracion
</a>
</nav>
<div class="sidebar__profile">
<div class="profile-avatar" id="profileAvatar" aria-hidden="true">--</div>
<div class="profile-info">
<div class="profile-info__name" id="profileName">--</div>
<div class="profile-info__role" id="profileRole">--</div>
</div>
</div>
</aside>
<!-- MAIN CONTENT -->
<main class="main-content">
<!-- Header: breadcrumb + search -->
<header class="content-header">
<nav class="breadcrumb" id="breadcrumb" aria-label="Navegacion del catalogo">
<span class="breadcrumb__current">Catalogo</span>
</nav>
<div class="header-actions" style="position:relative;">
<div class="search-bar" id="searchBar">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/></svg>
<input type="text" id="searchInput" placeholder="Buscar por numero de parte o nombre... (F1)" autocomplete="off" />
<button type="button" id="btnScanBarcode" title="Escanear codigo de barras" style="background:none;border:none;cursor:pointer;padding:4px 8px;color:var(--color-text-muted);display:flex;align-items:center;" onclick="CatalogApp.startBarcodeScan()">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M3 7V5a2 2 0 012-2h2"/><path d="M17 3h2a2 2 0 012 2v2"/><path d="M21 17v2a2 2 0 01-2 2h-2"/><path d="M7 21H5a2 2 0 01-2-2v-2"/><line x1="7" y1="12" x2="17" y2="12"/><line x1="7" y1="8" x2="17" y2="8"/><line x1="7" y1="16" x2="17" y2="16"/></svg>
</button>
</div>
<div class="search-dropdown" id="searchDropdown"></div>
</div>
</header>
<!-- Vehicle selector bar -->
<div class="vehicle-selector" id="vehicleSelector">
<div class="vehicle-selector__inner">
<div class="vs-group">
<label class="vs-label">Año</label>
<select class="vs-select" id="vsYear" onchange="CatalogApp.vsYearChanged()">
<option value="">Seleccionar...</option>
</select>
</div>
<div class="vs-arrow"></div>
<div class="vs-group">
<label class="vs-label">Marca</label>
<select class="vs-select" id="vsBrand" disabled onchange="CatalogApp.vsBrandChanged()">
<option value="">Seleccionar...</option>
</select>
</div>
<div class="vs-arrow"></div>
<div class="vs-group">
<label class="vs-label">Modelo</label>
<select class="vs-select" id="vsModel" disabled onchange="CatalogApp.vsModelChanged()">
<option value="">Seleccionar...</option>
</select>
</div>
<div class="vs-arrow"></div>
<div class="vs-group">
<label class="vs-label">Motor</label>
<select class="vs-select" id="vsEngine" disabled onchange="CatalogApp.vsEngineChanged()">
<option value="">Seleccionar...</option>
</select>
</div>
<button class="vs-clear" id="vsClear" onclick="CatalogApp.vsClear()" title="Limpiar seleccion" style="display:none;"></button>
<span class="vs-vin-divider" style="color:var(--color-text-disabled);padding-bottom:6px;flex-shrink:0;">|</span>
<div class="vs-group" id="vinGroup" style="position:relative;">
<a class="vs-label" id="vinToggle" href="#" onclick="event.preventDefault();CatalogApp.toggleVin();" style="color:var(--color-primary);cursor:pointer;text-decoration:underline;white-space:nowrap;">Tienes el VIN?</a>
<div id="vinInputWrap" style="display:none;">
<div style="display:flex;gap:4px;">
<input type="text" class="vs-select" id="vinInput" placeholder="Ej: 1HGBH41JXMN109186" maxlength="17" style="text-transform:uppercase;font-family:var(--font-mono,monospace);letter-spacing:0.05em;flex:1;" />
<button class="btn btn-primary" id="vinDecodeBtn" onclick="CatalogApp.decodeVin()" style="height:auto;padding:var(--space-2) var(--space-3);font-size:var(--text-body-sm);">Decodificar</button>
</div>
<div id="vinStatus" style="font-size:var(--text-caption);margin-top:4px;color:var(--color-text-muted);display:none;"></div>
</div>
</div>
</div>
</div>
<!-- Scrollable page body -->
<div class="page-body" id="pageBody">
<!-- Level title + optional filter -->
<div style="display:flex; align-items:center; justify-content:space-between; gap:var(--space-4); flex-wrap:wrap;">
<h2 class="level-title" id="levelTitle">Selecciona una marca</h2>
<input type="text" class="level-filter" id="levelFilter" placeholder="Filtrar..." style="display:none;" />
</div>
<!-- Loading spinner -->
<div class="loading" id="loading"><div class="spinner"></div></div>
<!-- Empty state -->
<div class="empty-state" id="emptyState">
<div class="empty-state__title" id="emptyTitle">Sin resultados</div>
<div class="empty-state__subtitle" id="emptySubtitle">No se encontraron datos para este nivel.</div>
</div>
<!-- Navigation grid (brands, models, years, engines, categories, groups) -->
<div class="nav-grid" id="navGrid" role="list"></div>
<!-- Parts grid (only for parts level) -->
<div class="nav-grid nav-grid--parts" id="partsGrid" role="list" style="display:none;"></div>
<!-- Pagination -->
<nav class="pagination" id="pagination" aria-label="Paginacion"></nav>
</div>
</main>
</div>
<!-- Detail panel overlay -->
<div class="detail-overlay" id="detailOverlay"></div>
<!-- Detail panel (slide-in) -->
<aside class="detail-panel" id="detailPanel">
<div class="detail-header">
<h3>Detalle de parte</h3>
<button class="detail-close" id="detailClose" aria-label="Cerrar detalle">&#10005;</button>
</div>
<div class="detail-body" id="detailBody">
<!-- Populated by JS -->
</div>
<div class="detail-footer" id="detailFooter">
<div class="qty-row">
<button class="qty-btn" id="qtyMinus">-</button>
<span class="qty-display" id="qtyDisplay">1</span>
<button class="qty-btn" id="qtyPlus">+</button>
</div>
<button class="btn btn-primary" id="addToCartBtn" style="width:100%;">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/><path d="M1 1h4l2.68 13.39a2 2 0 002 1.61h9.72a2 2 0 002-1.61L23 6H6"/></svg>
Agregar al carrito
</button>
</div>
</aside>
<!-- Cart FAB -->
<button class="cart-fab" id="cartFab" aria-label="Abrir carrito">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="9" cy="21" r="1"/><circle cx="20" cy="21" r="1"/><path d="M1 1h4l2.68 13.39a2 2 0 002 1.61h9.72a2 2 0 002-1.61L23 6H6"/></svg>
<span class="cart-fab__badge" id="cartBadge">0</span>
</button>
<!-- Cart overlay -->
<div class="cart-overlay" id="cartOverlay"></div>
<!-- Cart sidebar -->
<aside class="cart-sidebar" id="cartSidebar">
<div class="cart-header">
<h3>Carrito</h3>
<button id="cartCloseBtn" style="background:none;border:none;cursor:pointer;font-size:1.4rem;color:var(--color-text-secondary);padding:var(--space-1);">&#10005;</button>
</div>
<div class="cart-items" id="cartItems"></div>
<div class="cart-empty" id="cartEmpty" style="display:none;padding:2rem;text-align:center;color:var(--color-text-muted);">Carrito vacio</div>
<div class="cart-footer">
<div class="cart-totals">
<div>Subtotal: <span id="cartSubtotal">$0.00</span></div>
<div>IVA 16%: <span id="cartTax">$0.00</span></div>
<div style="font-weight:bold;font-size:1.2em;">Total: <span id="cartTotal">$0.00</span></div>
</div>
<button id="checkoutBtn" class="btn btn-primary" style="width:100%;margin-top:var(--space-3);">Ir a cobrar &rarr;</button>
</div>
</aside>
<!-- 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;">
<span class="banner__text" id="offlineBannerText"><strong>Modo offline</strong> — Mostrando solo tu inventario local.</span>
<button class="banner__dismiss" onclick="document.getElementById('offlineBanner').style.display='none'" aria-label="Cerrar">&times;</button>
</div>
<script src="/pos/static/js/app-init.js"></script>
<script src="/pos/static/js/sidebar.js"></script>
<script src="/pos/static/js/catalog.js"></script>
<script src="/pos/static/js/offline-banner.js"></script>
<script src="/pos/static/js/chat.js"></script>
<script src="/pos/static/js/sync-engine.js"></script>
<script src="/pos/static/js/onboarding.js"></script>
<script>if('serviceWorker' in navigator){navigator.serviceWorker.register('/pos/sw.js',{scope:'/pos/'});}</script>
</body>
</html>