Files
Autoparts-DB/pos/templates/inventory.html
consultoria-as 04340f2f29 fix(pos): remove hardcoded demo data from inventory, customers, invoicing, accounting pages
Demo/sample rows in HTML tables were visible on first render before JS
populated real data. Replaced all hardcoded tbody rows and finance cards
with empty containers that JS populates from API on load.

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

2107 lines
71 KiB
HTML

<!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>Inventario — Nexus Autoparts POS</title>
<link rel="stylesheet" href="/pos/static/css/tokens.css" />
<link rel="manifest" href="/pos/static/pwa/manifest.json" />
<meta name="theme-color" content="#F5A623" />
<style>
/* =========================================================================
BASE RESET & 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-sm);
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-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: space-between;
padding: 0 var(--space-5);
background: var(--color-bg-overlay);
border-bottom: 1px solid var(--color-border);
backdrop-filter: blur(10px);
height: 36px;
}
[data-theme="industrial"] .theme-bar {
background: rgba(13, 13, 13, 0.95);
border-bottom-color: var(--color-primary-muted);
}
.theme-bar__left {
display: flex;
align-items: center;
gap: var(--space-3);
}
.theme-bar__store {
display: flex;
align-items: center;
gap: var(--space-2);
font-family: var(--font-heading);
font-weight: var(--heading-weight-primary);
font-size: 0.75rem;
letter-spacing: var(--tracking-widest);
text-transform: uppercase;
color: var(--color-text-accent);
}
.theme-bar__dot {
width: 6px;
height: 6px;
background: var(--color-success);
border-radius: var(--radius-full);
box-shadow: 0 0 6px var(--color-success);
animation: blink 2.5s ease-in-out infinite;
}
@keyframes blink {
0%, 100% { opacity: 1; }
50% { opacity: 0.35; }
}
.theme-bar__sep {
width: 1px;
height: 16px;
background: var(--color-border);
}
.theme-bar__label {
font-size: var(--text-caption);
color: var(--color-text-muted);
letter-spacing: var(--tracking-wide);
text-transform: uppercase;
}
.theme-bar__right {
display: flex;
align-items: center;
gap: var(--space-2);
}
.theme-btn {
display: inline-flex;
align-items: center;
gap: var(--space-1);
padding: 2px 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);
letter-spacing: var(--tracking-wide);
}
.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);
}
.theme-btn__swatch {
width: 8px;
height: 8px;
border-radius: var(--radius-full);
}
.theme-btn--industrial .theme-btn__swatch { background: #F5A623; }
.theme-btn--modern .theme-btn__swatch { background: #FF6B35; }
/* =========================================================================
APP LAYOUT
========================================================================= */
.app-shell {
display: flex;
height: 100vh;
padding-top: 36px;
}
/* =========================================================================
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;
scrollbar-width: thin;
scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
}
.sidebar::-webkit-scrollbar { width: 4px; }
.sidebar::-webkit-scrollbar-track { background: var(--scrollbar-track); }
.sidebar::-webkit-scrollbar-thumb { background: var(--scrollbar-thumb); border-radius: var(--radius-full); }
/* Brand */
.sidebar__brand {
display: flex;
align-items: center;
gap: var(--space-3);
padding: var(--space-4) var(--space-4) var(--space-3);
border-bottom: 1px solid var(--color-border);
flex-shrink: 0;
}
.brand-logo {
width: 36px;
height: 36px;
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.125rem;
letter-spacing: -0.04em;
flex-shrink: 0;
}
[data-theme="industrial"] .brand-logo {
clip-path: polygon(0 0, calc(100% - 9px) 0, 100% 9px, 100% 100%, 0 100%);
}
[data-theme="modern"] .brand-logo {
border-radius: var(--radius-md);
}
.brand-name__primary {
font-family: var(--font-heading);
font-weight: var(--heading-weight-primary);
font-size: 0.9375rem;
letter-spacing: var(--tracking-wide);
text-transform: uppercase;
color: var(--color-text-primary);
line-height: 1;
}
.brand-name__sub {
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-4) var(--space-1);
font-size: 0.6875rem;
font-weight: var(--font-weight-semibold);
letter-spacing: var(--tracking-widest);
text-transform: uppercase;
color: var(--color-text-muted);
}
.nav-item {
display: flex;
align-items: center;
gap: var(--space-3);
padding: var(--space-2) var(--space-4);
color: var(--color-text-secondary);
text-decoration: none;
font-size: var(--text-body-sm);
font-weight: var(--font-weight-regular);
border-left: 3px solid transparent;
transition: var(--transition-fast);
cursor: pointer;
}
.nav-item:hover {
background: var(--color-surface-2);
color: var(--color-text-primary);
}
.nav-item.is-active {
background: var(--color-primary-muted);
color: var(--color-primary);
border-left-color: var(--color-primary);
font-weight: var(--font-weight-semibold);
}
.nav-item__icon {
width: 18px;
height: 18px;
flex-shrink: 0;
opacity: 0.7;
}
.nav-item.is-active .nav-item__icon {
opacity: 1;
}
/* Sidebar footer */
.sidebar__footer {
padding: var(--space-3) var(--space-4);
border-top: 1px solid var(--color-border);
display: flex;
align-items: center;
gap: var(--space-2);
}
.sidebar__user-avatar {
width: 28px;
height: 28px;
border-radius: var(--radius-full);
background: var(--color-primary);
color: var(--color-text-inverse);
display: flex;
align-items: center;
justify-content: center;
font-size: var(--text-caption);
font-weight: var(--font-weight-bold);
flex-shrink: 0;
}
.sidebar__user-info { flex: 1; overflow: hidden; }
.sidebar__user-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;
}
.sidebar__user-role {
font-size: var(--text-caption);
color: var(--color-text-muted);
}
/* =========================================================================
MAIN CONTENT
========================================================================= */
.main {
flex: 1;
display: flex;
flex-direction: column;
overflow: hidden;
min-width: 0;
}
/* ---- Page Header ---- */
.page-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--space-4) var(--space-6);
background: var(--color-bg-elevated);
border-bottom: 1px solid var(--color-border);
flex-shrink: 0;
}
[data-theme="industrial"] .page-header {
background: var(--color-surface-1);
}
.page-header__title-group { display: flex; flex-direction: column; gap: 2px; }
.page-header__eyebrow {
font-size: var(--text-caption);
font-weight: var(--font-weight-semibold);
letter-spacing: var(--tracking-widest);
text-transform: uppercase;
color: var(--color-text-muted);
}
.page-header__title {
font-family: var(--font-heading);
font-weight: var(--heading-weight-primary);
font-size: var(--text-h4);
letter-spacing: var(--heading-tracking-h4);
color: var(--color-text-primary);
line-height: 1.2;
}
[data-theme="industrial"] .page-header__title {
text-transform: uppercase;
}
.page-header__actions {
display: flex;
align-items: center;
gap: var(--space-3);
}
/* ---- Summary Cards ---- */
.summary-strip {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: var(--space-4);
padding: var(--space-4) var(--space-6);
background: var(--color-bg-base);
border-bottom: 1px solid var(--color-border);
flex-shrink: 0;
}
[data-theme="modern"] .summary-strip {
background: transparent;
}
.summary-card {
background: var(--color-bg-elevated);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
padding: var(--space-4) var(--space-5);
display: flex;
align-items: flex-start;
gap: var(--space-3);
box-shadow: var(--shadow-sm);
transition: var(--transition-normal);
}
.summary-card:hover {
box-shadow: var(--shadow-md);
border-color: var(--color-border-strong);
}
[data-theme="industrial"] .summary-card {
border-left: 3px solid var(--color-primary);
}
[data-theme="modern"] .summary-card {
background: var(--color-bg-overlay);
}
.summary-card__icon {
width: 38px;
height: 38px;
border-radius: var(--radius-md);
background: var(--color-primary-muted);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.summary-card__icon svg {
width: 20px;
height: 20px;
stroke: var(--color-primary);
fill: none;
stroke-width: 1.75;
stroke-linecap: round;
stroke-linejoin: round;
}
.summary-card__icon--alert { background: rgba(239,68,68,.12); }
.summary-card__icon--alert svg { stroke: var(--color-error); }
.summary-card__icon--value { background: rgba(34,197,94,.12); }
.summary-card__icon--value svg { stroke: var(--color-success); }
.summary-card__icon--time { background: var(--color-primary-muted); }
.summary-card__icon--time svg { stroke: var(--color-primary); }
.summary-card__body { flex: 1; min-width: 0; }
.summary-card__label {
font-size: var(--text-caption);
font-weight: var(--font-weight-semibold);
letter-spacing: var(--tracking-wider);
text-transform: uppercase;
color: var(--color-text-muted);
margin-bottom: var(--space-1);
}
.summary-card__value {
font-family: var(--font-heading);
font-weight: var(--heading-weight-primary);
font-size: 1.5rem;
color: var(--color-text-primary);
line-height: 1.1;
}
.summary-card__sub {
font-size: var(--text-caption);
color: var(--color-text-muted);
margin-top: 2px;
}
.summary-card__delta {
font-size: var(--text-caption);
font-weight: var(--font-weight-semibold);
margin-top: 2px;
}
.summary-card__delta--up { color: var(--color-success); }
.summary-card__delta--down { color: var(--color-error); }
/* ---- Tabs Row ---- */
.tabs-row {
display: flex;
align-items: stretch;
gap: 0;
padding: 0 var(--space-6);
background: var(--color-bg-elevated);
border-bottom: 1px solid var(--color-border);
flex-shrink: 0;
overflow-x: auto;
scrollbar-width: none;
}
.tabs-row::-webkit-scrollbar { display: none; }
[data-theme="industrial"] .tabs-row {
background: var(--color-surface-1);
}
.tab-btn {
display: inline-flex;
align-items: center;
gap: var(--space-2);
padding: var(--space-3) var(--space-4);
border: none;
border-bottom: 2px solid transparent;
background: transparent;
color: var(--color-text-muted);
font-family: var(--font-body);
font-size: var(--text-body-sm);
font-weight: var(--font-weight-semibold);
cursor: pointer;
white-space: nowrap;
transition: var(--transition-fast);
letter-spacing: var(--tracking-wide);
text-transform: uppercase;
position: relative;
}
.tab-btn:hover {
color: var(--color-text-primary);
background: var(--color-surface-2);
}
.tab-btn.is-active {
color: var(--color-primary);
border-bottom-color: var(--color-primary);
}
.tab-btn__badge {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 18px;
height: 18px;
padding: 0 var(--space-1);
border-radius: var(--radius-full);
background: var(--color-primary-muted);
color: var(--color-primary);
font-size: 0.6875rem;
font-weight: var(--font-weight-bold);
}
.tab-btn.is-active .tab-btn__badge {
background: var(--color-primary);
color: var(--color-text-inverse);
}
.tab-btn__badge--alert {
background: rgba(239,68,68,.15);
color: var(--color-error);
}
.tab-btn.is-active .tab-btn__badge--alert {
background: var(--color-error);
color: #fff;
}
/* ---- Tab Panels ---- */
.tab-panels {
flex: 1;
overflow-y: auto;
scrollbar-width: thin;
scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
}
.tab-panels::-webkit-scrollbar { width: 6px; }
.tab-panels::-webkit-scrollbar-track { background: var(--scrollbar-track); }
.tab-panels::-webkit-scrollbar-thumb { background: var(--scrollbar-thumb); border-radius: var(--radius-full); }
.tab-panel {
display: none;
padding: var(--space-5) var(--space-6);
}
.tab-panel.is-active {
display: block;
}
/* =========================================================================
TOOLBAR (search, filters, actions)
========================================================================= */
.toolbar {
display: flex;
align-items: center;
gap: var(--space-3);
margin-bottom: var(--space-4);
flex-wrap: wrap;
}
.search-box {
display: flex;
align-items: center;
gap: var(--space-2);
background: var(--color-bg-elevated);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
padding: 0 var(--space-3);
height: 36px;
flex: 1;
min-width: 200px;
max-width: 360px;
transition: var(--transition-fast);
}
.search-box:focus-within {
border-color: var(--color-border-focus);
box-shadow: var(--shadow-accent);
}
.search-box svg {
width: 15px;
height: 15px;
stroke: var(--color-text-muted);
fill: none;
stroke-width: 2;
stroke-linecap: round;
flex-shrink: 0;
}
.search-box input {
flex: 1;
background: transparent;
border: none;
outline: none;
color: var(--color-text-primary);
font-family: var(--font-body);
font-size: var(--text-body-sm);
}
.search-box input::placeholder {
color: var(--color-text-muted);
}
.select-filter {
height: 36px;
padding: 0 var(--space-3);
background: var(--color-bg-elevated);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
color: var(--color-text-secondary);
font-family: var(--font-body);
font-size: var(--text-body-sm);
cursor: pointer;
outline: none;
transition: var(--transition-fast);
}
.select-filter:focus {
border-color: var(--color-border-focus);
box-shadow: var(--shadow-accent);
}
.toolbar__spacer { flex: 1; }
/* =========================================================================
BUTTONS
========================================================================= */
.btn {
display: inline-flex;
align-items: center;
gap: var(--space-2);
padding: 0 var(--space-4);
height: 36px;
border-radius: var(--radius-md);
font-family: var(--font-body);
font-size: var(--text-body-sm);
font-weight: var(--font-weight-semibold);
letter-spacing: var(--tracking-wide);
cursor: pointer;
border: 1px solid transparent;
transition: var(--transition-fast);
text-decoration: none;
white-space: nowrap;
}
.btn svg {
width: 15px;
height: 15px;
stroke: currentColor;
fill: none;
stroke-width: 2;
stroke-linecap: round;
stroke-linejoin: round;
flex-shrink: 0;
}
.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--secondary {
background: var(--btn-secondary-bg);
color: var(--btn-secondary-text);
border-color: var(--btn-secondary-border);
}
.btn--secondary:hover {
background: var(--btn-secondary-bg-hover);
}
.btn--ghost {
background: var(--btn-ghost-bg);
color: var(--btn-ghost-text);
border-color: var(--btn-ghost-border);
}
.btn--ghost:hover {
background: var(--color-surface-2);
border-color: var(--color-border-strong);
color: var(--color-text-primary);
}
.btn--sm {
height: 28px;
padding: 0 var(--space-3);
font-size: var(--text-caption);
}
/* =========================================================================
DATA TABLE
========================================================================= */
.table-wrapper {
background: var(--color-bg-elevated);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
overflow: hidden;
box-shadow: var(--shadow-sm);
}
[data-theme="modern"] .table-wrapper {
background: var(--color-bg-overlay);
}
.data-table {
width: 100%;
border-collapse: collapse;
font-size: var(--text-body-sm);
}
.data-table thead {
background: var(--color-surface-2);
border-bottom: 1px solid var(--color-border);
}
[data-theme="industrial"] .data-table thead {
background: var(--color-surface-3);
}
.data-table th {
padding: var(--space-3) var(--space-4);
text-align: left;
font-size: var(--text-caption);
font-weight: var(--font-weight-semibold);
letter-spacing: var(--tracking-wider);
text-transform: uppercase;
color: var(--color-text-muted);
white-space: nowrap;
}
.data-table th:first-child { padding-left: var(--space-5); }
.data-table th:last-child { padding-right: var(--space-5); }
.data-table tbody tr {
border-bottom: 1px solid var(--color-border);
transition: background var(--duration-fast) var(--ease-in-out);
}
.data-table tbody tr:last-child {
border-bottom: none;
}
.data-table tbody tr:hover {
background: var(--color-surface-2);
}
.data-table td {
padding: var(--space-3) var(--space-4);
color: var(--color-text-secondary);
vertical-align: middle;
}
.data-table td:first-child { padding-left: var(--space-5); }
.data-table td:last-child { padding-right: var(--space-5); }
.data-table .td--primary {
color: var(--color-text-primary);
font-weight: var(--font-weight-semibold);
}
.data-table .td--mono {
font-family: var(--font-mono);
font-size: 0.8125rem;
color: var(--color-text-accent);
}
.data-table .td--amount {
font-family: var(--font-mono);
font-size: 0.8125rem;
color: var(--color-text-primary);
font-weight: var(--font-weight-semibold);
}
/* =========================================================================
BADGES / STATUS PILLS
========================================================================= */
.badge {
display: inline-flex;
align-items: center;
gap: 4px;
padding: 2px var(--space-2);
border-radius: var(--radius-full);
font-size: 0.6875rem;
font-weight: var(--font-weight-bold);
letter-spacing: var(--tracking-wide);
text-transform: uppercase;
white-space: nowrap;
}
.badge::before {
content: '';
width: 5px;
height: 5px;
border-radius: var(--radius-full);
background: currentColor;
flex-shrink: 0;
}
.badge--ok {
background: rgba(34, 197, 94, 0.15);
color: var(--color-success);
}
.badge--low {
background: rgba(239, 68, 68, 0.15);
color: var(--color-error);
}
.badge--over {
background: rgba(234, 179, 8, 0.15);
color: var(--color-warning);
}
.badge--pending {
background: var(--color-primary-muted);
color: var(--color-primary);
}
.badge--complete {
background: rgba(34, 197, 94, 0.15);
color: var(--color-success);
}
.badge--transit {
background: rgba(99, 102, 241, 0.15);
color: #818cf8;
}
.badge--cancelled {
background: rgba(115, 115, 115, 0.15);
color: var(--color-text-muted);
}
.badge--damage {
background: rgba(239, 68, 68, 0.15);
color: var(--color-error);
}
.badge--shrinkage {
background: rgba(234, 179, 8, 0.15);
color: var(--color-warning);
}
.badge--correction {
background: var(--color-primary-muted);
color: var(--color-primary);
}
.badge--partial {
background: rgba(234, 179, 8, 0.15);
color: var(--color-warning);
}
/* =========================================================================
ALERT CARDS (Tab Alertas)
========================================================================= */
.alerts-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: var(--space-4);
}
.alert-card {
background: var(--color-bg-elevated);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
padding: var(--space-4) var(--space-5);
display: flex;
gap: var(--space-4);
box-shadow: var(--shadow-sm);
transition: var(--transition-normal);
position: relative;
overflow: hidden;
}
[data-theme="modern"] .alert-card {
background: var(--color-bg-overlay);
}
.alert-card::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 4px;
}
.alert-card--critical::before { background: var(--color-error); }
.alert-card--warning::before { background: var(--color-warning); }
.alert-card--info::before { background: #818cf8; }
.alert-card--accent::before { background: var(--color-primary); }
.alert-card:hover {
box-shadow: var(--shadow-md);
transform: translateY(-1px);
}
.alert-card__icon {
width: 40px;
height: 40px;
border-radius: var(--radius-md);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.alert-card__icon svg {
width: 22px;
height: 22px;
fill: none;
stroke-width: 1.75;
stroke-linecap: round;
stroke-linejoin: round;
}
.alert-card--critical .alert-card__icon { background: rgba(239,68,68,.15); }
.alert-card--critical .alert-card__icon svg { stroke: var(--color-error); }
.alert-card--warning .alert-card__icon { background: rgba(234,179,8,.15); }
.alert-card--warning .alert-card__icon svg { stroke: var(--color-warning); }
.alert-card--info .alert-card__icon { background: rgba(99,102,241,.15); }
.alert-card--info .alert-card__icon svg { stroke: #818cf8; }
.alert-card--accent .alert-card__icon { background: var(--color-primary-muted); }
.alert-card--accent .alert-card__icon svg { stroke: var(--color-primary); }
.alert-card__body { flex: 1; min-width: 0; }
.alert-card__title {
font-weight: var(--font-weight-semibold);
font-size: var(--text-body-sm);
color: var(--color-text-primary);
margin-bottom: 2px;
}
.alert-card__desc {
font-size: var(--text-caption);
color: var(--color-text-muted);
margin-bottom: var(--space-3);
line-height: var(--leading-caption);
}
.alert-card__meta {
display: flex;
align-items: center;
gap: var(--space-3);
flex-wrap: wrap;
}
.alert-card__count {
font-family: var(--font-heading);
font-weight: var(--heading-weight-primary);
font-size: 1.5rem;
line-height: 1;
}
.alert-card--critical .alert-card__count { color: var(--color-error); }
.alert-card--warning .alert-card__count { color: var(--color-warning); }
.alert-card--info .alert-card__count { color: #818cf8; }
.alert-card--accent .alert-card__count { color: var(--color-primary); }
.alert-card__count-label {
font-size: var(--text-caption);
color: var(--color-text-muted);
}
/* =========================================================================
ALERTS SECTION HEADER
========================================================================= */
.section-heading {
display: flex;
align-items: center;
gap: var(--space-3);
margin-bottom: var(--space-4);
}
.section-heading__title {
font-family: var(--font-heading);
font-weight: var(--heading-weight-secondary);
font-size: var(--text-h6);
letter-spacing: var(--heading-tracking-h6);
text-transform: uppercase;
color: var(--color-text-secondary);
}
.section-heading__line {
flex: 1;
height: 1px;
background: var(--color-border);
}
/* =========================================================================
TABLE FOOTER / PAGINATION ROW
========================================================================= */
.table-footer {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--space-3) var(--space-5);
border-top: 1px solid var(--color-border);
background: var(--color-surface-1);
font-size: var(--text-caption);
color: var(--color-text-muted);
}
.pagination {
display: flex;
align-items: center;
gap: var(--space-1);
}
.page-btn {
width: 28px;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
border: 1px solid var(--color-border);
border-radius: var(--radius-sm);
background: transparent;
color: var(--color-text-secondary);
font-size: var(--text-caption);
font-family: var(--font-body);
cursor: pointer;
transition: var(--transition-fast);
}
.page-btn:hover {
border-color: var(--color-primary);
color: var(--color-primary);
}
.page-btn.is-active {
background: var(--color-primary);
border-color: var(--color-primary);
color: var(--color-text-inverse);
font-weight: var(--font-weight-bold);
}
/* =========================================================================
RESPONSIVE
========================================================================= */
@media (max-width: 1024px) {
.summary-strip {
grid-template-columns: repeat(2, 1fr);
}
.sidebar {
width: 60px;
overflow: visible;
}
.brand-name, .nav-item span, .sidebar__user-info,
.nav-section-label { display: none; }
.nav-item {
justify-content: center;
padding: var(--space-3);
border-left: none;
border-bottom: 2px solid transparent;
}
.nav-item.is-active {
border-left: none;
border-bottom-color: var(--color-primary);
}
}
@media (max-width: 768px) {
.summary-strip {
grid-template-columns: repeat(2, 1fr);
padding: var(--space-3) var(--space-4);
gap: var(--space-3);
}
.page-header { padding: var(--space-3) var(--space-4); }
.tabs-row { padding: 0 var(--space-4); }
.tab-panel { padding: var(--space-4); }
.toolbar { gap: var(--space-2); }
.search-box { max-width: 100%; }
.page-header__actions { display: none; }
}
/* =====================================================================
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; }
/* =========================================================================
INVENTORY MODALS
========================================================================= */
.inv-modal-overlay {
display: none;
position: fixed;
inset: 0;
z-index: 9000;
background: rgba(0,0,0,0.6);
backdrop-filter: blur(4px);
align-items: center;
justify-content: center;
}
.inv-modal-overlay.is-open {
display: flex;
}
.inv-modal {
background: var(--color-bg-elevated);
border: 1px solid var(--color-border);
border-radius: var(--radius-lg);
width: 520px;
max-height: 85vh;
overflow-y: auto;
box-shadow: 0 20px 60px rgba(0,0,0,0.4);
}
.inv-modal--wide {
width: 700px;
}
.inv-modal__header {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--space-4) var(--space-5);
border-bottom: 1px solid var(--color-border);
}
.inv-modal__header h3 {
font-family: var(--font-heading);
font-weight: var(--heading-weight-primary);
font-size: var(--text-h5);
color: var(--color-text-primary);
margin: 0;
}
.inv-modal__close {
background: none;
border: none;
font-size: 1.5rem;
color: var(--color-text-muted);
cursor: pointer;
padding: 0 var(--space-1);
line-height: 1;
}
.inv-modal__close:hover {
color: var(--color-text-primary);
}
.inv-modal__body {
padding: var(--space-4) var(--space-5);
}
.inv-modal__footer {
display: flex;
justify-content: flex-end;
gap: var(--space-3);
padding: var(--space-3) var(--space-5);
border-top: 1px solid var(--color-border);
}
.inv-form-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: var(--space-3);
}
.inv-field {
display: flex;
flex-direction: column;
gap: var(--space-1);
}
.inv-field--full {
grid-column: 1 / -1;
}
.inv-field label {
font-size: var(--text-caption);
font-weight: var(--font-weight-semibold);
color: var(--color-text-muted);
letter-spacing: var(--tracking-wide);
text-transform: uppercase;
}
.inv-field input {
padding: var(--space-2) var(--space-3);
background: var(--color-surface-1);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
color: var(--color-text-primary);
font-family: var(--font-body);
font-size: var(--text-body-sm);
}
.inv-field input:focus {
outline: none;
border-color: var(--color-primary);
box-shadow: 0 0 0 2px var(--color-primary-muted);
}
.count-row {
display: flex;
gap: var(--space-2);
align-items: center;
margin-bottom: var(--space-2);
}
.count-row input {
padding: var(--space-2) var(--space-3);
background: var(--color-surface-1);
border: 1px solid var(--color-border);
border-radius: var(--radius-md);
color: var(--color-text-primary);
font-family: var(--font-body);
font-size: var(--text-body-sm);
}
/* History table inside modal */
.inv-modal .data-table { width: 100%; }
</style>
</head>
<body>
<!-- =========================================================================
THEME SWITCHER BAR
========================================================================= -->
<header class="theme-bar" role="banner">
<div class="theme-bar__left">
<div class="theme-bar__store">
<span class="theme-bar__dot"></span>
Nexus Autoparts
</div>
<div class="theme-bar__sep"></div>
<span class="theme-bar__label">Sucursal Centro &mdash; Usuario: H. García</span>
</div>
<div class="theme-bar__right">
<span class="theme-bar__label">Tema:</span>
<button class="theme-btn theme-btn--industrial is-active" data-theme-target="industrial" onclick="setTheme('industrial')">
<span class="theme-btn__swatch"></span>
Industrial
</button>
<button class="theme-btn theme-btn--modern" data-theme-target="modern" onclick="setTheme('modern')">
<span class="theme-btn__swatch"></span>
Moderno
</button>
</div>
</header>
<!-- =========================================================================
APP SHELL
========================================================================= -->
<div class="app-shell">
<!-- -----------------------------------------------------------------------
SIDEBAR NAVIGATION
----------------------------------------------------------------------- -->
<aside class="sidebar" role="navigation" aria-label="Navegación principal">
<!-- Brand -->
<div class="sidebar__brand">
<div class="brand-logo">NA</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/sale">
<!-- Dashboard icon -->
<svg class="nav-item__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
<rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/>
<rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/>
</svg>
<span>Dashboard</span>
</a>
<a class="nav-item" href="/pos/sale">
<svg class="nav-item__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
<rect x="2" y="3" width="20" height="14" rx="2"/><path d="M8 21h8M12 17v4"/>
</svg>
<span>POS</span>
</a>
<a class="nav-item" href="/pos/catalog">
<svg class="nav-item__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
<path d="M4 6h16M4 10h16M4 14h16M4 18h16"/>
</svg>
<span>Catálogo</span>
</a>
<a class="nav-item is-active" href="/pos/inventory" aria-current="page">
<svg class="nav-item__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
<path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/>
<polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/>
</svg>
<span>Inventario</span>
</a>
<div class="nav-section-label">Gestión</div>
<a class="nav-item" href="/pos/customers">
<svg class="nav-item__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
<path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/>
<circle cx="9" cy="7" r="4"/>
<path d="M23 21v-2a4 4 0 0 0-3-3.87M16 3.13a4 4 0 0 1 0 7.75"/>
</svg>
<span>Clientes</span>
</a>
<a class="nav-item" href="/pos/invoicing">
<svg class="nav-item__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-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>
<span>Facturación</span>
</a>
<a class="nav-item" href="/pos/accounting">
<svg class="nav-item__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
<line x1="12" y1="1" x2="12" y2="23"/>
<path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/>
</svg>
<span>Contabilidad</span>
</a>
<a class="nav-item" href="/pos/reports">
<svg class="nav-item__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
<line x1="18" y1="20" x2="18" y2="10"/><line x1="12" y1="20" x2="12" y2="4"/>
<line x1="6" y1="20" x2="6" y2="14"/>
</svg>
<span>Reportes</span>
</a>
<div class="nav-section-label">Sistema</div>
<a class="nav-item" href="/pos/config">
<svg class="nav-item__icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round">
<circle cx="12" cy="12" r="3"/>
<path d="M19.07 4.93a10 10 0 0 1 0 14.14M4.93 4.93a10 10 0 0 0 0 14.14"/>
</svg>
<span>Configuración</span>
</a>
</nav>
<!-- Footer user -->
<div class="sidebar__footer">
<div class="sidebar__user-avatar">HG</div>
<div class="sidebar__user-info">
<div class="sidebar__user-name">Hugo García</div>
<div class="sidebar__user-role">Administrador</div>
</div>
</div>
</aside>
<!-- -----------------------------------------------------------------------
MAIN CONTENT
----------------------------------------------------------------------- -->
<main class="main" role="main">
<!-- Page Header -->
<div class="page-header">
<div class="page-header__title-group">
<span class="page-header__eyebrow">Almacén &middot; Sucursal Centro</span>
<h1 class="page-header__title">Inventario</h1>
</div>
<div class="page-header__actions">
<button class="btn btn--ghost" onclick="alert('Exportar: próximamente')">
<svg viewBox="0 0 24 24"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4"/><polyline points="7 10 12 15 17 10"/><line x1="12" y1="15" x2="12" y2="3"/></svg>
Exportar
</button>
<button class="btn btn--ghost" onclick="loadItems(1,'')">
<svg viewBox="0 0 24 24"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 1 1-.38-4.93"/></svg>
Sincronizar
</button>
<button class="btn btn--primary" onclick="showCreateModal()">
<svg viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
Nuevo Producto
</button>
</div>
</div>
<!-- Summary Cards -->
<div class="summary-strip">
<div class="summary-card">
<div class="summary-card__icon">
<svg viewBox="0 0 24 24"><path d="M21 16V8a2 2 0 0 0-1-1.73l-7-4a2 2 0 0 0-2 0l-7 4A2 2 0 0 0 3 8v8a2 2 0 0 0 1 1.73l7 4a2 2 0 0 0 2 0l7-4A2 2 0 0 0 21 16z"/><polyline points="3.27 6.96 12 12.01 20.73 6.96"/><line x1="12" y1="22.08" x2="12" y2="12"/></svg>
</div>
<div class="summary-card__body">
<div class="summary-card__label">Total SKUs</div>
<div class="summary-card__value">4,817</div>
<div class="summary-card__delta summary-card__delta--up">+38 este mes</div>
</div>
</div>
<div class="summary-card">
<div class="summary-card__icon summary-card__icon--value">
<svg viewBox="0 0 24 24"><line x1="12" y1="1" x2="12" y2="23"/><path d="M17 5H9.5a3.5 3.5 0 0 0 0 7h5a3.5 3.5 0 0 1 0 7H6"/></svg>
</div>
<div class="summary-card__body">
<div class="summary-card__label">Valor Inventario</div>
<div class="summary-card__value">$2.4M</div>
<div class="summary-card__sub">MXN · Costo promedio</div>
</div>
</div>
<div class="summary-card">
<div class="summary-card__icon summary-card__icon--alert">
<svg viewBox="0 0 24 24"><path d="M10.29 3.86L1.82 18a2 2 0 0 0 1.71 3h16.94a2 2 0 0 0 1.71-3L13.71 3.86a2 2 0 0 0-3.42 0z"/><line x1="12" y1="9" x2="12" y2="13"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
</div>
<div class="summary-card__body">
<div class="summary-card__label">Alertas Activas</div>
<div class="summary-card__value">23</div>
<div class="summary-card__delta summary-card__delta--down">12 críticas</div>
</div>
</div>
<div class="summary-card">
<div class="summary-card__icon summary-card__icon--time">
<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
</div>
<div class="summary-card__body">
<div class="summary-card__label">Última Actualización</div>
<div class="summary-card__value" style="font-size:1.1rem;">Hoy 09:47</div>
<div class="summary-card__sub">Sincronizado con central</div>
</div>
</div>
</div>
<!-- Tabs Row -->
<div class="tabs-row" role="tablist" aria-label="Módulos de Inventario">
<button class="tab-btn is-active" role="tab" aria-selected="true" aria-controls="panel-stock" onclick="switchTab('stock')">
Stock Actual <span class="tab-btn__badge">4,817</span>
</button>
<button class="tab-btn" role="tab" aria-selected="false" aria-controls="panel-entradas" onclick="switchTab('entradas')">
Entradas <span class="tab-btn__badge">14</span>
</button>
<button class="tab-btn" role="tab" aria-selected="false" aria-controls="panel-salidas" onclick="switchTab('salidas')">
Salidas
</button>
<button class="tab-btn" role="tab" aria-selected="false" aria-controls="panel-traspasos" onclick="switchTab('traspasos')">
Traspasos <span class="tab-btn__badge">3</span>
</button>
<button class="tab-btn" role="tab" aria-selected="false" aria-controls="panel-ajustes" onclick="switchTab('ajustes')">
Ajustes
</button>
<button class="tab-btn" role="tab" aria-selected="false" aria-controls="panel-conteos" onclick="switchTab('conteos')">
Conteos
</button>
<button class="tab-btn" role="tab" aria-selected="false" aria-controls="panel-alertas" onclick="switchTab('alertas')">
Alertas <span class="tab-btn__badge tab-btn__badge--alert">23</span>
</button>
</div>
<!-- Tab Panels -->
<div class="tab-panels" id="tab-panels">
<!-- ===================================================================
TAB 1 — STOCK ACTUAL
=================================================================== -->
<div class="tab-panel is-active" id="panel-stock" role="tabpanel">
<div class="toolbar">
<div class="search-box">
<svg viewBox="0 0 24 24" stroke-linecap="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" id="productSearch" placeholder="Buscar por SKU, nombre, marca…" />
</div>
<select class="select-filter">
<option value="">Todas las marcas</option>
<option>Gates</option><option>SKF</option><option>Bosch</option>
<option>Monroe</option><option>NGK</option>
</select>
<select class="select-filter">
<option value="">Todas las categorías</option>
<option>Motor</option><option>Frenos</option><option>Suspensión</option>
<option>Eléctrico</option><option>Filtros</option>
</select>
<select class="select-filter">
<option value="">Estado stock</option>
<option>OK</option><option>Bajo</option><option>Sobrestock</option>
</select>
<div class="toolbar__spacer"></div>
<button class="btn btn--ghost btn--sm">
<svg viewBox="0 0 24 24"><polygon points="22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3"/></svg>
Filtros
</button>
<button class="btn btn--ghost btn--sm">
<svg viewBox="0 0 24 24"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>
Columnas
</button>
<button class="btn btn--primary btn--sm" onclick="showCreateModal()">
<svg viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
Nuevo Producto
</button>
</div>
<div class="table-wrapper">
<table class="data-table" id="stockTable">
<thead>
<tr>
<th>Barcode</th>
<th>No. Parte</th>
<th>Nombre</th>
<th>Marca</th>
<th style="text-align:right">Stock</th>
<th style="text-align:right">Costo</th>
<th style="text-align:right">Precio 1</th>
<th style="text-align:right">Precio 2</th>
<th style="text-align:right">Precio 3</th>
<th>Ubicación</th>
<th>Acciones</th>
</tr>
</thead>
<tbody id="productTableBody">
</tbody>
</table>
<div class="table-footer">
<div id="productPagination"></div>
</div>
</div>
</div>
<!-- ===================================================================
TAB 2 — ENTRADAS
=================================================================== -->
<div class="tab-panel" id="panel-entradas" role="tabpanel">
<div class="toolbar">
<div class="search-box">
<svg viewBox="0 0 24 24" stroke-linecap="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" placeholder="Buscar por proveedor, folio…" />
</div>
<select class="select-filter">
<option value="">Todos los estados</option>
<option>Recibido</option><option>Pendiente</option><option>Parcial</option>
</select>
<input type="date" class="select-filter" value="2026-04-01" />
<div class="toolbar__spacer"></div>
<button class="btn btn--primary" onclick="showPurchaseModal()">
<svg viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
Nueva Entrada
</button>
</div>
<div class="table-wrapper">
<table class="data-table">
<thead>
<tr>
<th>Folio</th>
<th>Fecha</th>
<th>Proveedor</th>
<th style="text-align:right"># Productos</th>
<th style="text-align:right">Total</th>
<th>Estado</th>
<th>Recibió</th>
<th>Acciones</th>
</tr>
</thead>
<tbody>
<!-- Populated by JS -->
</tbody>
</table>
<div class="table-footer">
<span></span>
<div class="pagination"></div>
</div>
</div>
</div>
<!-- ===================================================================
TAB 3 — SALIDAS
=================================================================== -->
<div class="tab-panel" id="panel-salidas" role="tabpanel">
<div class="toolbar">
<div class="search-box">
<svg viewBox="0 0 24 24" stroke-linecap="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" placeholder="Buscar salidas…" />
</div>
<select class="select-filter">
<option value="">Todos los tipos</option>
<option>Venta</option><option>Garantía</option><option>Devolución</option><option>Traspaso</option>
</select>
<select class="select-filter">
<option value="">Todos los estados</option>
<option>Completado</option><option>En proceso</option><option>Cancelado</option>
</select>
<div class="toolbar__spacer"></div>
</div>
<div class="table-wrapper">
<table class="data-table">
<thead>
<tr>
<th>Folio</th>
<th>Fecha</th>
<th>Tipo</th>
<th style="text-align:right"># Productos</th>
<th>Destino / Referencia</th>
<th>Estado</th>
<th>Autorizó</th>
</tr>
</thead>
<tbody>
<!-- Populated by JS -->
</tbody>
</table>
<div class="table-footer">
<span></span>
<div class="pagination"></div>
</div>
</div>
</div>
<!-- ===================================================================
TAB 4 — TRASPASOS
=================================================================== -->
<div class="tab-panel" id="panel-traspasos" role="tabpanel">
<div class="toolbar">
<div class="search-box">
<svg viewBox="0 0 24 24" stroke-linecap="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" placeholder="Buscar traspasos…" />
</div>
<select class="select-filter">
<option value="">Todas las sucursales</option>
<option>Sucursal Norte</option><option>Sucursal Sur</option><option>Sucursal Oriente</option>
</select>
<select class="select-filter">
<option value="">Todos los estados</option>
<option>En tránsito</option><option>Recibido</option><option>Pendiente</option>
</select>
<div class="toolbar__spacer"></div>
<button class="btn btn--primary" onclick="showTransferModal()">
<svg viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
Nuevo Traspaso
</button>
</div>
<div class="table-wrapper">
<table class="data-table">
<thead>
<tr>
<th>Folio</th>
<th>Fecha</th>
<th>Origen</th>
<th>Destino</th>
<th style="text-align:right"># Productos</th>
<th>Estado</th>
<th>Solicitó</th>
<th>Acciones</th>
</tr>
</thead>
<tbody>
<!-- Populated by JS -->
</tbody>
</table>
<div class="table-footer">
<span></span>
<div class="pagination"></div>
</div>
</div>
</div>
<!-- ===================================================================
TAB 5 — AJUSTES
=================================================================== -->
<div class="tab-panel" id="panel-ajustes" role="tabpanel">
<div class="toolbar">
<div class="search-box">
<svg viewBox="0 0 24 24" stroke-linecap="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" placeholder="Buscar ajustes…" />
</div>
<select class="select-filter">
<option value="">Todos los tipos</option>
<option>Merma</option><option>Daño</option><option>Corrección</option>
</select>
<div class="toolbar__spacer"></div>
<button class="btn btn--primary" onclick="showAdjustmentModal()">
<svg viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
Nuevo Ajuste
</button>
</div>
<div class="table-wrapper">
<table class="data-table">
<thead>
<tr>
<th>Folio</th>
<th>Fecha</th>
<th>Tipo</th>
<th>Producto</th>
<th style="text-align:right">Cantidad</th>
<th>Motivo</th>
<th>Autorizó</th>
</tr>
</thead>
<tbody>
<!-- Populated by JS -->
</tbody>
</table>
<div class="table-footer">
<span></span>
<div class="pagination"></div>
</div>
</div>
</div>
<!-- ===================================================================
TAB 6 — CONTEOS
=================================================================== -->
<div class="tab-panel" id="panel-conteos" role="tabpanel">
<div class="toolbar">
<div class="search-box">
<svg viewBox="0 0 24 24" stroke-linecap="round"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
<input type="text" placeholder="Buscar conteos…" />
</div>
<select class="select-filter">
<option value="">Todas las zonas</option>
<option>Zona A</option><option>Zona B</option><option>Zona C</option><option>Zona D</option><option>Zona E</option>
</select>
<select class="select-filter">
<option value="">Todos los estados</option>
<option>Completado</option><option>En proceso</option><option>Programado</option>
</select>
<div class="toolbar__spacer"></div>
<button class="btn btn--primary" onclick="showCountModal()">
<svg viewBox="0 0 24 24"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
Programar Conteo
</button>
</div>
<div class="table-wrapper">
<table class="data-table">
<thead>
<tr>
<th>Folio</th>
<th>Fecha</th>
<th>Zona</th>
<th style="text-align:right">Contados</th>
<th style="text-align:right">Diferencias</th>
<th>Estado</th>
<th>Realizó</th>
<th>Acciones</th>
</tr>
</thead>
<tbody>
<!-- Populated by JS -->
</tbody>
</table>
<div class="table-footer">
<span></span>
<div class="pagination"></div>
</div>
</div>
</div>
<!-- ===================================================================
TAB 7 — ALERTAS
=================================================================== -->
<div class="tab-panel" id="panel-alertas" role="tabpanel">
<div id="alertsContent">
<!-- Populated by JS -->
</div><!-- /alertsContent -->
</div>
</div><!-- /tab-panels -->
</main>
</div><!-- /app-shell -->
<!-- =========================================================================
SCRIPTS
========================================================================= -->
<script>
/* ------------------------------------------------------------------
Theme switcher
------------------------------------------------------------------ */
function setTheme(theme) {
document.documentElement.setAttribute('data-theme', theme);
document.querySelectorAll('.theme-btn').forEach(function(btn) {
btn.classList.toggle('is-active', btn.dataset.themeTarget === theme);
});
// Persist preference
try { localStorage.setItem('nexus-theme', theme); } catch(e) {}
}
// Restore on load
(function() {
var saved;
try { saved = localStorage.getItem('nexus-theme'); } catch(e) {}
if (saved === 'industrial' || saved === 'modern') {
setTheme(saved);
}
})();
/* ------------------------------------------------------------------
Tab switcher
------------------------------------------------------------------ */
var TAB_MAP = {
stock: { btn: 0, panel: 'panel-stock' },
entradas: { btn: 1, panel: 'panel-entradas' },
salidas: { btn: 2, panel: 'panel-salidas' },
traspasos: { btn: 3, panel: 'panel-traspasos' },
ajustes: { btn: 4, panel: 'panel-ajustes' },
conteos: { btn: 5, panel: 'panel-conteos' },
alertas: { btn: 6, panel: 'panel-alertas' },
};
var tabBtns = document.querySelectorAll('.tab-btn');
var tabPanels = document.querySelectorAll('.tab-panel');
function switchTab(name) {
var target = TAB_MAP[name];
if (!target) return;
// Deactivate all
tabBtns.forEach(function(b) { b.classList.remove('is-active'); b.setAttribute('aria-selected', 'false'); });
tabPanels.forEach(function(p) { p.classList.remove('is-active'); });
// Activate target
tabBtns[target.btn].classList.add('is-active');
tabBtns[target.btn].setAttribute('aria-selected', 'true');
document.getElementById(target.panel).classList.add('is-active');
// Scroll panels container back to top
document.getElementById('tab-panels').scrollTop = 0;
}
/* ------------------------------------------------------------------
Live clock (status bar)
------------------------------------------------------------------ */
(function updateClock() {
var now = new Date();
var hh = String(now.getHours()).padStart(2, '0');
var mm = String(now.getMinutes()).padStart(2, '0');
var el = document.getElementById('live-clock');
if (el) el.textContent = hh + ':' + mm;
setTimeout(updateClock, 30000);
})();
</script>
<!-- ===== MODALS ===== -->
<!-- Create Item Modal -->
<div class="inv-modal-overlay" id="createModal">
<div class="inv-modal">
<div class="inv-modal__header">
<h3>Nuevo Producto</h3>
<button class="inv-modal__close" onclick="closeCreateModal()">&times;</button>
</div>
<div class="inv-modal__body">
<div class="inv-form-grid">
<div class="inv-field"><label>No. Parte *</label><input type="text" id="newPartNumber" placeholder="Ej: GAT-50104" /></div>
<div class="inv-field"><label>Nombre *</label><input type="text" id="newName" placeholder="Nombre del producto" /></div>
<div class="inv-field"><label>Marca</label><input type="text" id="newBrand" placeholder="Marca" /></div>
<div class="inv-field"><label>Barcode</label><input type="text" id="newBarcode" placeholder="Auto-generado si vacío" /></div>
<div class="inv-field"><label>Costo</label><input type="number" id="newCost" step="0.01" placeholder="0.00" /></div>
<div class="inv-field"><label>Precio 1</label><input type="number" id="newPrice1" step="0.01" placeholder="0.00" /></div>
<div class="inv-field"><label>Precio 2</label><input type="number" id="newPrice2" step="0.01" placeholder="0.00" /></div>
<div class="inv-field"><label>Precio 3</label><input type="number" id="newPrice3" step="0.01" placeholder="0.00" /></div>
<div class="inv-field"><label>Stock Mínimo</label><input type="number" id="newMinStock" placeholder="0" /></div>
<div class="inv-field"><label>Stock Inicial</label><input type="number" id="newInitialStock" placeholder="0" /></div>
<div class="inv-field"><label>Ubicación</label><input type="text" id="newLocation" placeholder="Ej: A-12-3" /></div>
</div>
<div id="createResult" style="margin-top:var(--space-3);min-height:1.5em;"></div>
</div>
<div class="inv-modal__footer">
<button class="btn btn--ghost" onclick="closeCreateModal()">Cancelar</button>
<button class="btn btn--primary" onclick="createItem()">Crear Producto</button>
</div>
</div>
</div>
<!-- Purchase Modal -->
<div class="inv-modal-overlay" id="purchaseModal">
<div class="inv-modal">
<div class="inv-modal__header">
<h3>Registrar Compra / Entrada</h3>
<button class="inv-modal__close" onclick="closePurchaseModal()">&times;</button>
</div>
<div class="inv-modal__body">
<div class="inv-form-grid">
<div class="inv-field"><label>ID Producto *</label><input type="number" id="purchaseItemId" placeholder="ID inventario" /></div>
<div class="inv-field"><label>Cantidad *</label><input type="number" id="purchaseQty" placeholder="Cantidad" /></div>
<div class="inv-field"><label>Costo Unitario *</label><input type="number" id="purchaseCost" step="0.01" placeholder="0.00" /></div>
<div class="inv-field"><label>Factura Proveedor</label><input type="text" id="purchaseInvoice" placeholder="No. factura" /></div>
<div class="inv-field inv-field--full"><label>Notas</label><input type="text" id="purchaseNotes" placeholder="Notas adicionales" /></div>
</div>
<div id="purchaseResult" style="margin-top:var(--space-3);min-height:1.5em;"></div>
</div>
<div class="inv-modal__footer">
<button class="btn btn--ghost" onclick="closePurchaseModal()">Cancelar</button>
<button class="btn btn--primary" onclick="recordPurchase()">Registrar Compra</button>
</div>
</div>
</div>
<!-- Transfer Modal -->
<div class="inv-modal-overlay" id="transferModal">
<div class="inv-modal">
<div class="inv-modal__header">
<h3>Nuevo Traspaso</h3>
<button class="inv-modal__close" onclick="closeTransferModal()">&times;</button>
</div>
<div class="inv-modal__body">
<div class="inv-form-grid">
<div class="inv-field"><label>ID Producto *</label><input type="number" id="transferItemId" placeholder="ID inventario" /></div>
<div class="inv-field"><label>Cantidad *</label><input type="number" id="transferQty" placeholder="Cantidad" /></div>
<div class="inv-field"><label>Sucursal Origen *</label><input type="number" id="transferFrom" placeholder="ID sucursal origen" /></div>
<div class="inv-field"><label>Sucursal Destino *</label><input type="number" id="transferTo" placeholder="ID sucursal destino" /></div>
<div class="inv-field inv-field--full"><label>Notas</label><input type="text" id="transferNotes" placeholder="Notas adicionales" /></div>
</div>
<div id="transferResult" style="margin-top:var(--space-3);min-height:1.5em;"></div>
</div>
<div class="inv-modal__footer">
<button class="btn btn--ghost" onclick="closeTransferModal()">Cancelar</button>
<button class="btn btn--primary" onclick="recordTransfer()">Registrar Traspaso</button>
</div>
</div>
</div>
<!-- Adjustment Modal -->
<div class="inv-modal-overlay" id="adjustmentModal">
<div class="inv-modal">
<div class="inv-modal__header">
<h3>Nuevo Ajuste</h3>
<button class="inv-modal__close" onclick="closeAdjustmentModal()">&times;</button>
</div>
<div class="inv-modal__body">
<div class="inv-form-grid">
<div class="inv-field"><label>ID Producto *</label><input type="number" id="adjustItemId" placeholder="ID inventario" /></div>
<div class="inv-field"><label>Cantidad * (negativo=salida)</label><input type="number" id="adjustQty" placeholder="Ej: -3 o +5" /></div>
<div class="inv-field inv-field--full"><label>Razón / Motivo *</label><input type="text" id="adjustReason" placeholder="Motivo del ajuste (obligatorio)" /></div>
</div>
<div id="adjustResult" style="margin-top:var(--space-3);min-height:1.5em;"></div>
</div>
<div class="inv-modal__footer">
<button class="btn btn--ghost" onclick="closeAdjustmentModal()">Cancelar</button>
<button class="btn btn--primary" onclick="recordAdjustment()">Registrar Ajuste</button>
</div>
</div>
</div>
<!-- Physical Count Modal -->
<div class="inv-modal-overlay" id="countModal">
<div class="inv-modal inv-modal--wide">
<div class="inv-modal__header">
<h3>Conteo Físico</h3>
<button class="inv-modal__close" onclick="closeCountModal()">&times;</button>
</div>
<div class="inv-modal__body">
<div id="countLines"></div>
<button class="btn btn--ghost btn--sm" onclick="addCountLine()" style="margin-top:var(--space-2);">+ Agregar Línea</button>
<div id="countResults" style="margin-top:var(--space-4);"></div>
</div>
<div class="inv-modal__footer">
<button class="btn btn--ghost" onclick="closeCountModal()">Cerrar</button>
<button class="btn btn--primary" onclick="startPhysicalCount()">Iniciar Conteo</button>
</div>
</div>
</div>
<!-- History Modal -->
<div class="inv-modal-overlay" id="historyModal">
<div class="inv-modal inv-modal--wide">
<div class="inv-modal__header">
<h3>Historial de Movimientos</h3>
<button class="inv-modal__close" onclick="closeHistoryModal()">&times;</button>
</div>
<div class="inv-modal__body" id="historyContent">
</div>
<div class="inv-modal__footer">
<button class="btn btn--ghost" onclick="closeHistoryModal()">Cerrar</button>
</div>
</div>
</div>
<!-- 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/i18n.js"></script>
<script src="/pos/static/js/app-init.js"></script>
<script src="/pos/static/js/sidebar.js"></script>
<script src="/pos/static/js/inventory.js"></script>
<script src="/pos/static/js/offline-banner.js"></script>
<script src="/pos/static/js/sync-engine.js"></script>
<script>if('serviceWorker' in navigator){navigator.serviceWorker.register('/pos/sw.js',{scope:'/pos/'});}</script>
</body>
</html>