Files
Autoparts-DB/pos/templates/catalog.html
consultoria-as 76c9c2d2db fix(pos): sidebar unificado en todas las paginas
Nuevo sidebar.js compartido que reemplaza el sidebar de cada pagina
con una version consistente:
- Misma estructura y orden de links en todas las paginas
- Nombre y rol del empleado real (de POS_USER)
- Link activo resaltado segun pagina actual
- Boton de logout
- Responsive (colapsa a iconos en <768px)
- Elimina inconsistencias de estilo entre paginas

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

2146 lines
68 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>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Catálogo — Nexus Autoparts POS</title>
<link rel="stylesheet" href="/pos/static/css/tokens.css" />
<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,
[data-theme="modern"].body-shell {
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
========================================================================= */
.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);
}
/* =========================================================================
APP LAYOUT
========================================================================= */
.app-shell {
display: flex;
height: 100vh;
padding-top: 36px; /* theme bar height */
}
/* =========================================================================
SIDEBAR
========================================================================= */
.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);
}
[data-theme="industrial"] .sidebar {
border-right-color: var(--color-border);
}
/* ---- Brand ---- */
.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;
}
/* ---- Navigation ---- */
.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;
position: relative;
}
.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;
}
/* ---- User profile ---- */
.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);
}
.profile-btn {
background: none;
border: none;
color: var(--color-text-muted);
cursor: pointer;
padding: var(--space-1);
border-radius: var(--radius-sm);
transition: var(--transition-fast);
flex-shrink: 0;
}
.profile-btn:hover {
color: var(--color-text-primary);
background: var(--color-primary-muted);
}
/* =========================================================================
MAIN CONTENT AREA
========================================================================= */
.main-content {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
min-width: 0;
}
/* ---- Header bar ---- */
.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);
}
.breadcrumb__link {
color: var(--color-text-muted);
text-decoration: none;
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);
}
.icon-btn {
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
background: none;
border: 1px solid var(--color-border);
color: var(--color-text-secondary);
cursor: pointer;
transition: var(--transition-fast);
border-radius: var(--radius-md);
position: relative;
}
[data-theme="industrial"] .icon-btn {
border-radius: 0;
}
.icon-btn:hover {
border-color: var(--color-primary);
color: var(--color-primary);
}
.icon-btn .notif-dot {
position: absolute;
top: 6px;
right: 6px;
width: 7px;
height: 7px;
background: var(--color-error);
border-radius: var(--radius-full);
border: 1px solid var(--color-bg-elevated);
}
/* =========================================================================
SCROLLABLE 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-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);
}
/* =========================================================================
SEARCH SECTION
========================================================================= */
.search-panel {
background: var(--color-bg-elevated);
border: 1px solid var(--color-border);
padding: var(--space-5);
box-shadow: var(--shadow-sm);
}
[data-theme="industrial"] .search-panel {
clip-path: polygon(0 0, calc(100% - 20px) 0, 100% 20px, 100% 100%, 0 100%);
border-radius: 0;
}
[data-theme="modern"] .search-panel {
border-radius: var(--radius-lg);
}
.search-panel__title {
font-family: var(--font-heading);
font-weight: var(--heading-weight-primary);
font-size: var(--text-h5);
color: var(--color-text-primary);
letter-spacing: var(--tracking-wide);
text-transform: uppercase;
margin-bottom: var(--space-4);
display: flex;
align-items: center;
gap: var(--space-2);
}
[data-theme="industrial"] .search-panel__title {
color: var(--color-primary);
}
.search-panel__title svg {
color: var(--color-primary);
}
.vehicle-search-row {
display: grid;
grid-template-columns: 1fr 1fr 1fr auto;
gap: var(--space-3);
align-items: end;
}
.form-group {
display: flex;
flex-direction: column;
gap: var(--space-1);
}
.form-label {
font-size: var(--text-caption);
font-weight: var(--font-weight-semibold);
color: var(--color-text-muted);
letter-spacing: var(--tracking-wider);
text-transform: uppercase;
}
.form-select,
.form-input {
width: 100%;
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;
transition: var(--transition-fast);
appearance: none;
-webkit-appearance: none;
height: 40px;
}
[data-theme="industrial"] .form-select,
[data-theme="industrial"] .form-input {
border-radius: 0;
}
[data-theme="modern"] .form-select,
[data-theme="modern"] .form-input {
border-radius: var(--radius-md);
}
.form-select:focus,
.form-input:focus {
border-color: var(--color-border-focus);
box-shadow: var(--shadow-focus);
}
.form-select {
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%23888888' stroke-width='2.5'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 10px center;
padding-right: var(--space-8);
cursor: pointer;
}
.form-select option {
background: var(--color-bg-overlay);
color: var(--color-text-primary);
}
/* Part search row */
.part-search-row {
display: grid;
grid-template-columns: 1fr auto;
gap: var(--space-3);
align-items: end;
margin-top: var(--space-3);
}
.form-input-with-icon {
position: relative;
}
.form-input-with-icon svg {
position: absolute;
left: 10px;
top: 50%;
transform: translateY(-50%);
color: var(--color-text-muted);
pointer-events: none;
}
.form-input-with-icon .form-input {
padding-left: var(--space-8);
}
/* ---- 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;
text-decoration: none;
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:active {
background: var(--btn-primary-bg-active);
}
.btn-secondary {
background: var(--btn-secondary-bg);
color: var(--btn-secondary-text);
border-color: var(--btn-secondary-border);
}
.btn-secondary:hover {
background: var(--btn-secondary-bg-hover);
}
.btn-ghost {
background: var(--btn-ghost-bg);
color: var(--btn-ghost-text);
border-color: var(--btn-ghost-border);
}
.btn-ghost:hover {
border-color: var(--color-primary);
color: var(--color-primary);
}
/* =========================================================================
RESULTS BAR
========================================================================= */
.results-bar {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
gap: var(--space-3);
}
.results-count {
font-family: var(--font-heading);
font-weight: var(--heading-weight-secondary);
font-size: var(--text-h5);
color: var(--color-text-primary);
letter-spacing: var(--tracking-snug);
}
.results-count strong {
color: var(--color-primary);
font-weight: var(--heading-weight-primary);
}
/* =========================================================================
FILTER CHIPS
========================================================================= */
.filters-row {
display: flex;
align-items: center;
gap: var(--space-2);
flex-wrap: wrap;
}
.filters-label {
font-size: var(--text-caption);
color: var(--color-text-muted);
font-weight: var(--font-weight-semibold);
letter-spacing: var(--tracking-wider);
text-transform: uppercase;
flex-shrink: 0;
}
.chip {
display: inline-flex;
align-items: center;
gap: var(--space-1);
padding: 4px var(--space-3);
border: 1px solid var(--color-border);
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);
user-select: none;
}
[data-theme="industrial"] .chip {
border-radius: 0;
letter-spacing: var(--tracking-wide);
text-transform: uppercase;
}
[data-theme="modern"] .chip {
border-radius: var(--radius-full);
}
.chip:hover {
border-color: var(--color-primary);
color: var(--color-primary);
background: var(--color-primary-muted);
}
.chip.is-active {
background: var(--color-primary);
border-color: var(--color-primary);
color: var(--color-text-inverse);
}
[data-theme="industrial"] .chip.is-active {
clip-path: polygon(0 0, calc(100% - 8px) 0, 100% 8px, 100% 100%, 0 100%);
}
/* =========================================================================
TOOLBAR (sort + view toggle)
========================================================================= */
.catalog-toolbar {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-3);
}
.sort-row {
display: flex;
align-items: center;
gap: var(--space-3);
}
.sort-label {
font-size: var(--text-caption);
color: var(--color-text-muted);
font-weight: var(--font-weight-semibold);
text-transform: uppercase;
letter-spacing: var(--tracking-wider);
}
.sort-select {
padding: 4px var(--space-7) 4px var(--space-3);
background: var(--color-bg-elevated);
border: 1px solid var(--color-border);
color: var(--color-text-secondary);
font-family: var(--font-body);
font-size: var(--text-caption);
outline: none;
cursor: pointer;
appearance: none;
-webkit-appearance: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='%23888888' stroke-width='2.5'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
background-repeat: no-repeat;
background-position: right 8px center;
transition: var(--transition-fast);
height: 32px;
}
[data-theme="industrial"] .sort-select {
border-radius: 0;
}
[data-theme="modern"] .sort-select {
border-radius: var(--radius-md);
}
.sort-select:focus {
border-color: var(--color-border-focus);
}
.view-toggle {
display: flex;
border: 1px solid var(--color-border);
overflow: hidden;
}
[data-theme="industrial"] .view-toggle {
border-radius: 0;
}
[data-theme="modern"] .view-toggle {
border-radius: var(--radius-md);
}
.view-btn {
width: 32px;
height: 32px;
display: flex;
align-items: center;
justify-content: center;
background: transparent;
border: none;
color: var(--color-text-muted);
cursor: pointer;
transition: var(--transition-fast);
border-right: 1px solid var(--color-border);
}
.view-btn:last-child {
border-right: none;
}
.view-btn:hover {
color: var(--color-primary);
background: var(--color-primary-muted);
}
.view-btn.is-active {
background: var(--color-primary);
color: var(--color-text-inverse);
}
/* =========================================================================
PRODUCT GRID
========================================================================= */
.product-grid {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--space-4);
}
.product-grid.view-list {
grid-template-columns: 1fr;
}
/* ---- Product Card ---- */
.product-card {
background: var(--color-bg-elevated);
border: 1px solid var(--color-border);
display: flex;
flex-direction: column;
transition: var(--transition-normal);
position: relative;
overflow: hidden;
}
[data-theme="industrial"] .product-card {
border-radius: 0;
clip-path: polygon(0 0, calc(100% - 16px) 0, 100% 16px, 100% 100%, 0 100%);
}
[data-theme="modern"] .product-card {
border-radius: var(--radius-lg);
}
.product-card:hover {
border-color: var(--color-primary);
box-shadow: var(--shadow-md);
transform: translateY(-2px);
}
[data-theme="industrial"] .product-card:hover {
box-shadow: 0 4px 16px rgba(245, 166, 35, 0.15), var(--shadow-md);
}
/* List view card overrides */
.view-list .product-card {
flex-direction: row;
align-items: center;
}
[data-theme="industrial"] .view-list .product-card {
clip-path: polygon(0 0, calc(100% - 12px) 0, 100% 12px, 100% 100%, 0 100%);
}
.view-list .product-card__image {
width: 100px;
min-width: 100px;
height: 80px;
}
.view-list .product-card__body {
flex: 1;
display: grid;
grid-template-columns: 1fr auto;
align-items: center;
gap: var(--space-4);
}
.view-list .product-card__pricing {
text-align: right;
flex-shrink: 0;
}
.view-list .product-card__compatibility {
grid-column: 1 / -1;
}
/* Card image placeholder */
.product-card__image {
height: 140px;
background: var(--color-bg-overlay);
display: flex;
align-items: center;
justify-content: center;
border-bottom: 1px solid var(--color-border);
flex-shrink: 0;
position: relative;
overflow: hidden;
}
[data-theme="industrial"] .product-card__image {
background: var(--color-surface-2);
}
.product-card__image svg {
color: var(--color-text-disabled);
opacity: 0.5;
}
.product-card__image-label {
position: absolute;
bottom: var(--space-2);
left: var(--space-2);
font-size: 10px;
font-family: var(--font-mono);
color: var(--color-text-muted);
letter-spacing: var(--tracking-wider);
text-transform: uppercase;
background: var(--color-bg-elevated);
padding: 1px var(--space-2);
}
[data-theme="industrial"] .product-card__image-label {
border-radius: 0;
border-left: 2px solid var(--color-primary);
}
[data-theme="modern"] .product-card__image-label {
border-radius: var(--radius-sm);
}
/* Card stock badge (on image) */
.stock-badge {
position: absolute;
top: var(--space-2);
right: var(--space-2);
display: flex;
align-items: center;
gap: 4px;
padding: 3px var(--space-2);
font-size: 10px;
font-weight: var(--font-weight-bold);
letter-spacing: var(--tracking-wider);
text-transform: uppercase;
}
[data-theme="industrial"] .stock-badge {
border-radius: 0;
}
[data-theme="modern"] .stock-badge {
border-radius: var(--radius-full);
}
.stock-badge::before {
content: '';
width: 6px;
height: 6px;
border-radius: var(--radius-full);
flex-shrink: 0;
}
.stock-badge.stock-ok {
background: rgba(34, 197, 94, 0.15);
color: var(--color-success);
border: 1px solid rgba(34, 197, 94, 0.3);
}
.stock-badge.stock-ok::before { background: var(--color-success); }
.stock-badge.stock-low {
background: rgba(234, 179, 8, 0.15);
color: var(--color-warning);
border: 1px solid rgba(234, 179, 8, 0.3);
}
.stock-badge.stock-low::before { background: var(--color-warning); }
.stock-badge.stock-out {
background: rgba(239, 68, 68, 0.15);
color: var(--color-error);
border: 1px solid rgba(239, 68, 68, 0.3);
}
.stock-badge.stock-out::before { background: var(--color-error); }
/* Card body */
.product-card__body {
padding: var(--space-4);
flex: 1;
display: flex;
flex-direction: column;
gap: var(--space-2);
}
.product-card__category {
font-size: 10px;
font-weight: var(--font-weight-bold);
letter-spacing: var(--tracking-widest);
text-transform: uppercase;
color: var(--color-primary);
}
.product-card__name {
font-family: var(--font-heading);
font-weight: var(--heading-weight-secondary);
font-size: 0.9375rem;
color: var(--color-text-primary);
line-height: 1.3;
letter-spacing: var(--tracking-snug);
}
[data-theme="industrial"] .product-card__name {
text-transform: uppercase;
}
.product-card__oem {
font-family: var(--font-mono);
font-size: var(--text-caption);
color: var(--color-text-muted);
letter-spacing: var(--tracking-wide);
}
.product-card__brand {
font-size: var(--text-caption);
color: var(--color-text-secondary);
display: flex;
align-items: center;
gap: var(--space-1);
}
.product-card__brand-dot {
width: 3px;
height: 3px;
border-radius: var(--radius-full);
background: var(--color-text-disabled);
flex-shrink: 0;
}
.product-card__compatibility {
display: flex;
align-items: center;
gap: var(--space-1);
padding: var(--space-1) var(--space-2);
background: var(--color-primary-muted);
border-left: 2px solid var(--color-primary);
font-size: 10px;
font-family: var(--font-body);
color: var(--color-text-accent);
}
[data-theme="modern"] .product-card__compatibility {
border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
}
/* Card footer */
.product-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;
gap: var(--space-3);
flex-shrink: 0;
}
.product-card__pricing {}
.product-card__price {
font-family: var(--font-mono);
font-weight: var(--font-weight-bold);
font-size: 1.1875rem;
color: var(--color-text-primary);
line-height: 1;
}
[data-theme="industrial"] .product-card__price {
color: var(--color-primary);
}
.product-card__price-unit {
font-size: var(--text-caption);
color: var(--color-text-muted);
margin-top: 2px;
}
/* Add button */
.btn-add {
display: inline-flex;
align-items: center;
gap: var(--space-1);
padding: var(--space-1) var(--space-3);
background: var(--btn-primary-bg);
color: var(--btn-primary-text);
font-family: var(--font-body);
font-size: var(--text-caption);
font-weight: var(--font-weight-bold);
border: 1px solid transparent;
cursor: pointer;
transition: var(--transition-fast);
letter-spacing: var(--tracking-wide);
white-space: nowrap;
height: 32px;
}
[data-theme="industrial"] .btn-add {
border-radius: 0;
clip-path: polygon(0 0, calc(100% - 8px) 0, 100% 8px, 100% 100%, 0 100%);
text-transform: uppercase;
}
[data-theme="modern"] .btn-add {
border-radius: var(--radius-md);
}
.btn-add:hover {
background: var(--btn-primary-bg-hover);
}
.btn-add:active {
background: var(--btn-primary-bg-active);
}
/* =========================================================================
PAGINATION
========================================================================= */
.pagination {
display: flex;
align-items: center;
justify-content: center;
gap: var(--space-1);
padding: var(--space-4) 0;
}
.page-item {
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
background: var(--color-bg-elevated);
border: 1px solid var(--color-border);
color: var(--color-text-secondary);
font-family: var(--font-body);
font-size: var(--text-body-sm);
font-weight: var(--font-weight-semibold);
cursor: pointer;
transition: var(--transition-fast);
user-select: none;
}
[data-theme="industrial"] .page-item {
border-radius: 0;
font-family: var(--font-mono);
}
[data-theme="modern"] .page-item {
border-radius: var(--radius-md);
}
.page-item:hover:not(.is-active):not(.is-disabled) {
border-color: var(--color-primary);
color: var(--color-primary);
background: var(--color-primary-muted);
}
.page-item.is-active {
background: var(--color-primary);
border-color: var(--color-primary);
color: var(--color-text-inverse);
}
[data-theme="industrial"] .page-item.is-active {
clip-path: polygon(0 0, calc(100% - 8px) 0, 100% 8px, 100% 100%, 0 100%);
}
.page-item.is-disabled {
opacity: 0.4;
cursor: not-allowed;
}
.page-item--wide {
width: auto;
padding: 0 var(--space-3);
gap: var(--space-1);
}
.page-ellipsis {
width: 36px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
color: var(--color-text-muted);
font-size: var(--text-body-sm);
user-select: none;
}
/* =========================================================================
SCROLLBAR
========================================================================= */
.page-body::-webkit-scrollbar,
.sidebar::-webkit-scrollbar {
width: 6px;
}
.page-body::-webkit-scrollbar-track,
.sidebar::-webkit-scrollbar-track {
background: var(--scrollbar-track);
}
.page-body::-webkit-scrollbar-thumb,
.sidebar::-webkit-scrollbar-thumb {
background-color: var(--scrollbar-thumb);
border-radius: var(--radius-full);
}
.page-body::-webkit-scrollbar-thumb:hover,
.sidebar::-webkit-scrollbar-thumb:hover {
background-color: var(--scrollbar-thumb-hover);
}
/* =========================================================================
INDUSTRIAL THEME — DECORATIVE ACCENTS
========================================================================= */
[data-theme="industrial"] .sidebar__brand::after {
display: none; /* removed, using clip already */
}
[data-theme="industrial"] .content-header {
border-bottom-color: var(--color-border);
position: relative;
}
[data-theme="industrial"] .content-header::after {
content: '';
position: absolute;
bottom: 0;
left: 0;
width: 80px;
height: 2px;
background: var(--color-primary);
}
/* =========================================================================
SIDEBAR TOGGLE (mobile)
========================================================================= */
.sidebar-toggle {
display: none;
width: 36px;
height: 36px;
align-items: center;
justify-content: center;
background: none;
border: 1px solid var(--color-border);
color: var(--color-text-secondary);
cursor: pointer;
transition: var(--transition-fast);
border-radius: var(--radius-md);
}
[data-theme="industrial"] .sidebar-toggle {
border-radius: 0;
}
/* =========================================================================
RESPONSIVE
========================================================================= */
@media (max-width: 1024px) {
.product-grid:not(.view-list) {
grid-template-columns: repeat(2, 1fr);
}
.vehicle-search-row {
grid-template-columns: 1fr 1fr;
}
.vehicle-search-row .form-group:last-child {
grid-column: 1 / -1;
}
}
@media (max-width: 768px) {
.sidebar {
position: fixed;
top: 36px;
left: 0;
bottom: 0;
z-index: var(--z-dropdown);
transform: translateX(-100%);
transition: transform var(--duration-normal) var(--ease-in-out);
}
.sidebar.is-open {
transform: translateX(0);
box-shadow: var(--shadow-xl);
}
.sidebar-toggle {
display: flex;
}
.product-grid:not(.view-list) {
grid-template-columns: repeat(2, 1fr);
}
.vehicle-search-row {
grid-template-columns: 1fr;
}
.vehicle-search-row .form-group:last-child {
grid-column: auto;
}
.part-search-row {
grid-template-columns: 1fr;
}
.catalog-toolbar {
flex-direction: column;
align-items: flex-start;
}
.results-bar {
flex-direction: column;
align-items: flex-start;
}
}
@media (max-width: 480px) {
.product-grid:not(.view-list) {
grid-template-columns: 1fr;
}
.page-body {
padding: var(--space-4);
}
}
/* =========================================================================
OVERLAY (mobile sidebar backdrop)
========================================================================= */
.sidebar-overlay {
display: none;
position: fixed;
inset: 0;
top: 36px;
background: var(--overlay-backdrop);
z-index: calc(var(--z-dropdown) - 1);
backdrop-filter: blur(2px);
}
.sidebar-overlay.is-open {
display: block;
}
/* =========================================================================
EMPTY STATE COMPONENT
========================================================================= */
.empty-state {
display: none;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
max-width: 320px;
padding: var(--space-8) var(--space-4);
margin: var(--space-10) auto;
width: 100%;
}
.empty-state.is-visible {
display: flex;
}
.empty-state__icon {
font-size: 48px;
line-height: 1;
margin-bottom: var(--space-4);
opacity: 0.6;
filter: grayscale(30%);
}
.empty-state__title {
font-family: var(--font-heading);
font-size: var(--text-h5);
font-weight: var(--heading-weight-secondary);
color: var(--color-text-primary);
margin-bottom: var(--space-2);
}
.empty-state__subtitle {
font-size: var(--text-body-sm);
color: var(--color-text-muted);
line-height: 1.5;
margin-bottom: var(--space-5);
}
.empty-state__action {
padding: var(--space-2) var(--space-5);
font-family: var(--font-body);
font-size: var(--text-body-sm);
font-weight: 500;
border-radius: var(--radius-md);
cursor: pointer;
transition: var(--transition-fast);
border: 1px solid var(--btn-secondary-border);
background: var(--btn-secondary-bg);
color: var(--btn-secondary-text);
}
.empty-state__action:hover {
background: var(--btn-secondary-bg-hover);
box-shadow: var(--shadow-sm);
}
/* =========================================================================
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; }
/* =========================================================================
CART SIDEBAR
========================================================================= */
.cart-fab {
position: fixed;
bottom: var(--space-6);
right: var(--space-6);
z-index: var(--z-modal);
width: 56px;
height: 56px;
border-radius: var(--radius-full);
background: var(--color-primary);
color: var(--color-text-inverse);
border: none;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
box-shadow: var(--shadow-lg);
transition: var(--transition-fast);
}
.cart-fab:hover {
transform: scale(1.08);
box-shadow: var(--shadow-xl);
}
.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, #ef4444);
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;
}
</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">
<!-- gear icon -->
<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">
<!-- sparkle icon -->
<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="Menú principal">
<!-- Brand -->
<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 -->
<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" aria-hidden="true"><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" aria-hidden="true"><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" aria-hidden="true"><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
<span class="nav-item__badge">12</span>
</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" aria-hidden="true"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M8 7h8M8 12h8M8 17h5"/></svg>
Catálogo
</a>
<div class="nav-section-label" style="margin-top: var(--space-2);">Gestión</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" aria-hidden="true"><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" aria-hidden="true"><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"/><polyline points="10,9 9,9 8,9"/></svg>
Facturación
</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" aria-hidden="true"><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" aria-hidden="true"><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" aria-hidden="true"><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>
Configuración
</a>
</nav>
<!-- User profile -->
<div class="sidebar__profile">
<div class="profile-avatar" aria-hidden="true">JR</div>
<div class="profile-info">
<div class="profile-info__name">José Ramírez</div>
<div class="profile-info__role">Administrador</div>
</div>
<button class="profile-btn" aria-label="Opciones de usuario" title="Opciones de usuario">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="12" cy="12" r="1"/><circle cx="12" cy="5" r="1"/><circle cx="12" cy="19" r="1"/></svg>
</button>
</div>
</aside>
<!-- =====================================================================
MAIN CONTENT
===================================================================== -->
<main class="main-content" id="mainContent">
<!-- Header bar -->
<header class="content-header">
<div style="display:flex;align-items:center;gap:var(--space-3);">
<button class="sidebar-toggle" id="sidebarToggle" aria-label="Abrir menú" aria-expanded="false" aria-controls="sidebar">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>
</button>
<nav class="breadcrumb" aria-label="Breadcrumb">
<a class="breadcrumb__link" href="#">Inicio</a>
<span class="breadcrumb__sep" aria-hidden="true"></span>
<span class="breadcrumb__current" aria-current="page">Catálogo</span>
</nav>
</div>
<div class="header-actions">
<button class="icon-btn" aria-label="Notificaciones">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M18 8A6 6 0 006 8c0 7-3 9-3 9h18s-3-2-3-9"/><path d="M13.73 21a2 2 0 01-3.46 0"/></svg>
<span class="notif-dot" aria-label="3 notificaciones pendientes"></span>
</button>
<button class="icon-btn" aria-label="Ayuda">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 015.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
</button>
</div>
</header>
<!-- ===================================================================
PAGE BODY
=================================================================== -->
<div class="page-body themed-scrollbar" id="pageBody">
<!-- Search panel -->
<section class="search-panel" aria-label="Búsqueda por vehículo">
<div class="search-panel__title">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/></svg>
Buscar por Vehículo
</div>
<!-- Vehicle dropdowns -->
<div class="vehicle-search-row">
<div class="form-group">
<label class="form-label" for="sel-make">Marca / Make</label>
<select class="form-select" id="sel-make" aria-label="Seleccionar marca">
<option value="">— Seleccionar marca —</option>
<option value="toyota" selected>Toyota</option>
<option value="honda">Honda</option>
<option value="nissan">Nissan</option>
<option value="chevrolet">Chevrolet</option>
<option value="ford">Ford</option>
<option value="volkswagen">Volkswagen</option>
<option value="mazda">Mazda</option>
<option value="hyundai">Hyundai</option>
<option value="kia">Kia</option>
<option value="bmw">BMW</option>
<option value="mercedes">Mercedes-Benz</option>
</select>
</div>
<div class="form-group">
<label class="form-label" for="sel-model">Modelo / Model</label>
<select class="form-select" id="sel-model" aria-label="Seleccionar modelo">
<option value="">— Seleccionar modelo —</option>
<option value="corolla" selected>Corolla</option>
<option value="camry">Camry</option>
<option value="hilux">Hilux</option>
<option value="rav4">RAV4</option>
<option value="yaris">Yaris</option>
<option value="prius">Prius</option>
<option value="4runner">4Runner</option>
<option value="tacoma">Tacoma</option>
</select>
</div>
<div class="form-group">
<label class="form-label" for="sel-year">Año / Year</label>
<select class="form-select" id="sel-year" aria-label="Seleccionar año">
<option value="">— Año —</option>
<option value="2024">2024</option>
<option value="2023">2023</option>
<option value="2022">2022</option>
<option value="2021">2021</option>
<option value="2020">2020</option>
<option value="2019">2019</option>
<option value="2018" selected>2018</option>
<option value="2017">2017</option>
<option value="2016">2016</option>
<option value="2015">2015</option>
</select>
</div>
<div class="form-group">
<label class="form-label" style="opacity:0" aria-hidden="true">Buscar</label>
<button class="btn btn-primary" id="vehicleSearchBtn">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" aria-hidden="true"><circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/></svg>
Buscar
</button>
</div>
</div>
<!-- Part search -->
<div class="part-search-row">
<div class="form-group">
<label class="form-label" for="inp-part">Número OEM o nombre de pieza</label>
<div class="form-input-with-icon">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><circle cx="11" cy="11" r="8"/><path d="M21 21l-4.35-4.35"/></svg>
<input
class="form-input"
id="inp-part"
type="text"
placeholder="Ej: 04465-02260, pastillas de freno, sensor de oxígeno..."
aria-label="Buscar por número OEM o nombre de pieza"
/>
</div>
</div>
<div class="form-group">
<label class="form-label" style="opacity:0" aria-hidden="true">Buscar parte</label>
<button class="btn btn-secondary" id="partSearchBtn">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><path d="M12 5v14M5 12h14"/></svg>
Búsqueda Avanzada
</button>
</div>
</div>
</section>
<!-- Results bar -->
<div class="results-bar">
<div class="results-count">
<strong>1,247</strong> partes encontradas
<span style="font-size:var(--text-body-sm);font-weight:var(--font-weight-regular);color:var(--color-text-muted);margin-left:var(--space-2);">para Toyota Corolla 2018</span>
</div>
<div class="filters-row">
<span class="filters-label">Filtrar:</span>
<button class="chip is-active" data-chip="frenos" aria-pressed="true">Frenos</button>
<button class="chip" data-chip="motor" aria-pressed="false">Motor</button>
<button class="chip" data-chip="suspension" aria-pressed="false">Suspensión</button>
<button class="chip" data-chip="electrico" aria-pressed="false">Eléctrico</button>
<button class="chip" data-chip="transmision" aria-pressed="false">Transmisión</button>
<button class="chip" data-chip="escape" aria-pressed="false">Escape</button>
<button class="chip" data-chip="carroceria" aria-pressed="false">Carrocería</button>
<button class="chip" data-chip="enfriamiento" aria-pressed="false">Enfriamiento</button>
</div>
</div>
<!-- Catalog toolbar -->
<div class="catalog-toolbar">
<div class="sort-row">
<span class="sort-label">Ordenar por:</span>
<select class="sort-select" aria-label="Ordenar resultados">
<option>Relevancia</option>
<option>Precio: Menor a Mayor</option>
<option>Precio: Mayor a Menor</option>
<option>Nombre AZ</option>
<option>Marca</option>
<option>Disponibilidad</option>
</select>
</div>
<div role="group" aria-label="Cambiar vista" class="view-toggle">
<button class="view-btn is-active" id="btnViewGrid" title="Vista en cuadrícula" aria-pressed="true">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><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>
</button>
<button class="view-btn" id="btnViewList" title="Vista en lista" aria-pressed="false">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/></svg>
</button>
</div>
</div>
<!-- Product grid — populated dynamically by catalog.js -->
<div class="product-grid" id="productGrid" role="list" aria-label="Resultados del catálogo">
<!-- Cards loaded from API -->
</div>
<!-- /product-grid -->
<!-- Empty State (shown when no results) -->
<div class="empty-state" id="emptyState">
<div class="empty-state__icon">&#128270;</div>
<div class="empty-state__title" id="emptyStateTitle">No se encontraron productos</div>
<div class="empty-state__subtitle" id="emptyStateSubtitle">Intenta con otro termino de busqueda o verifica el numero de parte</div>
<button class="empty-state__action" onclick="document.querySelector('.search-box input').value=''; document.querySelector('.search-box input').focus();">Limpiar filtros</button>
</div>
<!-- Pagination -->
<nav class="pagination" aria-label="Paginación de resultados">
<button class="page-item page-item--wide is-disabled" aria-label="Página anterior" disabled>
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><polyline points="15,18 9,12 15,6"/></svg>
Anterior
</button>
<button class="page-item is-active" aria-label="Página 1, actual" aria-current="page">1</button>
<button class="page-item" aria-label="Ir a página 2">2</button>
<button class="page-item" aria-label="Ir a página 3">3</button>
<span class="page-ellipsis" aria-hidden="true"></span>
<button class="page-item" aria-label="Ir a página 41">41</button>
<button class="page-item" aria-label="Ir a página 42">42</button>
<button class="page-item page-item--wide" aria-label="Ir a página siguiente">
Siguiente
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><polyline points="9,18 15,12 9,6"/></svg>
</button>
</nav>
</div>
<!-- /page-body -->
</main>
<!-- /main-content -->
</div>
<!-- /app-shell -->
<!-- =========================================================================
JAVASCRIPT
========================================================================= -->
<script>
'use strict';
/* =========================================================================
THEME SWITCHING
========================================================================= */
const html = document.documentElement;
const themeButtons = document.querySelectorAll('[data-theme-switch]');
function applyTheme(theme) {
html.setAttribute('data-theme', theme);
themeButtons.forEach(btn => {
const isActive = btn.dataset.themeSwitch === theme;
btn.classList.toggle('is-active', isActive);
btn.setAttribute('aria-pressed', String(isActive));
});
try {
localStorage.setItem('nexus-theme', theme);
} catch (_) { /* ignore */ }
}
themeButtons.forEach(btn => {
btn.addEventListener('click', () => applyTheme(btn.dataset.themeSwitch));
});
// Restore saved theme
(function () {
let saved;
try { saved = localStorage.getItem('nexus-theme'); } catch (_) { }
if (saved === 'industrial' || saved === 'modern') applyTheme(saved);
})();
/* =========================================================================
FILTER CHIP TOGGLING
========================================================================= */
const chips = document.querySelectorAll('[data-chip]');
chips.forEach(chip => {
chip.addEventListener('click', () => {
const isActive = chip.classList.toggle('is-active');
chip.setAttribute('aria-pressed', String(isActive));
});
});
/* =========================================================================
VIEW TOGGLE (Grid / List)
========================================================================= */
const productGrid = document.getElementById('productGrid');
const btnViewGrid = document.getElementById('btnViewGrid');
const btnViewList = document.getElementById('btnViewList');
function setView(view) {
const isGrid = view === 'grid';
productGrid.classList.toggle('view-list', !isGrid);
btnViewGrid.classList.toggle('is-active', isGrid);
btnViewList.classList.toggle('is-active', !isGrid);
btnViewGrid.setAttribute('aria-pressed', String(isGrid));
btnViewList.setAttribute('aria-pressed', String(!isGrid));
try {
localStorage.setItem('nexus-catalog-view', view);
} catch (_) { /* ignore */ }
}
btnViewGrid.addEventListener('click', () => setView('grid'));
btnViewList.addEventListener('click', () => setView('list'));
// Restore saved view
(function () {
let saved;
try { saved = localStorage.getItem('nexus-catalog-view'); } catch (_) { }
if (saved === 'list') setView('list');
})();
/* =========================================================================
MOBILE SIDEBAR TOGGLE
========================================================================= */
const sidebar = document.getElementById('sidebar');
const sidebarOverlay = document.getElementById('sidebarOverlay');
const sidebarToggle = document.getElementById('sidebarToggle');
function openSidebar() {
sidebar.classList.add('is-open');
sidebarOverlay.classList.add('is-open');
sidebarToggle.setAttribute('aria-expanded', 'true');
document.body.style.overflow = 'hidden';
}
function closeSidebar() {
sidebar.classList.remove('is-open');
sidebarOverlay.classList.remove('is-open');
sidebarToggle.setAttribute('aria-expanded', 'false');
document.body.style.overflow = '';
}
sidebarToggle.addEventListener('click', () => {
if (sidebar.classList.contains('is-open')) closeSidebar();
else openSidebar();
});
sidebarOverlay.addEventListener('click', closeSidebar);
// Close sidebar on Escape
document.addEventListener('keydown', e => {
if (e.key === 'Escape' && sidebar.classList.contains('is-open')) closeSidebar();
});
// Close sidebar if viewport grows beyond mobile breakpoint
const mq = window.matchMedia('(min-width: 769px)');
mq.addEventListener('change', e => {
if (e.matches) closeSidebar();
});
/* =========================================================================
PAGINATION INTERACTION
========================================================================= */
const pageItems = document.querySelectorAll('.page-item:not(.is-disabled):not([disabled])');
pageItems.forEach(item => {
if (item.id === 'paginationPrev' || item.id === 'paginationNext') return;
item.addEventListener('click', function () {
// Remove active from all numbered items (not prev/next)
document.querySelectorAll('.page-item:not(.page-item--wide)').forEach(p => {
p.classList.remove('is-active');
p.removeAttribute('aria-current');
});
this.classList.add('is-active');
this.setAttribute('aria-current', 'page');
// Scroll back to top of results
document.getElementById('pageBody').scrollTo({ top: 0, behavior: 'smooth' });
});
});
/* =========================================================================
SEARCH BUTTON FEEDBACK
========================================================================= */
document.getElementById('vehicleSearchBtn').addEventListener('click', function () {
const label = this.querySelector('svg') ? this : this;
const originalText = this.textContent.trim();
this.disabled = true;
this.style.opacity = '0.7';
setTimeout(() => {
this.disabled = false;
this.style.opacity = '';
}, 800);
});
</script>
<!-- Cart FAB (floating action button) -->
<button class="cart-fab" id="cartFab" onclick="CatalogApp.toggleCart()" aria-label="Abrir carrito">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" aria-hidden="true"><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" onclick="CatalogApp.toggleCart()"></div>
<!-- Cart sidebar -->
<aside class="cart-sidebar" id="cartSidebar">
<div class="cart-header">
<h3>Carrito</h3>
<button onclick="CatalogApp.toggleCart()" class="btn btn-ghost" aria-label="Cerrar carrito" 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);padding:var(--space-3);font-size:var(--text-body);font-weight:var(--font-weight-semibold);cursor:pointer;" onclick="CatalogApp.goToCheckout()">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;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>
<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>
</body>
</html>