FASE 7d: Lazy Loading + Minificación + Auto-serve minified
Cambios implementados: 1. Lazy loading de imágenes: - catalog.js: loading="lazy" decoding="async" en part cards y detail panel - inventory.js: lazy loading en imagen de detalle de item 2. Minificación de assets: - scripts/minify-assets.sh: minifica JS (terser) y CSS para POS y Dashboard - 25 archivos .min.js + 5 .min.css generados en pos/static/ - 14 archivos .min.js + 8 .min.css generados en dashboard/ 3. Nginx auto-serve minified: - try_files $1.min.js antes de servir .js original - try_files $1.min.css antes de servir .css original - Transparente para los templates HTML (cero cambios en HTML) 4. Cache warming script: - scripts/warm_vehicle_cache.py: pobla Redis con vehicle info por batches - Mitiga DISTINCT ON + 4 JOINs sobre 2B filas - Corre en background, procesa ~1.5M parts Tests: 73/73 pasando
This commit is contained in:
1
dashboard/admin.min.js
vendored
Normal file
1
dashboard/admin.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
485
dashboard/bodega.min.css
vendored
Normal file
485
dashboard/bodega.min.css
vendored
Normal file
@@ -0,0 +1,485 @@
|
|||||||
|
/* ============================================================
|
||||||
|
bodega.css -- Styles for Nexus Autoparts Warehouse (Bodega)
|
||||||
|
============================================================ */
|
||||||
|
|
||||||
|
/* --- Layout --- */
|
||||||
|
.bodega-container {
|
||||||
|
max-width: 1100px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 5.5rem 2rem 3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Tabs --- */
|
||||||
|
.bodega-tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 0;
|
||||||
|
border-bottom: 2px solid var(--border);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bodega-tab {
|
||||||
|
padding: 0.8rem 1.8rem;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 0.95rem;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
border-bottom: 3px solid transparent;
|
||||||
|
transition: all 0.2s;
|
||||||
|
position: relative;
|
||||||
|
bottom: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bodega-tab:hover {
|
||||||
|
color: var(--text-primary);
|
||||||
|
background: var(--bg-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bodega-tab.active {
|
||||||
|
color: var(--accent);
|
||||||
|
border-bottom-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bodega-section {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bodega-section.active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Section Intro --- */
|
||||||
|
.section-intro {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-intro h2 {
|
||||||
|
font-size: 1.3rem;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.section-intro p {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Mapping Form --- */
|
||||||
|
.mapping-form {
|
||||||
|
max-width: 550px;
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mapping-form .form-group {
|
||||||
|
margin-bottom: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mapping-form .form-label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.4rem;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.required {
|
||||||
|
color: var(--danger);
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.optional {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-hint {
|
||||||
|
display: block;
|
||||||
|
margin-top: 0.3rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
margin-top: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-msg {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-msg.success {
|
||||||
|
color: var(--success);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-msg.error {
|
||||||
|
color: var(--danger);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Upload Zone --- */
|
||||||
|
.upload-zone {
|
||||||
|
border: 2px dashed var(--border);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 3rem 2rem;
|
||||||
|
text-align: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
background: var(--bg-card);
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-zone:hover,
|
||||||
|
.upload-zone.dragover {
|
||||||
|
border-color: var(--accent);
|
||||||
|
background: rgba(255, 107, 53, 0.05);
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-icon {
|
||||||
|
font-size: 3rem;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-text {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-hint {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Selected File --- */
|
||||||
|
.selected-file {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--accent);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 0.6rem 1rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-icon {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 1.2rem;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 0.1rem 0.3rem;
|
||||||
|
line-height: 1;
|
||||||
|
transition: color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-icon:hover {
|
||||||
|
color: var(--danger);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Upload Result --- */
|
||||||
|
.upload-result {
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 1.25rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.upload-result h4 {
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-stats {
|
||||||
|
display: flex;
|
||||||
|
gap: 1.5rem;
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-stat {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.4rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-stat.ok {
|
||||||
|
color: var(--success);
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-stat.err {
|
||||||
|
color: var(--danger);
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-samples {
|
||||||
|
margin-top: 0.75rem;
|
||||||
|
padding: 0.75rem;
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
max-height: 150px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-samples p {
|
||||||
|
margin-bottom: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- History --- */
|
||||||
|
.history-section {
|
||||||
|
margin-top: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-section h3 {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Tables --- */
|
||||||
|
.table-wrap {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-size: 0.88rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table thead th {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 0.72rem;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
text-align: left;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table tbody td {
|
||||||
|
padding: 0.7rem 1rem;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.data-table tbody tr:hover {
|
||||||
|
background: var(--bg-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-row {
|
||||||
|
text-align: center;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
padding: 2rem 1rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Status Badges --- */
|
||||||
|
.badge {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.2rem 0.6rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-size: 0.72rem;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-success {
|
||||||
|
background: rgba(34, 197, 94, 0.15);
|
||||||
|
color: var(--success);
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-error {
|
||||||
|
background: rgba(255, 68, 68, 0.15);
|
||||||
|
color: var(--danger);
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-pending {
|
||||||
|
background: rgba(245, 158, 11, 0.15);
|
||||||
|
color: var(--warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-processing {
|
||||||
|
background: rgba(59, 130, 246, 0.15);
|
||||||
|
color: var(--info);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Inventory Toolbar --- */
|
||||||
|
.inventory-toolbar {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
gap: 1rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-box {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
flex: 1;
|
||||||
|
max-width: 500px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-box .form-input {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-danger {
|
||||||
|
background: rgba(255, 68, 68, 0.15);
|
||||||
|
border: 1px solid var(--danger);
|
||||||
|
color: var(--danger);
|
||||||
|
padding: 0.7rem 1.5rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-danger:hover {
|
||||||
|
background: var(--danger);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Pagination --- */
|
||||||
|
.bodega-pagination {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-top: 1.25rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bodega-pagination button {
|
||||||
|
padding: 0.4rem 0.8rem;
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 6px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bodega-pagination button:hover:not(:disabled) {
|
||||||
|
border-color: var(--accent);
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.bodega-pagination button.active {
|
||||||
|
background: var(--accent);
|
||||||
|
border-color: var(--accent);
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bodega-pagination button:disabled {
|
||||||
|
opacity: 0.4;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Confirm Modal --- */
|
||||||
|
.confirm-box {
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 14px;
|
||||||
|
padding: 2rem;
|
||||||
|
max-width: 420px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-box h3 {
|
||||||
|
margin-bottom: 0.75rem;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-box p {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Toast --- */
|
||||||
|
#toast-container {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 2rem;
|
||||||
|
right: 2rem;
|
||||||
|
z-index: 3000;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast {
|
||||||
|
padding: 0.8rem 1.2rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
font-weight: 500;
|
||||||
|
animation: fadeIn 0.3s ease;
|
||||||
|
max-width: 350px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast.success {
|
||||||
|
background: rgba(34, 197, 94, 0.15);
|
||||||
|
border: 1px solid var(--success);
|
||||||
|
color: var(--success);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast.error {
|
||||||
|
background: rgba(255, 68, 68, 0.15);
|
||||||
|
border: 1px solid var(--danger);
|
||||||
|
color: var(--danger);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Responsive --- */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.bodega-container {
|
||||||
|
padding: 5rem 1rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bodega-tabs {
|
||||||
|
overflow-x: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bodega-tab {
|
||||||
|
padding: 0.7rem 1.2rem;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.inventory-toolbar {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-box {
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.result-stats {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
1
dashboard/bodega.min.js
vendored
Normal file
1
dashboard/bodega.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
660
dashboard/captura.min.css
vendored
Normal file
660
dashboard/captura.min.css
vendored
Normal file
@@ -0,0 +1,660 @@
|
|||||||
|
/* ============================================================
|
||||||
|
captura.css -- Styles for Nexus Autoparts Data Entry
|
||||||
|
============================================================ */
|
||||||
|
|
||||||
|
/* --- Tabs --- */
|
||||||
|
.captura-tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 0;
|
||||||
|
border-bottom: 2px solid var(--border);
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.captura-tab {
|
||||||
|
padding: 0.8rem 1.8rem;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 0.95rem;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
border-bottom: 3px solid transparent;
|
||||||
|
transition: all 0.2s;
|
||||||
|
position: relative;
|
||||||
|
bottom: -2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.captura-tab:hover {
|
||||||
|
color: var(--text-primary);
|
||||||
|
background: var(--bg-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.captura-tab.active {
|
||||||
|
color: var(--accent);
|
||||||
|
border-bottom-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.captura-tab .tab-badge {
|
||||||
|
background: var(--accent);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 0.7rem;
|
||||||
|
padding: 0.15rem 0.5rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.captura-section {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.captura-section.active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Vehicle Selector (Section 1) --- */
|
||||||
|
.vehicle-filters {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-filters .filter-group {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-filters label {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-filters select,
|
||||||
|
.vehicle-filters input {
|
||||||
|
padding: 0.5rem 0.8rem;
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
min-width: 160px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-filters select:focus,
|
||||||
|
.vehicle-filters input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Vehicle List --- */
|
||||||
|
.vehicle-list {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||||
|
gap: 0.8rem;
|
||||||
|
max-height: 400px;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding-right: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-card {
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-card:hover {
|
||||||
|
border-color: var(--accent);
|
||||||
|
background: var(--bg-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-card .vc-brand {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 0.95rem;
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-card .vc-model {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 0.2rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-card .vc-details {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-card .vc-parts-count {
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--success);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Vehicle Header (when editing) --- */
|
||||||
|
.vehicle-header {
|
||||||
|
background: linear-gradient(135deg, var(--bg-card) 0%, var(--bg-hover) 100%);
|
||||||
|
border: 1px solid var(--accent);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-header .vh-info {
|
||||||
|
display: flex;
|
||||||
|
gap: 1.5rem;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-header .vh-label {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-header .vh-value {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-header .vh-brand { color: var(--accent); }
|
||||||
|
|
||||||
|
.vehicle-header .vh-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Part Groups Table --- */
|
||||||
|
.category-section {
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-header {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 8px 8px 0 0;
|
||||||
|
padding: 0.6rem 1rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-header:hover {
|
||||||
|
background: var(--bg-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-header h3 {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-header .cat-toggle {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
transition: transform 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-header.collapsed .cat-toggle {
|
||||||
|
transform: rotate(-90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-body {
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-top: none;
|
||||||
|
border-radius: 0 0 8px 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.category-body.collapsed {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-section {
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
padding: 0.8rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-section:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.group-name {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-primary);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Part Rows --- */
|
||||||
|
.part-rows {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.4rem;
|
||||||
|
margin-bottom: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.part-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.part-row input {
|
||||||
|
padding: 0.4rem 0.6rem;
|
||||||
|
background: var(--bg-primary);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 6px;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.part-row input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.part-row .pr-oem {
|
||||||
|
width: 160px;
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.part-row .pr-name {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 150px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.part-row .pr-qty {
|
||||||
|
width: 50px;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.part-row .pr-btn {
|
||||||
|
padding: 0.3rem 0.6rem;
|
||||||
|
border: none;
|
||||||
|
border-radius: 6px;
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
font-weight: 600;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.part-row .pr-save {
|
||||||
|
background: var(--success);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.part-row .pr-save:hover { background: #1ea34e; }
|
||||||
|
|
||||||
|
.part-row .pr-delete {
|
||||||
|
background: var(--danger);
|
||||||
|
color: #fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.part-row .pr-delete:hover { background: #cc3333; }
|
||||||
|
|
||||||
|
.part-row.saved {
|
||||||
|
background: rgba(34, 197, 94, 0.08);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 0.2rem 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.part-row.saved input {
|
||||||
|
background: transparent;
|
||||||
|
border-color: var(--success);
|
||||||
|
color: var(--success);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-add-part {
|
||||||
|
background: transparent;
|
||||||
|
border: 1px dashed var(--border);
|
||||||
|
border-radius: 6px;
|
||||||
|
padding: 0.3rem 0.8rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 0.8rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-add-part:hover {
|
||||||
|
border-color: var(--accent);
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Progress Bar --- */
|
||||||
|
.progress-bar {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border-radius: 10px;
|
||||||
|
height: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
margin: 0.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar .progress-fill {
|
||||||
|
height: 100%;
|
||||||
|
background: linear-gradient(90deg, var(--accent), var(--success));
|
||||||
|
border-radius: 10px;
|
||||||
|
transition: width 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-text {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Section 2: Intercambios --- */
|
||||||
|
.part-detail-card {
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 1rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.part-detail-card .pdc-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.part-detail-card .pdc-oem {
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.part-detail-card .pdc-name {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.part-detail-card .pdc-group {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
background: var(--bg-hover);
|
||||||
|
padding: 0.2rem 0.5rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aftermarket-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aftermarket-table th {
|
||||||
|
text-align: left;
|
||||||
|
padding: 0.5rem;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aftermarket-table td {
|
||||||
|
padding: 0.4rem 0.5rem;
|
||||||
|
border-bottom: 1px solid rgba(42, 42, 58, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.aftermarket-form {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
align-items: flex-end;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
margin-top: 0.8rem;
|
||||||
|
padding-top: 0.8rem;
|
||||||
|
border-top: 1px dashed var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.aftermarket-form .af-field {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aftermarket-form label {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aftermarket-form select,
|
||||||
|
.aftermarket-form input {
|
||||||
|
padding: 0.4rem 0.6rem;
|
||||||
|
background: var(--bg-primary);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 6px;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.aftermarket-form select:focus,
|
||||||
|
.aftermarket-form input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Section 3: Imágenes --- */
|
||||||
|
.image-card {
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 1rem;
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-card .ic-preview {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border: 2px dashed var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
overflow: hidden;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-card .ic-preview img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-card .ic-info {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-card .ic-oem {
|
||||||
|
font-family: monospace;
|
||||||
|
font-weight: 700;
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-card .ic-name {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-card .ic-upload {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.image-card .ic-upload input[type="file"] {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Search bar --- */
|
||||||
|
.captura-search {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.8rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.captura-search input {
|
||||||
|
padding: 0.5rem 0.8rem;
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
flex: 1;
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.captura-search input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Pagination --- */
|
||||||
|
.captura-pagination {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-top: 1rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.captura-pagination button {
|
||||||
|
padding: 0.4rem 0.8rem;
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 6px;
|
||||||
|
color: var(--text-primary);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.captura-pagination button:hover {
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.captura-pagination button:disabled {
|
||||||
|
opacity: 0.4;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.captura-pagination .page-info {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Empty state --- */
|
||||||
|
.empty-state {
|
||||||
|
text-align: center;
|
||||||
|
padding: 3rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state .es-icon {
|
||||||
|
font-size: 2.5rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state .es-text {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Toast notifications --- */
|
||||||
|
.toast {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 2rem;
|
||||||
|
right: 2rem;
|
||||||
|
padding: 0.8rem 1.5rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
z-index: 9999;
|
||||||
|
animation: toastIn 0.3s ease;
|
||||||
|
box-shadow: 0 4px 20px rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toast.success { background: var(--success); }
|
||||||
|
.toast.error { background: var(--danger); }
|
||||||
|
|
||||||
|
@keyframes toastIn {
|
||||||
|
from { transform: translateY(20px); opacity: 0; }
|
||||||
|
to { transform: translateY(0); opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Loading spinner --- */
|
||||||
|
.loading {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
border: 3px solid var(--border);
|
||||||
|
border-top-color: var(--accent);
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 0.8s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spin {
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Layout --- */
|
||||||
|
.captura-container {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 5rem 2rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Status tabs for vehicles --- */
|
||||||
|
.status-tabs {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-tab {
|
||||||
|
padding: 0.4rem 1rem;
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 20px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 0.8rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-tab:hover { border-color: var(--accent); }
|
||||||
|
|
||||||
|
.status-tab.active {
|
||||||
|
background: var(--accent);
|
||||||
|
color: #fff;
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
1
dashboard/captura.min.js
vendored
Normal file
1
dashboard/captura.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dashboard/catalog-public.min.js
vendored
Normal file
1
dashboard/catalog-public.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
237
dashboard/chat-public.min.css
vendored
Normal file
237
dashboard/chat-public.min.css
vendored
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
/* ==========================================================================
|
||||||
|
NEXUS — Public Catalog Chat Widget
|
||||||
|
Reuses design tokens from tokens.css
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
.chat-fab {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 24px;
|
||||||
|
right: 24px;
|
||||||
|
z-index: 8000;
|
||||||
|
width: 52px;
|
||||||
|
height: 52px;
|
||||||
|
border-radius: var(--radius-full, 50%);
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
background: var(--color-accent, #F5A623);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: var(--shadow-lg, 0 4px 12px rgba(0,0,0,0.3));
|
||||||
|
transition: transform 0.2s ease, background 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-fab:hover {
|
||||||
|
transform: scale(1.08);
|
||||||
|
background: var(--color-primary-hover, #e5952f);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-panel {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 90px;
|
||||||
|
right: 24px;
|
||||||
|
z-index: 8001;
|
||||||
|
width: 400px;
|
||||||
|
height: 520px;
|
||||||
|
max-height: calc(100vh - 100px);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background: var(--color-bg-elevated, #1a1a1a);
|
||||||
|
border: 1px solid var(--color-border, #333);
|
||||||
|
border-radius: var(--radius-xl, 16px);
|
||||||
|
box-shadow: var(--shadow-xl, 0 8px 32px rgba(0,0,0,0.4));
|
||||||
|
overflow: hidden;
|
||||||
|
transform: translateY(20px) scale(0.95);
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
transition: transform 0.25s ease, opacity 0.25s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-panel.open {
|
||||||
|
transform: translateY(0) scale(1);
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: all;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 12px 16px;
|
||||||
|
background: var(--color-accent, #F5A623);
|
||||||
|
color: #fff;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-header h3 {
|
||||||
|
font-family: var(--font-heading, sans-serif);
|
||||||
|
font-size: 0.95rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-header-close {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: 4px;
|
||||||
|
line-height: 1;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-header-close:hover { opacity: 1; }
|
||||||
|
|
||||||
|
.chat-messages {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: 12px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-msg {
|
||||||
|
max-width: 85%;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 0.875rem;
|
||||||
|
line-height: 1.45;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-msg.user {
|
||||||
|
align-self: flex-end;
|
||||||
|
background: var(--color-accent, #F5A623);
|
||||||
|
color: #fff;
|
||||||
|
border-bottom-right-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-msg.ai {
|
||||||
|
align-self: flex-start;
|
||||||
|
background: var(--color-surface-2, rgba(255,255,255,0.06));
|
||||||
|
color: var(--color-text-primary, #fff);
|
||||||
|
border-bottom-left-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-typing {
|
||||||
|
align-self: flex-start;
|
||||||
|
display: none;
|
||||||
|
gap: 4px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
background: var(--color-surface-2, rgba(255,255,255,0.06));
|
||||||
|
border-radius: 12px;
|
||||||
|
border-bottom-left-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-typing.visible { display: flex; }
|
||||||
|
|
||||||
|
.chat-typing span {
|
||||||
|
width: 7px;
|
||||||
|
height: 7px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--color-text-muted, #888);
|
||||||
|
animation: chatBounce 1.2s infinite;
|
||||||
|
}
|
||||||
|
.chat-typing span:nth-child(2) { animation-delay: 0.2s; }
|
||||||
|
.chat-typing span:nth-child(3) { animation-delay: 0.4s; }
|
||||||
|
|
||||||
|
@keyframes chatBounce {
|
||||||
|
0%, 60%, 100% { transform: translateY(0); }
|
||||||
|
30% { transform: translateY(-5px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-parts {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-part-card {
|
||||||
|
background: var(--color-bg-elevated, #1a1a1a);
|
||||||
|
border: 1px solid var(--color-border, #333);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 8px 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color 0.15s ease, background 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-part-card:hover {
|
||||||
|
border-color: var(--color-accent, #F5A623);
|
||||||
|
background: var(--color-bg-base, #111);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-part-card .part-number {
|
||||||
|
font-family: var(--font-mono, monospace);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--color-accent, #F5A623);
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-part-card .part-name {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
color: var(--color-text-primary, #fff);
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-input-area {
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
padding: 12px;
|
||||||
|
border-top: 1px solid var(--color-border, #333);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-input {
|
||||||
|
flex: 1;
|
||||||
|
padding: 8px 12px;
|
||||||
|
border: 1px solid var(--color-border, #333);
|
||||||
|
border-radius: 8px;
|
||||||
|
background: var(--color-bg-base, #111);
|
||||||
|
color: var(--color-text-primary, #fff);
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-family: var(--font-body, sans-serif);
|
||||||
|
resize: none;
|
||||||
|
outline: none;
|
||||||
|
min-height: 38px;
|
||||||
|
max-height: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-input:focus {
|
||||||
|
border-color: var(--color-accent, #F5A623);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-input::placeholder {
|
||||||
|
color: var(--color-text-muted, #888);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-send-btn {
|
||||||
|
width: 38px;
|
||||||
|
height: 38px;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: none;
|
||||||
|
background: var(--color-accent, #F5A623);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
transition: background 0.15s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-send-btn:hover { background: var(--color-primary-hover, #e5952f); }
|
||||||
|
.chat-send-btn:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.chat-panel {
|
||||||
|
width: calc(100vw - 16px);
|
||||||
|
right: 8px;
|
||||||
|
height: 60vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
1
dashboard/chat-public.min.js
vendored
Normal file
1
dashboard/chat-public.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
!function(){"use strict";var e=!1,t=!1,n=[];function a(){var e=document.createElement("button");e.className="chat-fab",e.id="chatFab",e.title="Asistente IA",e.innerHTML="💬",e.setAttribute("aria-label","Abrir asistente IA");var t=document.createElement("div");t.className="chat-panel",t.id="chatPanel",t.innerHTML='<div class="chat-header"><h3>Asistente — Buscar partes</h3><button class="chat-header-close" id="chatClose" aria-label="Cerrar">×</button></div><div class="chat-messages" id="chatMessages"><div class="chat-msg ai">Hola, soy el asistente de Nexus Autoparts. Dime que refaccion buscas y te ayudo a encontrarla en el catalogo.</div><div class="chat-typing" id="chatTyping"><span></span><span></span><span></span></div></div><div class="chat-input-area"><textarea class="chat-input" id="chatInput" placeholder="Ej: Balatas para Tsuru 2015..." rows="1"></textarea><button class="chat-send-btn" id="chatSend" aria-label="Enviar">▶</button></div>',document.body.appendChild(e),document.body.appendChild(t),e.addEventListener("click",s),document.getElementById("chatClose").addEventListener("click",s),document.getElementById("chatSend").addEventListener("click",c),document.getElementById("chatInput").addEventListener("keydown",(function(e){"Enter"!==e.key||e.shiftKey||(e.preventDefault(),c())})),document.getElementById("chatInput").addEventListener("input",(function(){this.style.height="auto",this.style.height=Math.min(this.scrollHeight,80)+"px"}))}function s(){e=!e;var t=document.getElementById("chatPanel"),n=document.getElementById("chatFab");e?(t.classList.add("open"),n.style.display="none",document.getElementById("chatInput").focus()):(t.classList.remove("open"),n.style.display="flex")}function c(){if(!t){var e=document.getElementById("chatInput"),a=e.value.trim();a&&(e.value="",e.style.height="auto",i(a,"user"),n.push({role:"user",content:a}),n.length>20&&n.splice(0,2),t=!0,document.getElementById("chatSend").disabled=!0,d(!0),fetch("/api/chat",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({message:a,history:n.slice(-10)})}).then((function(e){return e.json()})).then((function(e){if(e.error)i("Error: "+e.error,"ai");else{var t,a,c,d,l=e.response||"Sin respuesta.";i(l,"ai"),n.push({role:"assistant",content:l}),e.search_results&&e.search_results.length>0&&(t=e.search_results,a=document.getElementById("chatMessages"),c=document.getElementById("chatTyping"),(d=document.createElement("div")).className="chat-parts",t.slice(0,8).forEach((function(e){var t=document.createElement("div");t.className="chat-part-card";var n=e.name_es||e.name_part||"",a=e.oem_part_number||e.part_number||"",c=e.brand||"";t.innerHTML='<div class="part-number">'+o(a)+'</div><div class="part-name">'+o(n)+(c?' <span style="color:var(--color-text-muted);">('+o(c)+")</span>":"")+"</div>",t.style.cursor="pointer",t.addEventListener("click",(function(){var e=document.getElementById("searchInput");e&&a&&(e.value=a,"function"==typeof window.doSearch&&window.doSearch(),s())})),d.appendChild(t)})),a.insertBefore(d,c),r())}})).catch((function(e){i("Error de conexion: "+e.message,"ai")})).finally((function(){t=!1,document.getElementById("chatSend").disabled=!1,d(!1)})))}}function i(e,t){var n=document.getElementById("chatMessages"),a=document.getElementById("chatTyping"),s=document.createElement("div");s.className="chat-msg "+t,s.textContent=e,n.insertBefore(s,a),r()}function d(e){var t=document.getElementById("chatTyping");t&&t.classList.toggle("visible",e),e&&r()}function r(){var e=document.getElementById("chatMessages");e&&(e.scrollTop=e.scrollHeight)}function o(e){if(!e)return"";var t=document.createElement("div");return t.textContent=e,t.innerHTML}"loading"===document.readyState?document.addEventListener("DOMContentLoaded",a):a()}();
|
||||||
282
dashboard/cuentas.min.css
vendored
Normal file
282
dashboard/cuentas.min.css
vendored
Normal file
@@ -0,0 +1,282 @@
|
|||||||
|
/* ============================================================
|
||||||
|
cuentas.css -- Accounts receivable styles
|
||||||
|
============================================================ */
|
||||||
|
|
||||||
|
.cuentas-container {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 5rem 2rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Customer list --- */
|
||||||
|
.cuentas-search {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.8rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cuentas-search input {
|
||||||
|
flex: 1;
|
||||||
|
max-width: 400px;
|
||||||
|
padding: 0.5rem 0.8rem;
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cuentas-search input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||||
|
gap: 0.8rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-card-item {
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-card-item:hover {
|
||||||
|
border-color: var(--accent);
|
||||||
|
background: var(--bg-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cci-name {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1rem;
|
||||||
|
margin-bottom: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cci-rfc {
|
||||||
|
font-family: monospace;
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cci-balance-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cci-balance {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cci-balance.positive { color: var(--danger); }
|
||||||
|
.cci-balance.zero { color: var(--success); }
|
||||||
|
|
||||||
|
.cci-limit {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Customer detail view --- */
|
||||||
|
.detail-view {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-header {
|
||||||
|
background: linear-gradient(135deg, var(--bg-card) 0%, var(--bg-hover) 100%);
|
||||||
|
border: 1px solid var(--accent);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 1.2rem 1.5rem;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dh-info {
|
||||||
|
display: flex;
|
||||||
|
gap: 2rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dh-field .dh-label {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dh-field .dh-value {
|
||||||
|
font-size: 1rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dh-field .dh-value.accent { color: var(--accent); }
|
||||||
|
.dh-field .dh-value.danger { color: var(--danger); }
|
||||||
|
.dh-field .dh-value.success { color: var(--success); }
|
||||||
|
|
||||||
|
/* --- Two-column layout for invoices/payments --- */
|
||||||
|
.detail-columns {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.detail-columns { grid-template-columns: 1fr; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-card {
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-card h3 {
|
||||||
|
padding: 0.8rem 1rem;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-table th {
|
||||||
|
text-align: left;
|
||||||
|
padding: 0.5rem 0.6rem;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-table td {
|
||||||
|
padding: 0.4rem 0.6rem;
|
||||||
|
border-bottom: 1px solid rgba(42, 42, 58, 0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.15rem 0.5rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.status-badge.pending { background: rgba(245, 158, 11, 0.15); color: var(--warning); }
|
||||||
|
.status-badge.partial { background: rgba(59, 130, 246, 0.15); color: var(--info); }
|
||||||
|
.status-badge.paid { background: rgba(34, 197, 94, 0.15); color: var(--success); }
|
||||||
|
.status-badge.cancelled { background: rgba(255, 68, 68, 0.15); color: var(--danger); }
|
||||||
|
|
||||||
|
/* --- Payment form --- */
|
||||||
|
.payment-form {
|
||||||
|
padding: 1rem;
|
||||||
|
border-top: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.payment-form h4 {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: var(--accent);
|
||||||
|
margin-bottom: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-field {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-field label {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-field input,
|
||||||
|
.pf-field select {
|
||||||
|
padding: 0.4rem 0.6rem;
|
||||||
|
background: var(--bg-primary);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 6px;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pf-field input:focus,
|
||||||
|
.pf-field select:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Toast --- */
|
||||||
|
.toast {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 2rem;
|
||||||
|
right: 2rem;
|
||||||
|
padding: 0.8rem 1.5rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
z-index: 9999;
|
||||||
|
animation: toastIn 0.3s ease;
|
||||||
|
box-shadow: 0 4px 20px rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
|
.toast.success { background: var(--success); }
|
||||||
|
.toast.error { background: var(--danger); }
|
||||||
|
@keyframes toastIn {
|
||||||
|
from { transform: translateY(20px); opacity: 0; }
|
||||||
|
to { transform: translateY(0); opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Pagination --- */
|
||||||
|
.cuentas-pagination {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cuentas-pagination button {
|
||||||
|
padding: 0.4rem 0.8rem;
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 6px;
|
||||||
|
color: var(--text-primary);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cuentas-pagination button:hover { border-color: var(--accent); }
|
||||||
|
.cuentas-pagination button:disabled { opacity: 0.4; cursor: not-allowed; }
|
||||||
|
.cuentas-pagination .page-info { font-size: 0.8rem; color: var(--text-secondary); }
|
||||||
|
|
||||||
|
.empty-state {
|
||||||
|
text-align: center;
|
||||||
|
padding: 2rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
1
dashboard/cuentas.min.js
vendored
Normal file
1
dashboard/cuentas.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dashboard/dashboard.min.js
vendored
Normal file
1
dashboard/dashboard.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dashboard/enhanced-search.min.js
vendored
Normal file
1
dashboard/enhanced-search.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
dashboard/landing.min.js
vendored
Normal file
1
dashboard/landing.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
211
dashboard/login.min.css
vendored
Normal file
211
dashboard/login.min.css
vendored
Normal file
@@ -0,0 +1,211 @@
|
|||||||
|
/* ============================================================
|
||||||
|
login.css -- Login / Register page styles
|
||||||
|
============================================================ */
|
||||||
|
|
||||||
|
.login-page {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
min-height: 100vh;
|
||||||
|
background: var(--bg-primary);
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Card --- */
|
||||||
|
.login-card {
|
||||||
|
width: 100%;
|
||||||
|
max-width: 440px;
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 2.5rem;
|
||||||
|
animation: fadeIn 0.4s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Brand header --- */
|
||||||
|
.login-brand {
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-brand .logo-icon {
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
background: linear-gradient(135deg, var(--accent) 0%, #ff4500 100%);
|
||||||
|
border-radius: 14px;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 1.6rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
box-shadow: 0 4px 20px var(--accent-glow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-brand h1 {
|
||||||
|
font-family: 'Orbitron', sans-serif;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
font-weight: 700;
|
||||||
|
letter-spacing: 2px;
|
||||||
|
color: var(--text-primary);
|
||||||
|
margin-bottom: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-brand h1 span {
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-brand .slogan {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Form panel visibility --- */
|
||||||
|
.form-panel {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-panel.active {
|
||||||
|
display: block;
|
||||||
|
animation: fadeIn 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Form title --- */
|
||||||
|
.form-title {
|
||||||
|
font-size: 1.15rem;
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
text-align: center;
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Select (dropdown) --- */
|
||||||
|
.form-select {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 0.95rem;
|
||||||
|
transition: border-color 0.2s;
|
||||||
|
appearance: none;
|
||||||
|
-webkit-appearance: none;
|
||||||
|
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='%23a0a0b0' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: right 1rem center;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-select:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-select option {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Submit button (full width) --- */
|
||||||
|
.btn-submit {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.85rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Toggle link --- */
|
||||||
|
.toggle-link {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-link a {
|
||||||
|
color: var(--accent);
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.toggle-link a:hover {
|
||||||
|
color: var(--accent-hover);
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Alert messages --- */
|
||||||
|
.login-alert {
|
||||||
|
padding: 0.85rem 1rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 1.25rem;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
display: none;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-alert.show {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-alert.error {
|
||||||
|
background: rgba(255, 68, 68, 0.1);
|
||||||
|
border: 1px solid var(--danger);
|
||||||
|
color: var(--danger);
|
||||||
|
}
|
||||||
|
|
||||||
|
.login-alert.success {
|
||||||
|
background: rgba(0, 214, 143, 0.1);
|
||||||
|
border: 1px solid var(--success);
|
||||||
|
color: var(--success);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Loading spinner on button --- */
|
||||||
|
.btn-submit.loading {
|
||||||
|
pointer-events: none;
|
||||||
|
opacity: 0.7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-submit .spinner {
|
||||||
|
display: none;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
border: 2px solid rgba(255,255,255,0.3);
|
||||||
|
border-top-color: #fff;
|
||||||
|
border-radius: 50%;
|
||||||
|
animation: spin 0.6s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-submit.loading .spinner {
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-submit.loading .btn-label {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Row layout for two fields side by side --- */
|
||||||
|
.form-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row .form-group {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Responsive --- */
|
||||||
|
@media (max-width: 500px) {
|
||||||
|
.login-card {
|
||||||
|
padding: 1.75rem 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-row {
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
1
dashboard/login.min.js
vendored
Normal file
1
dashboard/login.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
!function(){"use strict";const e=document.getElementById("loginPanel"),t=document.getElementById("registerPanel"),o=document.getElementById("loginForm"),n=document.getElementById("registerForm"),r=document.getElementById("alert"),a={ADMIN:"/demo",OWNER:"/demo",BODEGA:"/bodega",TALLER:"/demo"};function s(e,t){r.textContent=e,r.className="login-alert show "+t}function c(){r.className="login-alert",r.textContent=""}function l(e,t){e.classList.toggle("loading",t)}!function(){const e=localStorage.getItem("access_token"),t=localStorage.getItem("user_role");if(e&&t){const e=a[t]||"/index.html";window.location.replace(e)}}(),window.showPanel=function(o){c(),"register"===o?(e.classList.remove("active"),t.classList.add("active")):(t.classList.remove("active"),e.classList.add("active"))},o.addEventListener("submit",(async function(e){e.preventDefault(),c();const t=document.getElementById("loginEmail").value.trim(),n=document.getElementById("loginPassword").value,r=o.querySelector(".btn-submit");if(t&&n){l(r,!0);try{const e=await fetch("/api/auth/login",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({email:t,password:n})}),o=await e.json();if(!e.ok)return void s(o.error||o.message||"Credenciales incorrectas.","error");localStorage.setItem("access_token",o.access_token),localStorage.setItem("refresh_token",o.refresh_token||""),localStorage.setItem("user_role",o.role||o.user?.role||""),localStorage.setItem("user_name",o.name||o.user?.name||"");const r=(o.role||o.user?.role||"").toUpperCase(),c=a[r]||"/index.html";window.location.replace(c)}catch(e){s("Error de conexion. Intenta de nuevo.","error")}finally{l(r,!1)}}else s("Completa todos los campos.","error")})),n.addEventListener("submit",(async function(e){e.preventDefault(),c();const t=document.getElementById("regName").value.trim(),o=document.getElementById("regEmail").value.trim(),r=document.getElementById("regPassword").value,a=document.getElementById("regConfirm").value,i=document.getElementById("regBusiness").value.trim(),m=document.getElementById("regPhone").value.trim(),d=document.getElementById("regRole").value,u=n.querySelector(".btn-submit");if(t&&o&&r&&a&&i&&m)if(r.length<8)s("La contrasena debe tener al menos 8 caracteres.","error");else if(r===a){l(u,!0);try{const e=await fetch("/api/auth/register",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({name:t,email:o,password:r,role:d,business_name:i,phone:m})}),a=await e.json();if(!e.ok)return void s(a.error||a.message||"Error al crear la cuenta.","error");s("Cuenta creada. Pendiente de aprobacion por administrador.","success"),n.reset()}catch(e){s("Error de conexion. Intenta de nuevo.","error")}finally{l(u,!1)}}else s("Las contrasenas no coinciden.","error");else s("Completa todos los campos.","error")})),window.authFetch=async function(e,t={}){const o=localStorage.getItem("access_token");if(!o)return void window.location.replace("/login.html");const n=Object.assign({},t.headers||{},{Authorization:"Bearer "+o});let r=await fetch(e,Object.assign({},t,{headers:n}));if(401===r.status){const o=await async function(){const e=localStorage.getItem("refresh_token");if(!e)return!1;try{const t=await fetch("/api/auth/refresh",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({refresh_token:e})});if(!t.ok)return!1;const o=await t.json();return localStorage.setItem("access_token",o.access_token),o.refresh_token&&localStorage.setItem("refresh_token",o.refresh_token),!0}catch(e){return!1}}();if(!o)return void logout();n.Authorization="Bearer "+localStorage.getItem("access_token"),r=await fetch(e,Object.assign({},t,{headers:n}))}return r},window.logout=function(){localStorage.removeItem("access_token"),localStorage.removeItem("refresh_token"),localStorage.removeItem("user_role"),localStorage.removeItem("user_name"),window.location.replace("/login.html")}}();
|
||||||
1
dashboard/nav.min.js
vendored
Normal file
1
dashboard/nav.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
!function(){"use strict";var e=window.location.pathname;function t(t){var a=t.replace(/\/+$/,"")||"/",n=e.replace(/\/+$/,"")||"/";return a===n||(!("/"!==a&&"/index.html"!==a||"/"!==n&&"/index.html"!==n)||(!("/admin.html"!==a&&"/admin"!==a||"/admin.html"!==n&&"/admin"!==n)||(!("/diagramas"!==a&&"/diagrams.html"!==a||"/diagramas"!==n&&"/diagrams.html"!==n)||("/customer-landing.html"===a&&"/customer-landing.html"===n||("/captura"===a&&"/captura"===n||("/pos"===a&&"/pos"===n||("/cuentas"===a&&"/cuentas"===n||("/tienda"===a&&"/tienda"===n||("/bodega"===a&&"/bodega"===n||!("/demo"!==a&&"/demo.html"!==a||"/demo"!==n&&"/demo.html"!==n))))))))))}var a='<header id="shared-nav-header" style="background: rgba(18, 18, 26, 0.95);backdrop-filter: blur(20px);-webkit-backdrop-filter: blur(20px);border-bottom: 1px solid var(--border);padding: 1rem 2rem;position: fixed;top: 0; left: 0; right: 0;z-index: 1000;"><div style="max-width: 1600px;margin: 0 auto;display: flex;justify-content: space-between;align-items: center;gap: 2rem;"><a href="/" style="display: flex;align-items: center;gap: 0.75rem;text-decoration: none;flex-shrink: 0;"><div style="width: 42px; height: 42px;background: linear-gradient(135deg, var(--accent) 0%, #ff4500 100%);border-radius: 10px;display: flex; align-items: center; justify-content: center;font-size: 1.5rem;box-shadow: 0 4px 20px var(--accent-glow);">⚙️</div><span style="font-family: Orbitron, sans-serif;font-size: 1.3rem;font-weight: 700;background: linear-gradient(135deg, #fff 0%, var(--accent) 100%);-webkit-background-clip: text;-webkit-text-fill-color: transparent;background-clip: text;">NEXUS AUTOPARTS</span></a><div id="shared-nav-extra" style="display: contents;"></div><nav id="shared-nav-links" style="display: flex;gap: 1.5rem;align-items: center;flex-shrink: 0;">'+[{label:"Demo",href:"/demo"},{label:"Tienda",href:"/tienda"},{label:"Catálogo",href:"/index.html"},{label:"Captura",href:"/captura"},{label:"POS",href:"/pos"},{label:"Cuentas",href:"/cuentas"},{label:"Bodega",href:"/bodega"},{label:"Admin",href:"/admin"}].map((function(e){var a="text-decoration: none; font-size: 0.9rem; font-weight: 500; transition: color 0.2s;";return t(e.href)?a+=" color: var(--accent);":a+=" color: var(--text-secondary);",'<a href="'+e.href+'" style="'+a+'" onmouseover="this.style.color=\'var(--accent)\'" onmouseout="'+(t(e.href)?"":"this.style.color='var(--text-secondary)'")+'">'+e.label+"</a>"})).join("")+'</nav><div id="nav-auth" style="display:flex;align-items:center;gap:0.75rem;flex-shrink:0;"><span id="nav-user-name" style="color:var(--text-secondary);font-size:0.85rem;"></span><a id="nav-auth-btn" href="/login.html" style="text-decoration:none;font-size:0.85rem;font-weight:500;color:var(--bg);background:var(--accent);padding:0.4rem 1rem;border-radius:6px;transition:opacity 0.2s;">Iniciar Sesión</a></div></div></header>',n=document.getElementById("shared-nav");n&&(n.innerHTML=a);var r=localStorage.getItem("access_token");if(r)try{var i=JSON.parse(atob(r.split(".")[1])),o=document.getElementById("nav-user-name"),l=document.getElementById("nav-auth-btn");o&&i.business_name?o.textContent=i.business_name:o&&(o.textContent=i.role||""),l&&(l.textContent="Salir",l.href="#",l.onclick=function(e){e.preventDefault(),localStorage.removeItem("access_token"),localStorage.removeItem("refresh_token"),window.location.href="/login.html"})}catch(e){}}();
|
||||||
418
dashboard/pos.min.css
vendored
Normal file
418
dashboard/pos.min.css
vendored
Normal file
@@ -0,0 +1,418 @@
|
|||||||
|
/* ============================================================
|
||||||
|
pos.css -- Point of Sale styles
|
||||||
|
============================================================ */
|
||||||
|
|
||||||
|
.pos-container {
|
||||||
|
max-width: 1400px;
|
||||||
|
margin: 0 auto;
|
||||||
|
padding: 5rem 2rem 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Layout: 2 columns --- */
|
||||||
|
.pos-layout {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 360px;
|
||||||
|
gap: 1.5rem;
|
||||||
|
align-items: start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Left: Search + Cart --- */
|
||||||
|
.pos-main {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Customer bar --- */
|
||||||
|
.customer-bar {
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 1rem;
|
||||||
|
display: flex;
|
||||||
|
gap: 1rem;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-bar .cb-search {
|
||||||
|
flex: 1;
|
||||||
|
padding: 0.5rem 0.8rem;
|
||||||
|
background: var(--bg-primary);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-bar .cb-search:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-bar .cb-selected {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.8rem;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-bar .cb-name {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-bar .cb-rfc {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-family: monospace;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-bar .cb-balance {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
padding: 0.2rem 0.6rem;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cb-balance.positive { background: rgba(255, 68, 68, 0.15); color: var(--danger); }
|
||||||
|
.cb-balance.zero { background: rgba(34, 197, 94, 0.15); color: var(--success); }
|
||||||
|
|
||||||
|
/* --- Customer dropdown --- */
|
||||||
|
.customer-dropdown {
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 0 0 8px 8px;
|
||||||
|
max-height: 250px;
|
||||||
|
overflow-y: auto;
|
||||||
|
z-index: 100;
|
||||||
|
box-shadow: 0 8px 30px rgba(0,0,0,0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-dropdown-item {
|
||||||
|
padding: 0.6rem 1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-dropdown-item:hover {
|
||||||
|
background: var(--bg-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.customer-dropdown-item .cdi-name { font-weight: 600; }
|
||||||
|
.customer-dropdown-item .cdi-rfc { font-size: 0.8rem; color: var(--text-secondary); }
|
||||||
|
|
||||||
|
/* --- Part search --- */
|
||||||
|
.part-search-wrap {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.part-search {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.7rem 1rem;
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 10px;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.part-search:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.part-results {
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 0 0 10px 10px;
|
||||||
|
max-height: 300px;
|
||||||
|
overflow-y: auto;
|
||||||
|
z-index: 100;
|
||||||
|
box-shadow: 0 8px 30px rgba(0,0,0,0.4);
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.part-result-item {
|
||||||
|
padding: 0.6rem 1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.part-result-item:hover,
|
||||||
|
.part-result-item.part-result-active {
|
||||||
|
background: var(--bg-hover);
|
||||||
|
border-left: 3px solid var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.part-result-item .pri-number {
|
||||||
|
font-family: monospace;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.part-result-item .pri-name {
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
margin-left: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.part-result-item .pri-type {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
padding: 0.15rem 0.4rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pri-type.oem { background: rgba(59, 130, 246, 0.15); color: var(--info); }
|
||||||
|
.pri-type.aftermarket { background: rgba(245, 158, 11, 0.15); color: var(--warning); }
|
||||||
|
|
||||||
|
/* --- Cart table --- */
|
||||||
|
.cart-card {
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 10px;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-card h3 {
|
||||||
|
padding: 0.8rem 1rem;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-table th {
|
||||||
|
text-align: left;
|
||||||
|
padding: 0.5rem 0.6rem;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 0.75rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-table td {
|
||||||
|
padding: 0.5rem 0.6rem;
|
||||||
|
border-bottom: 1px solid rgba(42, 42, 58, 0.5);
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-table input {
|
||||||
|
background: var(--bg-primary);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 4px;
|
||||||
|
color: var(--text-primary);
|
||||||
|
padding: 0.3rem 0.4rem;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
width: 70px;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-table input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-table .cart-desc { max-width: 250px; }
|
||||||
|
.cart-table .cart-qty { width: 45px; text-align: center; }
|
||||||
|
.cart-table .cart-cost { width: 80px; }
|
||||||
|
.cart-table .cart-margin { width: 55px; }
|
||||||
|
.cart-table .cart-price { width: 80px; }
|
||||||
|
|
||||||
|
.cart-table .cart-remove {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: var(--danger);
|
||||||
|
cursor: pointer;
|
||||||
|
font-size: 1rem;
|
||||||
|
padding: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cart-empty {
|
||||||
|
text-align: center;
|
||||||
|
padding: 2rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Right sidebar: Invoice summary --- */
|
||||||
|
.pos-sidebar {
|
||||||
|
position: sticky;
|
||||||
|
top: 5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice-summary {
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 10px;
|
||||||
|
padding: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice-summary h3 {
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.05em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0.4rem 0;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-row.total {
|
||||||
|
border-top: 2px solid var(--accent);
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
padding-top: 0.8rem;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
font-weight: 700;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-row .sr-label { color: var(--text-secondary); }
|
||||||
|
.summary-row .sr-value { font-weight: 600; }
|
||||||
|
.summary-row.total .sr-value { color: var(--accent); }
|
||||||
|
|
||||||
|
.btn-facturar {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 1.2rem;
|
||||||
|
padding: 0.9rem;
|
||||||
|
font-size: 1rem;
|
||||||
|
background: linear-gradient(135deg, var(--accent), #ff4500);
|
||||||
|
border: none;
|
||||||
|
border-radius: 10px;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 700;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-facturar:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 6px 25px var(--accent-glow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-facturar:disabled {
|
||||||
|
opacity: 0.4;
|
||||||
|
cursor: not-allowed;
|
||||||
|
transform: none;
|
||||||
|
box-shadow: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice-notes {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 0.8rem;
|
||||||
|
padding: 0.5rem;
|
||||||
|
background: var(--bg-primary);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 6px;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 0.85rem;
|
||||||
|
resize: vertical;
|
||||||
|
min-height: 60px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invoice-notes:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- New customer modal --- */
|
||||||
|
.modal-overlay {
|
||||||
|
position: fixed;
|
||||||
|
top: 0; left: 0; right: 0; bottom: 0;
|
||||||
|
background: rgba(0,0,0,0.6);
|
||||||
|
z-index: 2000;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content {
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 1.5rem;
|
||||||
|
width: 450px;
|
||||||
|
max-width: 95vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-content h3 {
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-field {
|
||||||
|
margin-bottom: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-field label {
|
||||||
|
display: block;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
margin-bottom: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-field input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.5rem;
|
||||||
|
background: var(--bg-primary);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 6px;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 0.9rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-field input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.5rem;
|
||||||
|
justify-content: flex-end;
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Toast (reuse from captura) --- */
|
||||||
|
.toast {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 2rem;
|
||||||
|
right: 2rem;
|
||||||
|
padding: 0.8rem 1.5rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
color: #fff;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
z-index: 9999;
|
||||||
|
animation: toastIn 0.3s ease;
|
||||||
|
box-shadow: 0 4px 20px rgba(0,0,0,0.3);
|
||||||
|
}
|
||||||
|
.toast.success { background: var(--success); }
|
||||||
|
.toast.error { background: var(--danger); }
|
||||||
|
@keyframes toastIn {
|
||||||
|
from { transform: translateY(20px); opacity: 0; }
|
||||||
|
to { transform: translateY(0); opacity: 1; }
|
||||||
|
}
|
||||||
1
dashboard/pos.min.js
vendored
Normal file
1
dashboard/pos.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
262
dashboard/shared.min.css
vendored
Normal file
262
dashboard/shared.min.css
vendored
Normal file
@@ -0,0 +1,262 @@
|
|||||||
|
/* ============================================================
|
||||||
|
shared.css -- Common styles for all Nexus Autoparts pages
|
||||||
|
============================================================ */
|
||||||
|
|
||||||
|
/* --- Reset --- */
|
||||||
|
* {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- CSS Variables (union of all pages) --- */
|
||||||
|
:root {
|
||||||
|
--bg-primary: #0a0a0f;
|
||||||
|
--bg-secondary: #12121a;
|
||||||
|
--bg-card: #1a1a24;
|
||||||
|
--bg-hover: #252532;
|
||||||
|
--bg-tertiary: #1a1a25;
|
||||||
|
--accent: #ff6b35;
|
||||||
|
--accent-hover: #ff8555;
|
||||||
|
--accent-glow: rgba(255, 107, 53, 0.3);
|
||||||
|
--text-primary: #ffffff;
|
||||||
|
--text-secondary: #a0a0b0;
|
||||||
|
--border: #2a2a3a;
|
||||||
|
--success: #22c55e;
|
||||||
|
--warning: #f59e0b;
|
||||||
|
--info: #3b82f6;
|
||||||
|
--danger: #ff4444;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Base body --- */
|
||||||
|
body {
|
||||||
|
font-family: 'Inter', sans-serif;
|
||||||
|
background: var(--bg-primary);
|
||||||
|
color: var(--text-primary);
|
||||||
|
min-height: 100vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Shared Button Styles --- */
|
||||||
|
.btn {
|
||||||
|
padding: 0.7rem 1.5rem;
|
||||||
|
border-radius: 10px;
|
||||||
|
border: none;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
text-decoration: none;
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background: linear-gradient(135deg, var(--accent) 0%, #ff4500 100%);
|
||||||
|
color: white;
|
||||||
|
box-shadow: 0 4px 15px var(--accent-glow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary:hover {
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: 0 6px 25px var(--accent-glow);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary {
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
color: var(--text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-secondary:hover {
|
||||||
|
border-color: var(--accent);
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-back {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.5rem;
|
||||||
|
padding: 0.6rem 1.2rem;
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 10px;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
margin-bottom: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-back:hover {
|
||||||
|
border-color: var(--accent);
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Shared Animations --- */
|
||||||
|
@keyframes spin {
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
from { opacity: 0; }
|
||||||
|
to { opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Loading & Empty States --- */
|
||||||
|
.state-container {
|
||||||
|
text-align: center;
|
||||||
|
padding: 4rem 2rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.state-container i {
|
||||||
|
font-size: 4rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.state-container h4 {
|
||||||
|
color: var(--text-primary);
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Scrollbar Styling --- */
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
background: var(--bg-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--border);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Skip Link (accessibility) --- */
|
||||||
|
.skip-link {
|
||||||
|
position: absolute;
|
||||||
|
top: -50px;
|
||||||
|
left: 0;
|
||||||
|
background: var(--accent);
|
||||||
|
color: white;
|
||||||
|
padding: 0.75rem 1.5rem;
|
||||||
|
z-index: 3000;
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 600;
|
||||||
|
border-radius: 0 0 8px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.skip-link:focus {
|
||||||
|
top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Screen Reader Only --- */
|
||||||
|
.sr-only {
|
||||||
|
position: absolute;
|
||||||
|
width: 1px;
|
||||||
|
height: 1px;
|
||||||
|
padding: 0;
|
||||||
|
margin: -1px;
|
||||||
|
overflow: hidden;
|
||||||
|
clip: rect(0, 0, 0, 0);
|
||||||
|
white-space: nowrap;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Alert / Toast Styles --- */
|
||||||
|
.alert {
|
||||||
|
padding: 1rem 1.5rem;
|
||||||
|
border-radius: 8px;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.75rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-success {
|
||||||
|
background: rgba(0, 214, 143, 0.1);
|
||||||
|
border: 1px solid var(--success);
|
||||||
|
color: var(--success);
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-error {
|
||||||
|
background: rgba(255, 68, 68, 0.1);
|
||||||
|
border: 1px solid var(--danger);
|
||||||
|
color: var(--danger);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Modal Base Styles --- */
|
||||||
|
.modal-overlay {
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
bottom: 0;
|
||||||
|
background: rgba(0, 0, 0, 0.8);
|
||||||
|
z-index: 2000;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal-overlay.active {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Form Styles --- */
|
||||||
|
.form-group {
|
||||||
|
margin-bottom: 1.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-label {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 0.5rem;
|
||||||
|
font-weight: 500;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.75rem 1rem;
|
||||||
|
background: var(--bg-tertiary);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 0.95rem;
|
||||||
|
transition: border-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input:focus {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-input::placeholder {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Quality Badges --- */
|
||||||
|
.quality-badge {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 0.25rem 0.6rem;
|
||||||
|
border-radius: 12px;
|
||||||
|
font-size: 0.75rem;
|
||||||
|
font-weight: 600;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quality-economy { background: var(--warning); color: #000; }
|
||||||
|
.quality-standard { background: var(--info); color: white; }
|
||||||
|
.quality-premium { background: var(--success); color: white; }
|
||||||
|
.quality-oem { background: #9b59b6; color: white; }
|
||||||
678
dashboard/tienda.min.css
vendored
Normal file
678
dashboard/tienda.min.css
vendored
Normal file
@@ -0,0 +1,678 @@
|
|||||||
|
/* ============================================================
|
||||||
|
tienda.css -- Store / Tablet dashboard styles
|
||||||
|
Nexus Autoparts — tablet-first, touch-friendly
|
||||||
|
============================================================ */
|
||||||
|
|
||||||
|
/* --- Base overrides for tienda page --- */
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
font-family: 'DM Sans', sans-serif;
|
||||||
|
background: var(--bg-primary);
|
||||||
|
color: var(--text-primary);
|
||||||
|
min-height: 100vh;
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
-webkit-touch-callout: none;
|
||||||
|
overscroll-behavior: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Header --- */
|
||||||
|
.t-header {
|
||||||
|
position: fixed;
|
||||||
|
top: 0; left: 0; right: 0;
|
||||||
|
z-index: 100;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0.6rem 1.2rem;
|
||||||
|
background: rgba(18, 18, 26, 0.92);
|
||||||
|
backdrop-filter: blur(24px);
|
||||||
|
-webkit-backdrop-filter: blur(24px);
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-header-left {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.6rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-logo-mark {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
background: linear-gradient(135deg, var(--accent) 0%, #ff4500 100%);
|
||||||
|
border-radius: 9px;
|
||||||
|
box-shadow: 0 3px 14px var(--accent-glow);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-logo-mark::after {
|
||||||
|
content: '\2699\FE0F';
|
||||||
|
font-size: 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-brand {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
line-height: 1.1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-brand-name {
|
||||||
|
font-family: 'Outfit', sans-serif;
|
||||||
|
font-weight: 800;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
background: linear-gradient(135deg, #fff 0%, var(--accent) 100%);
|
||||||
|
-webkit-background-clip: text;
|
||||||
|
-webkit-text-fill-color: transparent;
|
||||||
|
background-clip: text;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-brand-sub {
|
||||||
|
font-size: 0.55rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
letter-spacing: 0.15em;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Header center: search --- */
|
||||||
|
.t-header-center {
|
||||||
|
flex: 1;
|
||||||
|
max-width: 420px;
|
||||||
|
margin: 0 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-search-box {
|
||||||
|
position: relative;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-search-icon {
|
||||||
|
position: absolute;
|
||||||
|
left: 0.7rem;
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-search-box input {
|
||||||
|
width: 100%;
|
||||||
|
padding: 0.55rem 0.8rem 0.55rem 2.2rem;
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 10px;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-family: 'DM Sans', sans-serif;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
outline: none;
|
||||||
|
transition: border-color 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-search-box input:focus {
|
||||||
|
border-color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-search-box input::placeholder {
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-search-results {
|
||||||
|
position: absolute;
|
||||||
|
top: calc(100% + 4px);
|
||||||
|
left: 0; right: 0;
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 10px;
|
||||||
|
max-height: 300px;
|
||||||
|
overflow-y: auto;
|
||||||
|
box-shadow: 0 12px 40px rgba(0,0,0,0.5);
|
||||||
|
display: none;
|
||||||
|
z-index: 200;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-search-results.active {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-search-result-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.6rem 0.8rem;
|
||||||
|
border-bottom: 1px solid var(--border);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-search-result-item:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-search-result-item:hover,
|
||||||
|
.t-search-result-item:active {
|
||||||
|
background: var(--bg-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-search-result-item .sri-number {
|
||||||
|
font-family: 'JetBrains Mono', monospace;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-search-result-item .sri-name {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
margin-left: 0.4rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Header right: clock --- */
|
||||||
|
.t-header-right {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-clock {
|
||||||
|
font-family: 'JetBrains Mono', monospace;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
letter-spacing: 0.03em;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Main --- */
|
||||||
|
.t-main {
|
||||||
|
padding: 4.2rem 1rem 1.5rem;
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- KPI Row --- */
|
||||||
|
.t-kpi-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
||||||
|
gap: 0.8rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-kpi {
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 0.9rem 1rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.8rem;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: transform 0.2s, box-shadow 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-kpi:active {
|
||||||
|
transform: scale(0.98);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Colored left accent bar */
|
||||||
|
.t-kpi::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 0; top: 0; bottom: 0;
|
||||||
|
width: 3px;
|
||||||
|
border-radius: 3px 0 0 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-kpi[data-color="accent"]::before { background: var(--accent); }
|
||||||
|
.t-kpi[data-color="success"]::before { background: var(--success); }
|
||||||
|
.t-kpi[data-color="info"]::before { background: var(--info); }
|
||||||
|
.t-kpi[data-color="warning"]::before { background: var(--warning); }
|
||||||
|
|
||||||
|
.t-kpi-icon {
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 10px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-kpi-icon svg {
|
||||||
|
width: 22px;
|
||||||
|
height: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-kpi[data-color="accent"] .t-kpi-icon { background: rgba(255, 107, 53, 0.12); color: var(--accent); }
|
||||||
|
.t-kpi[data-color="success"] .t-kpi-icon { background: rgba(34, 197, 94, 0.12); color: var(--success); }
|
||||||
|
.t-kpi[data-color="info"] .t-kpi-icon { background: rgba(59, 130, 246, 0.12); color: var(--info); }
|
||||||
|
.t-kpi[data-color="warning"] .t-kpi-icon { background: rgba(245, 158, 11, 0.12); color: var(--warning); }
|
||||||
|
|
||||||
|
.t-kpi-data {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-kpi-value {
|
||||||
|
font-family: 'Outfit', sans-serif;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 1.3rem;
|
||||||
|
line-height: 1.2;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-kpi-label {
|
||||||
|
font-size: 0.72rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-kpi-count {
|
||||||
|
font-size: 0.65rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-family: 'JetBrains Mono', monospace;
|
||||||
|
white-space: nowrap;
|
||||||
|
align-self: flex-start;
|
||||||
|
margin-top: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Content Grid --- */
|
||||||
|
.t-content {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Cards --- */
|
||||||
|
.t-card {
|
||||||
|
background: var(--bg-card);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-card-full {
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
margin-bottom: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-card-title {
|
||||||
|
font-family: 'DM Sans', sans-serif;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
font-weight: 600;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.04em;
|
||||||
|
margin-bottom: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-card-header .t-card-title {
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-see-all {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--accent);
|
||||||
|
text-decoration: none;
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 0.3rem 0.6rem;
|
||||||
|
border-radius: 6px;
|
||||||
|
transition: background 0.2s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-see-all:hover,
|
||||||
|
.t-see-all:active {
|
||||||
|
background: rgba(255, 107, 53, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Quick Actions Grid --- */
|
||||||
|
.t-actions-card {
|
||||||
|
padding-bottom: 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-actions-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
gap: 0.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-action {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.7rem;
|
||||||
|
padding: 0.8rem;
|
||||||
|
background: var(--bg-primary);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 10px;
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--text-primary);
|
||||||
|
font-size: 0.85rem;
|
||||||
|
font-weight: 600;
|
||||||
|
transition: transform 0.15s, background 0.2s, border-color 0.2s;
|
||||||
|
-webkit-tap-highlight-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-action:active {
|
||||||
|
transform: scale(0.96);
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-action:hover {
|
||||||
|
background: var(--bg-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-action-icon {
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
border-radius: 9px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-action-icon svg {
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-action[data-color="accent"] .t-action-icon { background: rgba(255, 107, 53, 0.12); color: var(--accent); }
|
||||||
|
.t-action[data-color="accent"]:hover { border-color: var(--accent); }
|
||||||
|
.t-action[data-color="info"] .t-action-icon { background: rgba(59, 130, 246, 0.12); color: var(--info); }
|
||||||
|
.t-action[data-color="info"]:hover { border-color: var(--info); }
|
||||||
|
.t-action[data-color="success"] .t-action-icon { background: rgba(34, 197, 94, 0.12); color: var(--success); }
|
||||||
|
.t-action[data-color="success"]:hover { border-color: var(--success); }
|
||||||
|
.t-action[data-color="warning"] .t-action-icon { background: rgba(245, 158, 11, 0.12); color: var(--warning); }
|
||||||
|
.t-action[data-color="warning"]:hover { border-color: var(--warning); }
|
||||||
|
|
||||||
|
/* --- Debtors List --- */
|
||||||
|
.t-debtors-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.4rem;
|
||||||
|
max-height: 280px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-debtor {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.6rem 0.7rem;
|
||||||
|
background: var(--bg-primary);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.15s, border-color 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-debtor:hover,
|
||||||
|
.t-debtor:active {
|
||||||
|
background: var(--bg-hover);
|
||||||
|
border-color: var(--danger);
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-debtor-name {
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-debtor-invoices {
|
||||||
|
font-size: 0.7rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-debtor-amount {
|
||||||
|
font-family: 'JetBrains Mono', monospace;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 0.9rem;
|
||||||
|
color: var(--danger);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Invoice List --- */
|
||||||
|
.t-invoice-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.4rem;
|
||||||
|
max-height: 320px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-invoice {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 0.6rem 0.7rem;
|
||||||
|
background: var(--bg-primary);
|
||||||
|
border: 1px solid var(--border);
|
||||||
|
border-radius: 8px;
|
||||||
|
transition: background 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-invoice:hover,
|
||||||
|
.t-invoice:active {
|
||||||
|
background: var(--bg-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-invoice-left {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 0.15rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-invoice-folio {
|
||||||
|
font-family: 'JetBrains Mono', monospace;
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
color: var(--accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-invoice-customer {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-invoice-right {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: flex-end;
|
||||||
|
gap: 0.15rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-invoice-total {
|
||||||
|
font-family: 'JetBrains Mono', monospace;
|
||||||
|
font-weight: 600;
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-invoice-status {
|
||||||
|
font-size: 0.65rem;
|
||||||
|
font-weight: 600;
|
||||||
|
padding: 0.15rem 0.45rem;
|
||||||
|
border-radius: 4px;
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: 0.03em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-invoice-status.paid {
|
||||||
|
background: rgba(34, 197, 94, 0.15);
|
||||||
|
color: var(--success);
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-invoice-status.pending {
|
||||||
|
background: rgba(245, 158, 11, 0.15);
|
||||||
|
color: var(--warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-invoice-status.partial {
|
||||||
|
background: rgba(59, 130, 246, 0.15);
|
||||||
|
color: var(--info);
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-invoice-status.cancelled {
|
||||||
|
background: rgba(255, 68, 68, 0.15);
|
||||||
|
color: var(--danger);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Today's Payments card --- */
|
||||||
|
.t-today-payments {
|
||||||
|
text-align: center;
|
||||||
|
padding: 0.5rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-today-amount {
|
||||||
|
font-family: 'Outfit', sans-serif;
|
||||||
|
font-weight: 800;
|
||||||
|
font-size: 2rem;
|
||||||
|
color: var(--success);
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-today-count {
|
||||||
|
font-size: 0.8rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
margin-top: 0.3rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Empty state --- */
|
||||||
|
.t-empty {
|
||||||
|
text-align: center;
|
||||||
|
padding: 1.5rem;
|
||||||
|
color: var(--text-secondary);
|
||||||
|
font-size: 0.85rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Scrollbar (minimal for touch) --- */
|
||||||
|
.t-debtors-list::-webkit-scrollbar,
|
||||||
|
.t-invoice-list::-webkit-scrollbar,
|
||||||
|
.t-search-results::-webkit-scrollbar {
|
||||||
|
width: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-debtors-list::-webkit-scrollbar-track,
|
||||||
|
.t-invoice-list::-webkit-scrollbar-track,
|
||||||
|
.t-search-results::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-debtors-list::-webkit-scrollbar-thumb,
|
||||||
|
.t-invoice-list::-webkit-scrollbar-thumb,
|
||||||
|
.t-search-results::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--border);
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Responsive --- */
|
||||||
|
|
||||||
|
/* Tablet landscape (default target) */
|
||||||
|
@media (max-width: 1024px) {
|
||||||
|
.t-main {
|
||||||
|
padding: 4rem 0.8rem 1.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-kpi-row {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tablet portrait / large phone */
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.t-header-center {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-main {
|
||||||
|
padding: 3.8rem 0.6rem 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-content {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-kpi-row {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
gap: 0.6rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-kpi {
|
||||||
|
padding: 0.7rem 0.8rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-kpi-value {
|
||||||
|
font-size: 1.1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-kpi-count {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-actions-grid {
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Small phone */
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.t-kpi-row {
|
||||||
|
grid-template-columns: 1fr 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-kpi-icon {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-kpi-icon svg {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-kpi-value {
|
||||||
|
font-size: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-actions-grid {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* --- Fade-in animation for cards --- */
|
||||||
|
@keyframes t-fadeIn {
|
||||||
|
from { opacity: 0; transform: translateY(8px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-kpi {
|
||||||
|
animation: t-fadeIn 0.4s ease both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-kpi:nth-child(1) { animation-delay: 0.05s; }
|
||||||
|
.t-kpi:nth-child(2) { animation-delay: 0.1s; }
|
||||||
|
.t-kpi:nth-child(3) { animation-delay: 0.15s; }
|
||||||
|
.t-kpi:nth-child(4) { animation-delay: 0.2s; }
|
||||||
|
|
||||||
|
.t-card {
|
||||||
|
animation: t-fadeIn 0.4s ease both;
|
||||||
|
animation-delay: 0.25s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-content .t-col:nth-child(2) .t-card {
|
||||||
|
animation-delay: 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.t-content .t-col:nth-child(2) .t-card:nth-child(2) {
|
||||||
|
animation-delay: 0.35s;
|
||||||
|
}
|
||||||
1
dashboard/tienda.min.js
vendored
Normal file
1
dashboard/tienda.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
!function(){"use strict";function t(t){return"$"+(parseFloat(t)||0).toFixed(2).replace(/\B(?=(\d{3})+(?!\d))/g,",")}function e(t){if(!t)return"";var e=document.createElement("div");return e.textContent=t,e.innerHTML}function n(){var t=new Date,e=t.getHours(),n=String(t.getMinutes()).padStart(2,"0"),i=e>=12?"PM":"AM";e=e%12||12,document.getElementById("clock").textContent=e+":"+n+" "+i}function i(){fetch("/api/tienda/stats").then((function(t){return t.json()})).then((function(n){var i=n.sales_today||{},a=n.sales_month||{},o=n.payments_today||{};document.getElementById("kpi-sales-today").textContent=t(i.total),document.getElementById("kpi-sales-count").textContent=(i.count||0)+" facturas",document.getElementById("kpi-month").textContent=t(a.total),document.getElementById("kpi-month-count").textContent=(a.count||0)+" facturas",document.getElementById("kpi-customers").textContent=n.total_customers||0,document.getElementById("kpi-parts-count").textContent=(n.total_parts||0)+" partes",document.getElementById("kpi-pending").textContent=t(n.pending_balance||0),document.getElementById("kpi-pending-count").textContent=(n.pending_invoices||0)+" facturas",document.getElementById("payments-today-amount").textContent=t(o.total),document.getElementById("payments-today-count").textContent=(o.count||0)+" pagos registrados",function(n){var i=document.getElementById("debtors-list");if(0===n.length)return void(i.innerHTML='<div class="t-empty">Sin cuentas pendientes</div>');i.innerHTML=n.map((function(n){var i=n.credit_limit>0?Math.round(n.balance/n.credit_limit*100):0;return'<a href="/cuentas" class="t-debtor"><div><div class="t-debtor-name">'+e(n.name)+"</div>"+(n.credit_limit>0?'<div class="t-debtor-invoices">'+i+"% de límite</div>":"")+'</div><span class="t-debtor-amount">'+t(n.balance)+"</span></a>"})).join("")}(n.top_debtors||[]),function(n){var i=document.getElementById("recent-invoices");if(0===n.length)return void(i.innerHTML='<div class="t-empty">Sin facturas recientes</div>');i.innerHTML=n.map((function(n){var i=n.status||"pending",a={pending:"Pendiente",paid:"Pagada",partial:"Parcial",cancelled:"Cancelada"};return'<div class="t-invoice"><div class="t-invoice-left"><span class="t-invoice-folio">'+e(n.folio)+'</span><span class="t-invoice-customer">'+e(n.customer_name)+'</span></div><div class="t-invoice-right"><span class="t-invoice-total">'+t(n.total)+'</span><span class="t-invoice-status '+i+'">'+(a[i]||i)+"</span></div></div>"})).join("")}(n.recent_invoices||[])})).catch((function(t){console.error("Error loading stats:",t)}))}n(),setInterval(n,3e4);var a=null,o=document.getElementById("global-search"),s=document.getElementById("global-results");o&&(o.addEventListener("input",(function(){clearTimeout(a);var t=this.value.trim();if(t.length<2)return s.classList.remove("active"),void(s.innerHTML="");a=setTimeout((function(){fetch("/api/pos/search-parts?q="+encodeURIComponent(t)).then((function(t){return t.json()})).then((function(n){0===n.length?s.innerHTML='<div style="padding:0.8rem;color:var(--text-secondary);font-size:0.85rem">Sin resultados para "'+e(t)+'"</div>':s.innerHTML=n.slice(0,8).map((function(t){return'<div class="t-search-result-item"><div><span class="sri-number">'+e(t.oem_part_number)+'</span><span class="sri-name">'+e(t.name_part)+"</span></div></div>"})).join(""),s.classList.add("active")}))}),250)})),o.addEventListener("blur",(function(){setTimeout((function(){s.classList.remove("active")}),200)})),o.addEventListener("focus",(function(){s.innerHTML.trim()&&s.classList.add("active")}))),i(),setInterval(i,12e4)}();
|
||||||
@@ -32,6 +32,21 @@ server {
|
|||||||
add_header X-Content-Type-Options nosniff always;
|
add_header X-Content-Type-Options nosniff always;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Auto-serve minified JS/CSS when available (transparent to templates)
|
||||||
|
location ~* ^(.+)\.js$ {
|
||||||
|
try_files $1.min.js $uri =404;
|
||||||
|
expires 6M;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
add_header X-Content-Type-Options nosniff always;
|
||||||
|
}
|
||||||
|
|
||||||
|
location ~* ^(.+)\.css$ {
|
||||||
|
try_files $1.min.css $uri =404;
|
||||||
|
expires 6M;
|
||||||
|
add_header Cache-Control "public, immutable";
|
||||||
|
add_header X-Content-Type-Options nosniff always;
|
||||||
|
}
|
||||||
|
|
||||||
location / {
|
location / {
|
||||||
proxy_pass http://nexus_main;
|
proxy_pass http://nexus_main;
|
||||||
proxy_set_header Host $host;
|
proxy_set_header Host $host;
|
||||||
|
|||||||
385
pos/static/css/chat.min.css
vendored
Normal file
385
pos/static/css/chat.min.css
vendored
Normal file
@@ -0,0 +1,385 @@
|
|||||||
|
/* ==========================================================================
|
||||||
|
NEXUS POS — AI Chat Widget
|
||||||
|
Uses design system tokens from tokens.css
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/* ─── Floating Button ─── */
|
||||||
|
|
||||||
|
.chat-fab {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 140px; /* above cart FAB and F-keys footer */
|
||||||
|
right: var(--space-5);
|
||||||
|
z-index: 8000;
|
||||||
|
width: 52px;
|
||||||
|
height: 52px;
|
||||||
|
border-radius: var(--radius-full);
|
||||||
|
border: none;
|
||||||
|
cursor: pointer;
|
||||||
|
background: var(--color-accent);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 1.5rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
box-shadow: var(--shadow-lg);
|
||||||
|
transition: transform var(--duration-fast) var(--ease-in-out),
|
||||||
|
background var(--duration-fast) var(--ease-in-out);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-fab:hover {
|
||||||
|
transform: scale(1.08);
|
||||||
|
background: var(--color-primary-hover, #e5952f);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-fab.has-unread::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 4px;
|
||||||
|
right: 4px;
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--color-error);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─── Chat Panel ─── */
|
||||||
|
|
||||||
|
.chat-panel {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 200px;
|
||||||
|
right: var(--space-5);
|
||||||
|
z-index: 8001;
|
||||||
|
width: 400px;
|
||||||
|
height: 520px;
|
||||||
|
max-height: calc(100vh - 100px);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background: var(--color-bg-elevated);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: var(--radius-xl);
|
||||||
|
box-shadow: var(--shadow-xl);
|
||||||
|
overflow: hidden;
|
||||||
|
transform: translateY(20px) scale(0.95);
|
||||||
|
opacity: 0;
|
||||||
|
pointer-events: none;
|
||||||
|
transition: transform var(--duration-normal) var(--ease-in-out),
|
||||||
|
opacity var(--duration-normal) var(--ease-in-out);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-panel.open {
|
||||||
|
transform: translateY(0) scale(1);
|
||||||
|
opacity: 1;
|
||||||
|
pointer-events: all;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─── Header ─── */
|
||||||
|
|
||||||
|
.chat-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: var(--space-3) var(--space-4);
|
||||||
|
background: var(--color-accent);
|
||||||
|
color: #fff;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-header h3 {
|
||||||
|
font-family: var(--font-heading);
|
||||||
|
font-size: var(--text-body);
|
||||||
|
font-weight: var(--font-weight-semibold);
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-header-close {
|
||||||
|
background: none;
|
||||||
|
border: none;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 1.2rem;
|
||||||
|
cursor: pointer;
|
||||||
|
padding: var(--space-1);
|
||||||
|
line-height: 1;
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-header-close:hover { opacity: 1; }
|
||||||
|
|
||||||
|
/* ─── Messages Area ─── */
|
||||||
|
|
||||||
|
.chat-messages {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
padding: var(--space-3);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─── Message Bubbles ─── */
|
||||||
|
|
||||||
|
.chat-msg {
|
||||||
|
max-width: 85%;
|
||||||
|
padding: var(--space-2) var(--space-3);
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
font-size: var(--text-body-sm, 0.875rem);
|
||||||
|
line-height: 1.45;
|
||||||
|
word-wrap: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-msg.user {
|
||||||
|
align-self: flex-end;
|
||||||
|
background: var(--color-accent);
|
||||||
|
color: #fff;
|
||||||
|
border-bottom-right-radius: var(--radius-sm, 4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-msg.ai {
|
||||||
|
align-self: flex-start;
|
||||||
|
background: var(--color-surface-2, rgba(255,255,255,0.06));
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
border-bottom-left-radius: var(--radius-sm, 4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─── Typing Indicator ─── */
|
||||||
|
|
||||||
|
.chat-typing {
|
||||||
|
align-self: flex-start;
|
||||||
|
display: none;
|
||||||
|
gap: 4px;
|
||||||
|
padding: var(--space-2) var(--space-3);
|
||||||
|
background: var(--color-surface-2, rgba(255,255,255,0.06));
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
border-bottom-left-radius: var(--radius-sm, 4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-typing.visible { display: flex; }
|
||||||
|
|
||||||
|
.chat-typing span {
|
||||||
|
width: 7px;
|
||||||
|
height: 7px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: var(--color-text-muted);
|
||||||
|
animation: chatBounce 1.2s infinite;
|
||||||
|
}
|
||||||
|
.chat-typing span:nth-child(2) { animation-delay: 0.2s; }
|
||||||
|
.chat-typing span:nth-child(3) { animation-delay: 0.4s; }
|
||||||
|
|
||||||
|
@keyframes chatBounce {
|
||||||
|
0%, 60%, 100% { transform: translateY(0); }
|
||||||
|
30% { transform: translateY(-5px); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─── Part Result Cards ─── */
|
||||||
|
|
||||||
|
.chat-parts {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-2);
|
||||||
|
margin-top: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-part-card {
|
||||||
|
background: var(--color-bg-elevated);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
padding: var(--space-2) var(--space-3);
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color var(--duration-fast) var(--ease-in-out),
|
||||||
|
background var(--duration-fast) var(--ease-in-out);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-part-card:hover {
|
||||||
|
border-color: var(--color-accent);
|
||||||
|
background: var(--color-bg-base);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-part-card .part-number {
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: var(--text-caption, 0.75rem);
|
||||||
|
color: var(--color-accent);
|
||||||
|
font-weight: var(--font-weight-semibold);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-part-card .part-name {
|
||||||
|
font-size: var(--text-body-sm, 0.875rem);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-part-card .part-stock {
|
||||||
|
font-size: var(--text-caption, 0.75rem);
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-part-card .part-stock.in-stock {
|
||||||
|
color: var(--color-success);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─── Input Area ─── */
|
||||||
|
|
||||||
|
.chat-input-area {
|
||||||
|
display: flex;
|
||||||
|
gap: var(--space-2);
|
||||||
|
padding: var(--space-3);
|
||||||
|
border-top: 1px solid var(--color-border);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-input {
|
||||||
|
flex: 1;
|
||||||
|
padding: var(--space-2) var(--space-3);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
background: var(--color-bg-base);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
font-size: var(--text-body-sm, 0.875rem);
|
||||||
|
font-family: var(--font-body);
|
||||||
|
resize: none;
|
||||||
|
outline: none;
|
||||||
|
min-height: 38px;
|
||||||
|
max-height: 80px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-input:focus {
|
||||||
|
border-color: var(--color-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-input::placeholder {
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-send-btn {
|
||||||
|
width: 38px;
|
||||||
|
height: 38px;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
border: none;
|
||||||
|
background: var(--color-accent);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 1.1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
transition: background var(--duration-fast) var(--ease-in-out);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-send-btn:hover { background: var(--color-primary-hover, #e5952f); }
|
||||||
|
.chat-send-btn:disabled { opacity: 0.5; cursor: not-allowed; }
|
||||||
|
|
||||||
|
/* ─── Camera Button (Photo identification) ─── */
|
||||||
|
|
||||||
|
.chat-cam-btn {
|
||||||
|
width: 38px;
|
||||||
|
height: 38px;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
background: var(--color-bg-base);
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
font-size: 1.1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
transition: background var(--duration-fast) var(--ease-in-out),
|
||||||
|
color var(--duration-fast) var(--ease-in-out),
|
||||||
|
border-color var(--duration-fast) var(--ease-in-out);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-cam-btn:hover {
|
||||||
|
border-color: var(--color-accent);
|
||||||
|
color: var(--color-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-msg-image img {
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─── Mic Button (Voice Input) ─── */
|
||||||
|
|
||||||
|
.chat-mic-btn {
|
||||||
|
width: 38px;
|
||||||
|
height: 38px;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
background: var(--color-bg-base);
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
font-size: 1.1rem;
|
||||||
|
cursor: pointer;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
flex-shrink: 0;
|
||||||
|
transition: background var(--duration-fast) var(--ease-in-out),
|
||||||
|
color var(--duration-fast) var(--ease-in-out),
|
||||||
|
border-color var(--duration-fast) var(--ease-in-out);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-mic-btn:hover {
|
||||||
|
border-color: var(--color-accent);
|
||||||
|
color: var(--color-accent);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-mic-btn.listening {
|
||||||
|
background: #f85149;
|
||||||
|
border-color: #f85149;
|
||||||
|
color: #fff;
|
||||||
|
animation: micPulse 1.4s infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes micPulse {
|
||||||
|
0%, 100% { box-shadow: 0 0 0 0 rgba(248, 81, 73, 0.4); }
|
||||||
|
50% { box-shadow: 0 0 0 10px rgba(248, 81, 73, 0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─── Voice Toast ─── */
|
||||||
|
|
||||||
|
.chat-voice-toast {
|
||||||
|
position: fixed;
|
||||||
|
bottom: 160px;
|
||||||
|
left: 50%;
|
||||||
|
transform: translateX(-50%) translateY(10px);
|
||||||
|
background: rgba(0, 0, 0, 0.8);
|
||||||
|
color: #fff;
|
||||||
|
padding: 8px 18px;
|
||||||
|
border-radius: var(--radius-md, 8px);
|
||||||
|
font-size: 0.85rem;
|
||||||
|
z-index: 9999;
|
||||||
|
opacity: 0;
|
||||||
|
transition: opacity 0.3s, transform 0.3s;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-voice-toast.visible {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(-50%) translateY(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─── Vehicle Info Banner ─── */
|
||||||
|
|
||||||
|
.chat-vehicle-banner {
|
||||||
|
margin-top: var(--space-2);
|
||||||
|
padding: var(--space-2) var(--space-3);
|
||||||
|
background: var(--color-bg-base);
|
||||||
|
border: 1px solid var(--color-accent);
|
||||||
|
border-left: 3px solid var(--color-accent);
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
font-size: var(--text-caption, 0.75rem);
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.chat-vehicle-banner strong {
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ─── Responsive ─── */
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.chat-panel {
|
||||||
|
width: calc(100vw - var(--space-4));
|
||||||
|
right: var(--space-2);
|
||||||
|
height: 60vh;
|
||||||
|
}
|
||||||
|
}
|
||||||
85
pos/static/css/common.min.css
vendored
Normal file
85
pos/static/css/common.min.css
vendored
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
/* /home/Autopartes/pos/static/css/common.css */
|
||||||
|
/* Theme variables — overridden by tenant theme */
|
||||||
|
:root {
|
||||||
|
--color-primary: #1a73e8;
|
||||||
|
--color-secondary: #5f6368;
|
||||||
|
--color-accent: #ff6b35;
|
||||||
|
--color-bg: #ffffff;
|
||||||
|
--color-surface: #f8f9fa;
|
||||||
|
--color-text: #202124;
|
||||||
|
--color-text-secondary: #5f6368;
|
||||||
|
--color-border: #dadce0;
|
||||||
|
--color-success: #34a853;
|
||||||
|
--color-warning: #f9ab00;
|
||||||
|
--color-error: #ea4335;
|
||||||
|
--font-display: 'Sora', sans-serif;
|
||||||
|
--font-body: 'Plus Jakarta Sans', sans-serif;
|
||||||
|
--font-mono: 'JetBrains Mono', monospace;
|
||||||
|
--radius: 8px;
|
||||||
|
--shadow: 0 1px 3px rgba(0,0,0,0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }
|
||||||
|
|
||||||
|
body {
|
||||||
|
font-family: var(--font-body);
|
||||||
|
background: var(--color-bg);
|
||||||
|
color: var(--color-text);
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 10px 20px;
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
font-family: var(--font-body);
|
||||||
|
font-size: 0.95rem;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
background: var(--color-surface);
|
||||||
|
color: var(--color-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn:hover { background: var(--color-border); }
|
||||||
|
.btn--primary { background: var(--color-primary); color: white; border-color: var(--color-primary); }
|
||||||
|
.btn--primary:hover { opacity: 0.9; }
|
||||||
|
.btn--accent { background: var(--color-accent); color: white; border-color: var(--color-accent); }
|
||||||
|
|
||||||
|
.card {
|
||||||
|
background: var(--color-surface);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
border-radius: var(--radius);
|
||||||
|
padding: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Catalog grid */
|
||||||
|
.catalog-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 16px; }
|
||||||
|
.catalog-card { cursor: pointer; transition: all 0.2s; }
|
||||||
|
.catalog-card:hover { border-color: var(--color-primary); transform: translateY(-2px); box-shadow: var(--shadow); }
|
||||||
|
.stock-badge { display: inline-block; padding: 2px 8px; border-radius: 12px; font-size: 0.75rem; font-weight: 600; }
|
||||||
|
.stock-badge--ok { background: #dcfce7; color: #166534; }
|
||||||
|
.stock-badge--low { background: #fef9c3; color: #854d0e; }
|
||||||
|
.stock-badge--zero { background: #fecaca; color: #991b1b; }
|
||||||
|
|
||||||
|
/* Cart sidebar */
|
||||||
|
.cart-sidebar { position: fixed; right: 0; top: 0; bottom: 0; width: 360px; background: var(--color-surface); border-left: 1px solid var(--color-border); padding: 20px; overflow-y: auto; transform: translateX(100%); transition: transform 0.3s; z-index: 50; }
|
||||||
|
.cart-sidebar.open { transform: translateX(0); }
|
||||||
|
.cart-item { display: flex; gap: 12px; padding: 12px 0; border-bottom: 1px solid var(--color-border); }
|
||||||
|
.cart-total { font-family: var(--font-mono); font-size: 1.3rem; font-weight: 700; }
|
||||||
|
|
||||||
|
/* Search bar */
|
||||||
|
.search-bar { display: flex; gap: 8px; margin-bottom: 20px; }
|
||||||
|
.search-bar input { flex: 1; padding: 10px 16px; border: 1px solid var(--color-border); border-radius: var(--radius); font-size: 1rem; }
|
||||||
|
.search-bar input:focus { outline: none; border-color: var(--color-primary); }
|
||||||
|
|
||||||
|
/* Filter chips */
|
||||||
|
.filter-chips { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 16px; }
|
||||||
|
.chip { padding: 4px 12px; border-radius: 20px; border: 1px solid var(--color-border); font-size: 0.8rem; cursor: pointer; background: transparent; }
|
||||||
|
.chip.active { background: var(--color-primary); color: white; border-color: var(--color-primary); }
|
||||||
|
|
||||||
|
/* External availability */
|
||||||
|
.external-results { background: #eff6ff; border: 1px solid #bfdbfe; border-radius: var(--radius); padding: 16px; margin-top: 12px; }
|
||||||
428
pos/static/css/onboarding.min.css
vendored
Normal file
428
pos/static/css/onboarding.min.css
vendored
Normal file
@@ -0,0 +1,428 @@
|
|||||||
|
/* ==========================================================================
|
||||||
|
NEXUS POS — Onboarding Wizard
|
||||||
|
Uses design system tokens (works with both industrial + modern themes)
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/* Overlay backdrop */
|
||||||
|
.onboarding-overlay {
|
||||||
|
position: fixed;
|
||||||
|
inset: 0;
|
||||||
|
z-index: var(--z-modal);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: var(--overlay-backdrop);
|
||||||
|
opacity: 0;
|
||||||
|
animation: onb-fade-in var(--duration-normal) var(--ease-out) forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes onb-fade-in {
|
||||||
|
to { opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Modal card */
|
||||||
|
.onboarding-modal {
|
||||||
|
width: 92vw;
|
||||||
|
max-width: 500px;
|
||||||
|
max-height: 90vh;
|
||||||
|
background: var(--color-bg-elevated);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
box-shadow: var(--shadow-xl);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
overflow: hidden;
|
||||||
|
transform: translateY(20px);
|
||||||
|
animation: onb-slide-up var(--duration-normal) var(--ease-out) forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="industrial"] .onboarding-modal {
|
||||||
|
border-radius: 0;
|
||||||
|
clip-path: polygon(0 0, calc(100% - 20px) 0, 100% 20px, 100% 100%, 0 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="modern"] .onboarding-modal {
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes onb-slide-up {
|
||||||
|
to { transform: translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Step content area */
|
||||||
|
.onboarding-body {
|
||||||
|
padding: var(--space-8) var(--space-6);
|
||||||
|
overflow-y: auto;
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Step icon */
|
||||||
|
.onb-icon {
|
||||||
|
width: 64px;
|
||||||
|
height: 64px;
|
||||||
|
margin: 0 auto var(--space-5);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 2rem;
|
||||||
|
background: var(--color-primary-muted);
|
||||||
|
color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="industrial"] .onb-icon {
|
||||||
|
border-radius: 0;
|
||||||
|
clip-path: polygon(0 0, calc(100% - 12px) 0, 100% 12px, 100% 100%, 0 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="modern"] .onb-icon {
|
||||||
|
border-radius: var(--radius-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Titles */
|
||||||
|
.onb-title {
|
||||||
|
font-family: var(--font-heading);
|
||||||
|
font-weight: var(--heading-weight-primary);
|
||||||
|
font-size: var(--text-h4);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
text-align: center;
|
||||||
|
margin-bottom: var(--space-2);
|
||||||
|
letter-spacing: var(--tracking-snug);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="industrial"] .onb-title {
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
||||||
|
|
||||||
|
.onb-desc {
|
||||||
|
font-size: var(--text-body-sm);
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
text-align: center;
|
||||||
|
line-height: var(--leading-body-sm);
|
||||||
|
margin-bottom: var(--space-6);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Form fields inside wizard */
|
||||||
|
.onb-form {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.onb-field {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.onb-label {
|
||||||
|
font-size: var(--text-label);
|
||||||
|
font-weight: var(--font-weight-semibold);
|
||||||
|
color: var(--color-text-secondary);
|
||||||
|
letter-spacing: var(--tracking-wide);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="industrial"] .onb-label {
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: var(--text-caption);
|
||||||
|
letter-spacing: var(--tracking-wider);
|
||||||
|
}
|
||||||
|
|
||||||
|
.onb-input {
|
||||||
|
height: 40px;
|
||||||
|
padding: 0 var(--space-3);
|
||||||
|
background: var(--color-bg-base);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
font-family: var(--font-body);
|
||||||
|
font-size: var(--text-body-sm);
|
||||||
|
transition: var(--transition-fast);
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="industrial"] .onb-input {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="modern"] .onb-input {
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.onb-input:focus {
|
||||||
|
border-color: var(--color-border-focus);
|
||||||
|
box-shadow: var(--shadow-focus);
|
||||||
|
}
|
||||||
|
|
||||||
|
.onb-input::placeholder {
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
}
|
||||||
|
|
||||||
|
.onb-select {
|
||||||
|
height: 40px;
|
||||||
|
padding: 0 var(--space-3);
|
||||||
|
background: var(--color-bg-base);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
font-family: var(--font-body);
|
||||||
|
font-size: var(--text-body-sm);
|
||||||
|
transition: var(--transition-fast);
|
||||||
|
outline: none;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="industrial"] .onb-select {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="modern"] .onb-select {
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.onb-select:focus {
|
||||||
|
border-color: var(--color-border-focus);
|
||||||
|
box-shadow: var(--shadow-focus);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Inline error */
|
||||||
|
.onb-error {
|
||||||
|
font-size: var(--text-caption);
|
||||||
|
color: var(--color-error);
|
||||||
|
min-height: 18px;
|
||||||
|
margin-top: var(--space-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Success message in step */
|
||||||
|
.onb-success {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-2);
|
||||||
|
padding: var(--space-3);
|
||||||
|
background: var(--color-success-light);
|
||||||
|
color: var(--color-success-dark);
|
||||||
|
font-size: var(--text-body-sm);
|
||||||
|
font-weight: var(--font-weight-semibold);
|
||||||
|
margin-top: var(--space-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="industrial"] .onb-success {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="modern"] .onb-success {
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Footer: buttons + progress */
|
||||||
|
.onboarding-footer {
|
||||||
|
padding: var(--space-4) var(--space-6);
|
||||||
|
border-top: 1px solid var(--color-border);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: var(--space-4);
|
||||||
|
background: var(--color-bg-elevated);
|
||||||
|
}
|
||||||
|
|
||||||
|
.onb-actions {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: var(--space-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Buttons */
|
||||||
|
.onb-btn {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: var(--space-2);
|
||||||
|
padding: 0 var(--space-5);
|
||||||
|
height: 40px;
|
||||||
|
font-family: var(--font-body);
|
||||||
|
font-size: var(--text-body-sm);
|
||||||
|
font-weight: var(--font-weight-semibold);
|
||||||
|
border: 1px solid transparent;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: var(--transition-fast);
|
||||||
|
white-space: nowrap;
|
||||||
|
letter-spacing: var(--tracking-wide);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="industrial"] .onb-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"] .onb-btn {
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.onb-btn--primary {
|
||||||
|
background: var(--btn-primary-bg);
|
||||||
|
color: var(--btn-primary-text);
|
||||||
|
}
|
||||||
|
|
||||||
|
.onb-btn--primary:hover {
|
||||||
|
background: var(--btn-primary-bg-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.onb-btn--primary:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.onb-btn--secondary {
|
||||||
|
background: var(--btn-secondary-bg);
|
||||||
|
color: var(--btn-secondary-text);
|
||||||
|
border-color: var(--btn-secondary-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
.onb-btn--secondary:hover {
|
||||||
|
background: var(--btn-secondary-bg-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
.onb-btn--ghost {
|
||||||
|
background: transparent;
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
border-color: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.onb-btn--ghost:hover {
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Progress dots */
|
||||||
|
.onb-progress {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.onb-dot {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
border: 2px solid var(--color-border-strong);
|
||||||
|
background: transparent;
|
||||||
|
transition: var(--transition-normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="industrial"] .onb-dot {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="modern"] .onb-dot {
|
||||||
|
border-radius: var(--radius-full);
|
||||||
|
}
|
||||||
|
|
||||||
|
.onb-dot.is-active {
|
||||||
|
background: var(--color-primary);
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
transform: scale(1.2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.onb-dot.is-done {
|
||||||
|
background: var(--color-primary-muted);
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Step counter label */
|
||||||
|
.onb-step-label {
|
||||||
|
font-size: var(--text-caption);
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
text-align: center;
|
||||||
|
letter-spacing: var(--tracking-wide);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* "No mostrar de nuevo" checkbox row */
|
||||||
|
.onb-dismiss-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
gap: var(--space-2);
|
||||||
|
padding-top: var(--space-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.onb-dismiss-row label {
|
||||||
|
font-size: var(--text-caption);
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.onb-dismiss-row input[type="checkbox"] {
|
||||||
|
accent-color: var(--color-primary);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Final step link grid */
|
||||||
|
.onb-links {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: var(--space-3);
|
||||||
|
margin-top: var(--space-4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.onb-link-card {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
gap: var(--space-2);
|
||||||
|
padding: var(--space-4) var(--space-3);
|
||||||
|
background: var(--color-bg-base);
|
||||||
|
border: 1px solid var(--color-border);
|
||||||
|
text-decoration: none;
|
||||||
|
color: var(--color-text-primary);
|
||||||
|
font-size: var(--text-body-sm);
|
||||||
|
font-weight: var(--font-weight-semibold);
|
||||||
|
transition: var(--transition-fast);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="industrial"] .onb-link-card {
|
||||||
|
border-radius: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="modern"] .onb-link-card {
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.onb-link-card:hover {
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
color: var(--color-primary);
|
||||||
|
transform: translateY(-2px);
|
||||||
|
box-shadow: var(--shadow-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.onb-link-card span.onb-link-icon {
|
||||||
|
font-size: 1.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Step transition */
|
||||||
|
.onb-step-enter {
|
||||||
|
animation: onb-step-in var(--duration-normal) var(--ease-out) forwards;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes onb-step-in {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateX(20px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateX(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Responsive */
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
.onboarding-modal {
|
||||||
|
width: 96vw;
|
||||||
|
max-height: 95vh;
|
||||||
|
}
|
||||||
|
.onboarding-body {
|
||||||
|
padding: var(--space-6) var(--space-4);
|
||||||
|
}
|
||||||
|
.onboarding-footer {
|
||||||
|
padding: var(--space-3) var(--space-4);
|
||||||
|
}
|
||||||
|
.onb-links {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
683
pos/static/css/pos-glass.min.css
vendored
Normal file
683
pos/static/css/pos-glass.min.css
vendored
Normal file
@@ -0,0 +1,683 @@
|
|||||||
|
/* ==========================================================================
|
||||||
|
POS-GLASS.CSS — Pixel-Perfect glassmorphism overlay for Nexus POS
|
||||||
|
Load AFTER tokens.css. Applies glass effects, glow, 3D buttons,
|
||||||
|
and animations to all POS pages without modifying inline styles.
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/* ── Hidden scrollbar (global) ── */
|
||||||
|
html { scrollbar-width: none; }
|
||||||
|
html::-webkit-scrollbar { width: 0; }
|
||||||
|
|
||||||
|
/* ── Smooth font rendering ── */
|
||||||
|
body {
|
||||||
|
-webkit-font-smoothing: antialiased;
|
||||||
|
-moz-osx-font-smoothing: grayscale;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
SIDEBAR — Glass treatment
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
.sidebar,
|
||||||
|
.pos-sidebar {
|
||||||
|
background: var(--glass-bg-strong) !important;
|
||||||
|
backdrop-filter: blur(20px);
|
||||||
|
-webkit-backdrop-filter: blur(20px);
|
||||||
|
border-right: 1px solid var(--glass-border) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar__logo {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar__logo-text {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Glow under logo text */
|
||||||
|
.sidebar__logo-text::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: -4px;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 2px;
|
||||||
|
background: var(--gradient-accent);
|
||||||
|
border-radius: 1px;
|
||||||
|
opacity: 0.4;
|
||||||
|
filter: blur(2px);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Nav items — hover glow */
|
||||||
|
.sidebar__nav a,
|
||||||
|
.sidebar__nav-item,
|
||||||
|
.sidebar .nav-item {
|
||||||
|
transition: all 0.25s var(--ease-out) !important;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar__nav a:hover,
|
||||||
|
.sidebar__nav-item:hover,
|
||||||
|
.sidebar .nav-item:hover {
|
||||||
|
box-shadow: 0 0 12px var(--glow-color-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar__nav a.active,
|
||||||
|
.sidebar__nav-item.active,
|
||||||
|
.sidebar .nav-item.active {
|
||||||
|
box-shadow: 0 0 16px var(--glow-color-soft), inset 0 0 0 1px var(--glass-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
THEME BAR — Glass
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
.theme-bar {
|
||||||
|
background: var(--glass-bg-strong) !important;
|
||||||
|
backdrop-filter: blur(16px);
|
||||||
|
-webkit-backdrop-filter: blur(16px);
|
||||||
|
border-bottom: 1px solid var(--glass-border) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
CARDS — Glass with glow hover
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
.kpi-card,
|
||||||
|
.table-card,
|
||||||
|
.card,
|
||||||
|
.stat-card,
|
||||||
|
.chart-card,
|
||||||
|
.alert-card,
|
||||||
|
.config-card,
|
||||||
|
.fleet-card,
|
||||||
|
.report-card,
|
||||||
|
.invoice-card,
|
||||||
|
.customer-card,
|
||||||
|
.panel {
|
||||||
|
background: var(--glass-bg) !important;
|
||||||
|
backdrop-filter: blur(var(--glass-blur));
|
||||||
|
-webkit-backdrop-filter: blur(var(--glass-blur));
|
||||||
|
border: 1px solid var(--glass-border) !important;
|
||||||
|
transition: all 0.3s var(--ease-out) !important;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Accent top-line on hover */
|
||||||
|
.kpi-card::before,
|
||||||
|
.table-card::before,
|
||||||
|
.chart-card::before {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
height: 2px;
|
||||||
|
background: var(--gradient-accent);
|
||||||
|
transform: scaleX(0);
|
||||||
|
transform-origin: left;
|
||||||
|
transition: transform 0.4s var(--ease-out);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kpi-card:hover::before,
|
||||||
|
.table-card:hover::before,
|
||||||
|
.chart-card:hover::before {
|
||||||
|
transform: scaleX(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.kpi-card:hover,
|
||||||
|
.table-card:hover,
|
||||||
|
.card:hover,
|
||||||
|
.stat-card:hover,
|
||||||
|
.chart-card:hover,
|
||||||
|
.config-card:hover,
|
||||||
|
.fleet-card:hover,
|
||||||
|
.report-card:hover {
|
||||||
|
border-color: var(--color-border-accent) !important;
|
||||||
|
box-shadow: 0 4px 20px var(--glow-color-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* KPI card accent bar — add glow */
|
||||||
|
.kpi-card__accent-bar {
|
||||||
|
box-shadow: 0 0 8px var(--glow-color-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
BUTTONS — 3D depth effect
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/* Primary buttons */
|
||||||
|
.btn--primary,
|
||||||
|
button.primary,
|
||||||
|
.btn-primary,
|
||||||
|
input[type="submit"],
|
||||||
|
button[type="submit"] {
|
||||||
|
background: var(--gradient-accent) !important;
|
||||||
|
border: none !important;
|
||||||
|
box-shadow: 0 3px 0 var(--color-primary-active),
|
||||||
|
0 4px 10px var(--glow-color-soft) !important;
|
||||||
|
transition: all 0.25s var(--ease-out) !important;
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn--primary:hover,
|
||||||
|
button.primary:hover,
|
||||||
|
.btn-primary:hover,
|
||||||
|
input[type="submit"]:hover,
|
||||||
|
button[type="submit"]:hover {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
box-shadow: 0 4px 0 var(--color-primary-active),
|
||||||
|
0 8px 20px var(--glow-color) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn--primary:active,
|
||||||
|
button.primary:active,
|
||||||
|
.btn-primary:active,
|
||||||
|
input[type="submit"]:active,
|
||||||
|
button[type="submit"]:active {
|
||||||
|
transform: translateY(1px);
|
||||||
|
box-shadow: 0 1px 0 var(--color-primary-active) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Ghost / secondary buttons — glass */
|
||||||
|
.btn--ghost,
|
||||||
|
.btn--secondary,
|
||||||
|
.btn-secondary,
|
||||||
|
.btn-ghost,
|
||||||
|
button.secondary {
|
||||||
|
background: var(--glass-bg) !important;
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
|
-webkit-backdrop-filter: blur(8px);
|
||||||
|
border: 1px solid var(--glass-border) !important;
|
||||||
|
transition: all 0.25s var(--ease-out) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn--ghost:hover,
|
||||||
|
.btn--secondary:hover,
|
||||||
|
.btn-secondary:hover,
|
||||||
|
.btn-ghost:hover,
|
||||||
|
button.secondary:hover {
|
||||||
|
border-color: var(--color-border-accent) !important;
|
||||||
|
box-shadow: 0 0 16px var(--glow-color-soft);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
INPUTS — Glass with focus glow
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
input[type="text"],
|
||||||
|
input[type="number"],
|
||||||
|
input[type="email"],
|
||||||
|
input[type="password"],
|
||||||
|
input[type="search"],
|
||||||
|
input[type="tel"],
|
||||||
|
input[type="date"],
|
||||||
|
input[type="url"],
|
||||||
|
textarea,
|
||||||
|
select,
|
||||||
|
.search-input,
|
||||||
|
.filter-input {
|
||||||
|
background: var(--glass-bg) !important;
|
||||||
|
border: 1px solid var(--glass-border) !important;
|
||||||
|
transition: all 0.25s var(--ease-out) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type="text"]:focus,
|
||||||
|
input[type="number"]:focus,
|
||||||
|
input[type="email"]:focus,
|
||||||
|
input[type="password"]:focus,
|
||||||
|
input[type="search"]:focus,
|
||||||
|
input[type="tel"]:focus,
|
||||||
|
input[type="date"]:focus,
|
||||||
|
input[type="url"]:focus,
|
||||||
|
textarea:focus,
|
||||||
|
select:focus,
|
||||||
|
.search-input:focus,
|
||||||
|
.filter-input:focus {
|
||||||
|
border-color: var(--color-border-focus) !important;
|
||||||
|
box-shadow: 0 0 0 3px var(--glow-color-soft), 0 0 16px var(--glow-color-soft) !important;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
TABLES — Subtle glass rows
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
table thead th {
|
||||||
|
background: var(--glass-bg) !important;
|
||||||
|
backdrop-filter: blur(8px);
|
||||||
|
font-family: var(--font-mono);
|
||||||
|
font-size: var(--text-caption);
|
||||||
|
text-transform: uppercase;
|
||||||
|
letter-spacing: var(--tracking-wider);
|
||||||
|
}
|
||||||
|
|
||||||
|
table tbody tr {
|
||||||
|
transition: all 0.2s ease !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table tbody tr:hover {
|
||||||
|
background: var(--glass-highlight) !important;
|
||||||
|
box-shadow: inset 0 0 0 1px var(--glass-border);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
MODALS — Glass overlay + glass content
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
.modal-overlay,
|
||||||
|
.overlay,
|
||||||
|
.modal-backdrop {
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
-webkit-backdrop-filter: blur(4px);
|
||||||
|
}
|
||||||
|
|
||||||
|
.modal,
|
||||||
|
.modal-content,
|
||||||
|
.modal-dialog,
|
||||||
|
.dialog {
|
||||||
|
background: var(--glass-bg-strong) !important;
|
||||||
|
backdrop-filter: blur(24px);
|
||||||
|
-webkit-backdrop-filter: blur(24px);
|
||||||
|
border: 1px solid var(--glass-border) !important;
|
||||||
|
box-shadow: 0 24px 48px rgba(0,0,0,0.3) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
TABS — Glass active state
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
.tab,
|
||||||
|
.tab-btn,
|
||||||
|
.tabs button {
|
||||||
|
transition: all 0.25s var(--ease-out) !important;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab.active,
|
||||||
|
.tab-btn.active,
|
||||||
|
.tabs button.active {
|
||||||
|
background: var(--color-primary-muted) !important;
|
||||||
|
box-shadow: 0 0 12px var(--glow-color-soft);
|
||||||
|
border-color: var(--color-border-accent) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
BADGES / TAGS — Subtle glow
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
.badge,
|
||||||
|
.tag,
|
||||||
|
.status-badge,
|
||||||
|
.pill {
|
||||||
|
backdrop-filter: blur(4px);
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
SCROLL REVEAL — Available for any POS page that wants it
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
.nx-reveal {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(24px);
|
||||||
|
transition: opacity 0.6s var(--ease-out), transform 0.6s var(--ease-out);
|
||||||
|
}
|
||||||
|
.nx-reveal.is-visible {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
|
}
|
||||||
|
.nx-stagger > .nx-reveal:nth-child(1) { transition-delay: 0ms; }
|
||||||
|
.nx-stagger > .nx-reveal:nth-child(2) { transition-delay: 80ms; }
|
||||||
|
.nx-stagger > .nx-reveal:nth-child(3) { transition-delay: 160ms; }
|
||||||
|
.nx-stagger > .nx-reveal:nth-child(4) { transition-delay: 240ms; }
|
||||||
|
.nx-stagger > .nx-reveal:nth-child(5) { transition-delay: 320ms; }
|
||||||
|
.nx-stagger > .nx-reveal:nth-child(6) { transition-delay: 400ms; }
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
TOAST / NOTIFICATIONS — Glass
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
.toast,
|
||||||
|
.notification,
|
||||||
|
.snackbar,
|
||||||
|
.alert {
|
||||||
|
background: var(--glass-bg-strong) !important;
|
||||||
|
backdrop-filter: blur(16px);
|
||||||
|
-webkit-backdrop-filter: blur(16px);
|
||||||
|
border: 1px solid var(--glass-border) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
DROPDOWN / POPOVER — Glass
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
.dropdown-menu,
|
||||||
|
.popover,
|
||||||
|
.autocomplete-list,
|
||||||
|
.suggestion-list {
|
||||||
|
background: var(--glass-bg-strong) !important;
|
||||||
|
backdrop-filter: blur(16px);
|
||||||
|
-webkit-backdrop-filter: blur(16px);
|
||||||
|
border: 1px solid var(--glass-border) !important;
|
||||||
|
box-shadow: 0 8px 32px rgba(0,0,0,0.2) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
STATUS BAR (POS) — Glass
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
.status-bar,
|
||||||
|
.pos-status-bar {
|
||||||
|
background: var(--glass-bg-strong) !important;
|
||||||
|
backdrop-filter: blur(16px);
|
||||||
|
-webkit-backdrop-filter: blur(16px);
|
||||||
|
border-bottom: 1px solid var(--glass-border) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
LOADING SPINNER — Glow animation
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
.spinner,
|
||||||
|
.loading-spinner {
|
||||||
|
animation: nx-glow-pulse 1.5s ease-in-out infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
ANIMATIONS — Available keyframes
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
@keyframes pos-fade-in {
|
||||||
|
from { opacity: 0; transform: translateY(12px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Apply subtle entry animation to main content area */
|
||||||
|
.content,
|
||||||
|
.main-content,
|
||||||
|
main {
|
||||||
|
animation: pos-fade-in 0.4s var(--ease-out) both;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
DASHED BORDER ACCENTS (Pixel-Perfect style)
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
.section-divider,
|
||||||
|
hr {
|
||||||
|
border: none;
|
||||||
|
border-top: 1px dashed var(--glass-border);
|
||||||
|
margin: var(--space-4) 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
TABLET RESPONSIVE — Adaptive layout for 768px-1024px screens
|
||||||
|
Applied globally to all POS pages via pos-glass.css.
|
||||||
|
Targets iPad (768×1024), Android tablets (800×1280), and similar.
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/* ── Tablet portrait (768-1023px) — sidebar collapses, grids reflow ── */
|
||||||
|
@media (max-width: 1023px) {
|
||||||
|
|
||||||
|
/* Sidebar collapses to an overlay drawer */
|
||||||
|
.sidebar,
|
||||||
|
.pos-sidebar {
|
||||||
|
position: fixed !important;
|
||||||
|
top: 0 !important;
|
||||||
|
left: 0 !important;
|
||||||
|
bottom: 0 !important;
|
||||||
|
z-index: var(--z-modal) !important;
|
||||||
|
transform: translateX(-100%) !important;
|
||||||
|
transition: transform 0.3s var(--ease-out) !important;
|
||||||
|
width: 260px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar.open,
|
||||||
|
.pos-sidebar.open {
|
||||||
|
transform: translateX(0) !important;
|
||||||
|
box-shadow: 0 0 40px rgba(0,0,0,0.3) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-overlay {
|
||||||
|
display: none !important;
|
||||||
|
position: fixed !important;
|
||||||
|
inset: 0 !important;
|
||||||
|
z-index: calc(var(--z-modal) - 1) !important;
|
||||||
|
background: rgba(0,0,0,0.5) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar-overlay.open {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* App shell: full width when sidebar is hidden */
|
||||||
|
.app-shell {
|
||||||
|
flex-direction: column !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-shell > main,
|
||||||
|
.app-shell > .main-content,
|
||||||
|
.app-shell > .content,
|
||||||
|
.main-content,
|
||||||
|
.content {
|
||||||
|
margin-left: 0 !important;
|
||||||
|
width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Show hamburger button */
|
||||||
|
.hamburger-btn {
|
||||||
|
display: flex !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Touch-friendly targets — minimum 44px tap area */
|
||||||
|
button,
|
||||||
|
.btn,
|
||||||
|
.nav-card,
|
||||||
|
.tab-btn,
|
||||||
|
.tab,
|
||||||
|
.part-card,
|
||||||
|
.search-result-item,
|
||||||
|
table tbody tr,
|
||||||
|
.kpi-card {
|
||||||
|
min-height: 44px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Larger text for readability on tablets */
|
||||||
|
.kpi-card__value {
|
||||||
|
font-size: 1.5rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Grid reflow: 2 columns instead of 3-4 */
|
||||||
|
.kpi-grid {
|
||||||
|
grid-template-columns: repeat(2, 1fr) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.nav-grid {
|
||||||
|
grid-template-columns: repeat(2, 1fr) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tables: horizontal scroll wrapper on narrow screens */
|
||||||
|
.table-wrap,
|
||||||
|
.table-card {
|
||||||
|
overflow-x: auto !important;
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* POS-specific: if the POS has a side panel (cart), stack vertically */
|
||||||
|
.pos-layout {
|
||||||
|
flex-direction: column !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.pos-layout .pos-cart,
|
||||||
|
.pos-layout .cart-panel {
|
||||||
|
width: 100% !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
height: auto !important;
|
||||||
|
max-height: 40vh !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Content headers: tighter padding */
|
||||||
|
.content-header,
|
||||||
|
.header,
|
||||||
|
.page-header {
|
||||||
|
padding: var(--space-3) var(--space-4) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Search bar: full width */
|
||||||
|
.search-bar,
|
||||||
|
.search-wrapper {
|
||||||
|
width: 100% !important;
|
||||||
|
max-width: 100% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Mode toggle: slightly larger buttons for touch */
|
||||||
|
.mode-toggle button {
|
||||||
|
padding: 6px 14px !important;
|
||||||
|
font-size: 12px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Vehicle selector dropdowns: stack on smaller tablets */
|
||||||
|
.vehicle-selector__inner,
|
||||||
|
.vehicle-selector .vs-group {
|
||||||
|
flex-wrap: wrap !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-selector .vs-arrow {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vehicle-selector .vs-select {
|
||||||
|
min-width: 130px !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Phone portrait (< 768px) — single column, max simplification ── */
|
||||||
|
@media (max-width: 767px) {
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
width: 85vw !important;
|
||||||
|
max-width: 300px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kpi-grid,
|
||||||
|
.nav-grid,
|
||||||
|
.results-grid {
|
||||||
|
grid-template-columns: 1fr !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.kpi-card__value {
|
||||||
|
font-size: 1.3rem !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Stack the mode toggle buttons vertically if tight */
|
||||||
|
.mode-toggle {
|
||||||
|
flex-wrap: wrap !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Hide non-essential UI to save space */
|
||||||
|
.header__store-badge,
|
||||||
|
.vs-vin-divider {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Full-width modals */
|
||||||
|
.modal-content {
|
||||||
|
max-width: 95vw !important;
|
||||||
|
margin: var(--space-3) !important;
|
||||||
|
padding: var(--space-4) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Tables: force readable font size */
|
||||||
|
table {
|
||||||
|
font-size: 12px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
table th,
|
||||||
|
table td {
|
||||||
|
padding: var(--space-2) var(--space-2) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Landscape tablet (height < 600px with wide screen) ── */
|
||||||
|
@media (max-height: 600px) and (min-width: 768px) {
|
||||||
|
/* Reduce vertical padding for landscape tablet use */
|
||||||
|
.kpi-grid {
|
||||||
|
gap: var(--space-2) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dashboard,
|
||||||
|
.main-content,
|
||||||
|
.content {
|
||||||
|
padding: var(--space-3) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ── Touch device hints ── */
|
||||||
|
@media (hover: none) and (pointer: coarse) {
|
||||||
|
/* Remove hover-only effects on touch devices — they cause sticky states */
|
||||||
|
.kpi-card:hover,
|
||||||
|
.nav-card:hover,
|
||||||
|
.part-card:hover,
|
||||||
|
.table-card:hover,
|
||||||
|
.card:hover {
|
||||||
|
transform: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Larger touch targets for interactive elements */
|
||||||
|
.sidebar__nav a,
|
||||||
|
.sidebar__nav-item,
|
||||||
|
.sidebar .nav-item {
|
||||||
|
padding: 12px 16px !important;
|
||||||
|
min-height: 48px !important;
|
||||||
|
display: flex !important;
|
||||||
|
align-items: center !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Scroll momentum on iOS */
|
||||||
|
.table-wrap,
|
||||||
|
.main-content,
|
||||||
|
.content,
|
||||||
|
.parts-grid,
|
||||||
|
.nav-grid {
|
||||||
|
-webkit-overflow-scrolling: touch;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Disable text selection on buttons (prevents accidental blue highlight on long tap) */
|
||||||
|
button,
|
||||||
|
.btn,
|
||||||
|
.nav-card,
|
||||||
|
.tab-btn {
|
||||||
|
-webkit-user-select: none;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
PRINT — Disable glass effects for printing
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
.sidebar,
|
||||||
|
.theme-bar,
|
||||||
|
.kpi-card,
|
||||||
|
.table-card,
|
||||||
|
.card,
|
||||||
|
.modal,
|
||||||
|
.modal-content,
|
||||||
|
table thead th,
|
||||||
|
input,
|
||||||
|
select,
|
||||||
|
textarea {
|
||||||
|
background: #fff !important;
|
||||||
|
backdrop-filter: none !important;
|
||||||
|
-webkit-backdrop-filter: none !important;
|
||||||
|
box-shadow: none !important;
|
||||||
|
border-color: #ccc !important;
|
||||||
|
color: #000 !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
627
pos/static/css/tokens.min.css
vendored
Normal file
627
pos/static/css/tokens.min.css
vendored
Normal file
@@ -0,0 +1,627 @@
|
|||||||
|
/* ==========================================================================
|
||||||
|
NEXUS AUTOPARTS — Design Tokens
|
||||||
|
POS System for Auto Parts Stores
|
||||||
|
Version: 1.0.0
|
||||||
|
==========================================================================
|
||||||
|
Themes:
|
||||||
|
- [data-theme="industrial"] — Industrial Robusto (Dark)
|
||||||
|
- [data-theme="modern"] — Técnico Moderno (Light)
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/* --------------------------------------------------------------------------
|
||||||
|
GOOGLE FONTS IMPORTS
|
||||||
|
-------------------------------------------------------------------------- */
|
||||||
|
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=Barlow:wght@400;700&family=Barlow+Condensed:wght@600;800&family=Poppins:wght@300;400;600;700&display=swap');
|
||||||
|
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
GLOBAL TOKENS — Theme-independent, shared across both themes
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
:root {
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------
|
||||||
|
SEMANTIC COLORS — Status / Feedback (shared)
|
||||||
|
------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
--color-success: #22c55e;
|
||||||
|
--color-success-light: #bbf7d0;
|
||||||
|
--color-success-dark: #15803d;
|
||||||
|
|
||||||
|
--color-warning: #eab308;
|
||||||
|
--color-warning-light: #fef08a;
|
||||||
|
--color-warning-dark: #a16207;
|
||||||
|
|
||||||
|
--color-error: #ef4444;
|
||||||
|
--color-error-light: #fecaca;
|
||||||
|
--color-error-dark: #b91c1c;
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------
|
||||||
|
NEUTRAL SCALE — Grey ramp (50–900)
|
||||||
|
------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
--color-neutral-50: #fafafa;
|
||||||
|
--color-neutral-100: #f5f5f5;
|
||||||
|
--color-neutral-200: #e5e5e5;
|
||||||
|
--color-neutral-300: #d4d4d4;
|
||||||
|
--color-neutral-400: #a3a3a3;
|
||||||
|
--color-neutral-500: #737373;
|
||||||
|
--color-neutral-600: #525252;
|
||||||
|
--color-neutral-700: #404040;
|
||||||
|
--color-neutral-800: #262626;
|
||||||
|
--color-neutral-900: #171717;
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------
|
||||||
|
SPACING — 4px base grid
|
||||||
|
------------------------------------------------------------------------ */
|
||||||
|
/* --space-N = N × 4px */
|
||||||
|
|
||||||
|
--space-1: 4px; /* 4px */
|
||||||
|
--space-2: 8px; /* 8px */
|
||||||
|
--space-3: 12px; /* 12px */
|
||||||
|
--space-4: 16px; /* 16px */
|
||||||
|
--space-5: 20px; /* 20px */
|
||||||
|
--space-6: 24px; /* 24px */
|
||||||
|
--space-7: 28px; /* 28px */
|
||||||
|
--space-8: 32px; /* 32px */
|
||||||
|
--space-9: 36px; /* 36px */
|
||||||
|
--space-10: 40px; /* 40px */
|
||||||
|
--space-11: 44px; /* 44px */
|
||||||
|
--space-12: 48px; /* 48px */
|
||||||
|
--space-14: 56px; /* 56px */
|
||||||
|
--space-16: 64px; /* 64px */
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------
|
||||||
|
BORDER RADIUS
|
||||||
|
------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
--radius-sm: 4px;
|
||||||
|
--radius-md: 8px;
|
||||||
|
--radius-lg: 12px;
|
||||||
|
--radius-xl: 20px;
|
||||||
|
--radius-full: 9999px;
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------
|
||||||
|
TRANSITIONS
|
||||||
|
------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
--transition-fast: all 0.10s ease;
|
||||||
|
--transition-normal: all 0.20s ease;
|
||||||
|
--transition-slow: all 0.40s ease;
|
||||||
|
|
||||||
|
/* Easing functions for fine-grained control */
|
||||||
|
--ease-in-out: cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
--ease-out: cubic-bezier(0, 0, 0.2, 1);
|
||||||
|
--ease-in: cubic-bezier(0.4, 0, 1, 1);
|
||||||
|
|
||||||
|
--duration-fast: 100ms;
|
||||||
|
--duration-normal: 200ms;
|
||||||
|
--duration-slow: 400ms;
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------
|
||||||
|
Z-INDEX SCALE
|
||||||
|
------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
--z-dropdown: 1000;
|
||||||
|
--z-sticky: 1020;
|
||||||
|
--z-modal: 1050;
|
||||||
|
--z-toast: 1080;
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------
|
||||||
|
BREAKPOINTS — Reference only (use in media queries, not calc())
|
||||||
|
sm: 640px
|
||||||
|
md: 768px
|
||||||
|
lg: 1024px
|
||||||
|
xl: 1280px
|
||||||
|
------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
THEME A — Industrial Robusto (Dark)
|
||||||
|
Usage: <html data-theme="industrial"> or <body data-theme="industrial">
|
||||||
|
Style: Industrial, robust, high-contrast amber accents, clip-path diagonals
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
[data-theme="industrial"] {
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------
|
||||||
|
PRIMITIVE COLORS
|
||||||
|
------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
--color-primary: #F5A623; /* Amber gold — main brand accent */
|
||||||
|
--color-primary-hover: #e8951a; /* Darker amber on hover */
|
||||||
|
--color-primary-active: #d4850f; /* Pressed state */
|
||||||
|
--color-primary-muted: rgba(245, 166, 35, 0.15); /* Subtle tint */
|
||||||
|
|
||||||
|
--color-secondary: #333333; /* Mid-dark border / secondary bg */
|
||||||
|
--color-secondary-hover: #444444;
|
||||||
|
|
||||||
|
--color-accent: #F5A623; /* Same as primary in this theme */
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------
|
||||||
|
BACKGROUNDS
|
||||||
|
------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
--color-bg-base: #0d0d0d; /* Page / app shell background */
|
||||||
|
--color-bg-elevated: #1a1a1a; /* Cards, panels, sidebars */
|
||||||
|
--color-bg-overlay: #252525; /* Modals, dropdowns, tooltips */
|
||||||
|
|
||||||
|
/* Surface levels (for layered UI) */
|
||||||
|
--color-surface-1: #1a1a1a; /* Lowest raised surface */
|
||||||
|
--color-surface-2: #252525; /* Mid-level surface */
|
||||||
|
--color-surface-3: #303030; /* Highest raised surface */
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------
|
||||||
|
TEXT
|
||||||
|
------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
--color-text-primary: #FFFFFF;
|
||||||
|
--color-text-secondary: #CCCCCC;
|
||||||
|
--color-text-muted: #888888;
|
||||||
|
--color-text-disabled: #555555;
|
||||||
|
--color-text-inverse: #000000; /* Text on amber background */
|
||||||
|
--color-text-accent: #F5A623;
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------
|
||||||
|
BORDERS
|
||||||
|
------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
--color-border: #333333;
|
||||||
|
--color-border-strong: #555555;
|
||||||
|
--color-border-accent: #F5A623;
|
||||||
|
--color-border-focus: #F5A623;
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------
|
||||||
|
BUTTONS
|
||||||
|
------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
/* Primary button */
|
||||||
|
--btn-primary-bg: #F5A623;
|
||||||
|
--btn-primary-bg-hover: #e8951a;
|
||||||
|
--btn-primary-bg-active: #d4850f;
|
||||||
|
--btn-primary-text: #000000;
|
||||||
|
--btn-primary-border: transparent;
|
||||||
|
|
||||||
|
/* Secondary button */
|
||||||
|
--btn-secondary-bg: transparent;
|
||||||
|
--btn-secondary-bg-hover: rgba(245, 166, 35, 0.10);
|
||||||
|
--btn-secondary-text: #F5A623;
|
||||||
|
--btn-secondary-border: #F5A623;
|
||||||
|
|
||||||
|
/* Ghost / Danger */
|
||||||
|
--btn-ghost-bg: transparent;
|
||||||
|
--btn-ghost-text: #CCCCCC;
|
||||||
|
--btn-ghost-border: #333333;
|
||||||
|
|
||||||
|
--btn-danger-bg: #ef4444;
|
||||||
|
--btn-danger-text: #FFFFFF;
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------
|
||||||
|
TYPOGRAPHY
|
||||||
|
------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
/* Font families */
|
||||||
|
--font-heading: 'Barlow Condensed', 'Arial Narrow', sans-serif;
|
||||||
|
--font-body: 'Barlow', 'Arial', sans-serif;
|
||||||
|
--font-mono: 'Courier New', 'Consolas', monospace; /* prices / SKUs */
|
||||||
|
|
||||||
|
/* Font weights */
|
||||||
|
--font-weight-light: 300; /* n/a in Barlow — falls to 400 */
|
||||||
|
--font-weight-regular: 400;
|
||||||
|
--font-weight-semibold: 600;
|
||||||
|
--font-weight-bold: 700;
|
||||||
|
--font-weight-extrabold: 800;
|
||||||
|
|
||||||
|
/* Heading weights (Barlow Condensed) */
|
||||||
|
--heading-weight-primary: 800;
|
||||||
|
--heading-weight-secondary: 600;
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------
|
||||||
|
SHADOWS / ELEVATION
|
||||||
|
Tinted with amber to feel cohesive with the theme
|
||||||
|
------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
--shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.60),
|
||||||
|
0 1px 2px rgba(0, 0, 0, 0.40);
|
||||||
|
|
||||||
|
--shadow-md: 0 4px 6px rgba(0, 0, 0, 0.60),
|
||||||
|
0 2px 4px rgba(0, 0, 0, 0.40);
|
||||||
|
|
||||||
|
--shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.70),
|
||||||
|
0 4px 6px rgba(0, 0, 0, 0.50);
|
||||||
|
|
||||||
|
--shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.80),
|
||||||
|
0 10px 10px rgba(0, 0, 0, 0.50);
|
||||||
|
|
||||||
|
/* Accent glow — use on focused/highlighted elements */
|
||||||
|
--shadow-accent: 0 0 0 3px rgba(245, 166, 35, 0.40);
|
||||||
|
--shadow-focus: 0 0 0 3px rgba(245, 166, 35, 0.50);
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------
|
||||||
|
MISC UI
|
||||||
|
------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
--scrollbar-track: #1a1a1a;
|
||||||
|
--scrollbar-thumb: #444444;
|
||||||
|
--scrollbar-thumb-hover: #F5A623;
|
||||||
|
|
||||||
|
--overlay-backdrop: rgba(0, 0, 0, 0.75);
|
||||||
|
|
||||||
|
/* Industrial clip-path angle (use in clip-path: polygon(...) utilities) */
|
||||||
|
--clip-diagonal-angle: 6deg;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
THEME B — Técnico Moderno (Light)
|
||||||
|
Usage: <html data-theme="modern"> or <body data-theme="modern">
|
||||||
|
Style: Clean, modern, Poppins typography, subtle dot-grid background
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
[data-theme="modern"] {
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------
|
||||||
|
PRIMITIVE COLORS
|
||||||
|
------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
--color-primary: #FF6B35; /* Orange — main brand accent */
|
||||||
|
--color-primary-hover: #f05a22; /* Darker on hover */
|
||||||
|
--color-primary-active: #dc4a12; /* Pressed state */
|
||||||
|
--color-primary-muted: rgba(255, 107, 53, 0.10); /* Subtle tint */
|
||||||
|
|
||||||
|
--color-secondary: #1a1a2e; /* Deep navy — used for strong text */
|
||||||
|
--color-secondary-hover: #252545;
|
||||||
|
|
||||||
|
--color-accent: #FF6B35; /* Same as primary in this theme */
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------
|
||||||
|
BACKGROUNDS
|
||||||
|
------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
--color-bg-base: #FFFFFF; /* Page / app shell background */
|
||||||
|
--color-bg-elevated: #F8F9FF; /* Cards, panels — very subtle blue */
|
||||||
|
--color-bg-overlay: #FFFFFF; /* Modals, dropdowns */
|
||||||
|
|
||||||
|
/* Surface levels */
|
||||||
|
--color-surface-1: #F8F9FF;
|
||||||
|
--color-surface-2: #F0F2FF;
|
||||||
|
--color-surface-3: #E8EBFF;
|
||||||
|
|
||||||
|
/* Dot-grid background pattern (apply via background-image on body/shell) */
|
||||||
|
/* background-image: radial-gradient(circle, var(--dot-grid-color) 1px, transparent 1px); */
|
||||||
|
/* background-size: var(--dot-grid-size) var(--dot-grid-size); */
|
||||||
|
--dot-grid-color: rgba(26, 26, 46, 0.07);
|
||||||
|
--dot-grid-size: 24px;
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------
|
||||||
|
TEXT
|
||||||
|
------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
--color-text-primary: #1a1a2e;
|
||||||
|
--color-text-secondary: #4a4a6a;
|
||||||
|
--color-text-muted: #8080a0;
|
||||||
|
--color-text-disabled: #b0b0c8;
|
||||||
|
--color-text-inverse: #FFFFFF; /* Text on orange background */
|
||||||
|
--color-text-accent: #FF6B35;
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------
|
||||||
|
BORDERS
|
||||||
|
------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
--color-border: #e2e4f0;
|
||||||
|
--color-border-strong: #c8cadc;
|
||||||
|
--color-border-accent: #FF6B35;
|
||||||
|
--color-border-focus: #FF6B35;
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------
|
||||||
|
BUTTONS
|
||||||
|
------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
/* Primary button */
|
||||||
|
--btn-primary-bg: #FF6B35;
|
||||||
|
--btn-primary-bg-hover: #f05a22;
|
||||||
|
--btn-primary-bg-active: #dc4a12;
|
||||||
|
--btn-primary-text: #FFFFFF;
|
||||||
|
--btn-primary-border: transparent;
|
||||||
|
|
||||||
|
/* Secondary button */
|
||||||
|
--btn-secondary-bg: transparent;
|
||||||
|
--btn-secondary-bg-hover: rgba(255, 107, 53, 0.08);
|
||||||
|
--btn-secondary-text: #FF6B35;
|
||||||
|
--btn-secondary-border: #FF6B35;
|
||||||
|
|
||||||
|
/* Ghost / Danger */
|
||||||
|
--btn-ghost-bg: transparent;
|
||||||
|
--btn-ghost-text: #4a4a6a;
|
||||||
|
--btn-ghost-border: #e2e4f0;
|
||||||
|
|
||||||
|
--btn-danger-bg: #ef4444;
|
||||||
|
--btn-danger-text: #FFFFFF;
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------
|
||||||
|
TYPOGRAPHY
|
||||||
|
------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
/* Font families */
|
||||||
|
--font-heading: 'Poppins', 'Segoe UI', sans-serif;
|
||||||
|
--font-body: 'Poppins', 'Segoe UI', sans-serif;
|
||||||
|
--font-mono: 'Courier New', 'Consolas', monospace; /* prices / SKUs */
|
||||||
|
|
||||||
|
/* Font weights */
|
||||||
|
--font-weight-light: 300;
|
||||||
|
--font-weight-regular: 400;
|
||||||
|
--font-weight-semibold: 600;
|
||||||
|
--font-weight-bold: 700;
|
||||||
|
--font-weight-extrabold: 800; /* falls to 700 in Poppins */
|
||||||
|
|
||||||
|
/* Heading weights (Poppins) */
|
||||||
|
--heading-weight-primary: 700;
|
||||||
|
--heading-weight-secondary: 600;
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------
|
||||||
|
SHADOWS / ELEVATION
|
||||||
|
Softer, cooler tints for the light theme
|
||||||
|
------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
--shadow-sm: 0 1px 3px rgba(26, 26, 46, 0.08),
|
||||||
|
0 1px 2px rgba(26, 26, 46, 0.05);
|
||||||
|
|
||||||
|
--shadow-md: 0 4px 6px rgba(26, 26, 46, 0.08),
|
||||||
|
0 2px 4px rgba(26, 26, 46, 0.05);
|
||||||
|
|
||||||
|
--shadow-lg: 0 10px 15px rgba(26, 26, 46, 0.10),
|
||||||
|
0 4px 6px rgba(26, 26, 46, 0.06);
|
||||||
|
|
||||||
|
--shadow-xl: 0 20px 25px rgba(26, 26, 46, 0.12),
|
||||||
|
0 10px 10px rgba(26, 26, 46, 0.06);
|
||||||
|
|
||||||
|
/* Accent glow — use on focused/highlighted elements */
|
||||||
|
--shadow-accent: 0 0 0 3px rgba(255, 107, 53, 0.25);
|
||||||
|
--shadow-focus: 0 0 0 3px rgba(255, 107, 53, 0.30);
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------------
|
||||||
|
MISC UI
|
||||||
|
------------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
--scrollbar-track: #F8F9FF;
|
||||||
|
--scrollbar-thumb: #c8cadc;
|
||||||
|
--scrollbar-thumb-hover: #FF6B35;
|
||||||
|
|
||||||
|
--overlay-backdrop: rgba(26, 26, 46, 0.50);
|
||||||
|
|
||||||
|
/* No diagonal clip in modern theme — set to 0 for override-safe utilities */
|
||||||
|
--clip-diagonal-angle: 0deg;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
TYPOGRAPHY SCALE — Token definitions
|
||||||
|
Resolved at theme level because font families differ between themes.
|
||||||
|
These tokens map to semantic roles and should be consumed directly.
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
/* Shared scale values (dimensionless, theme-independent) */
|
||||||
|
:root {
|
||||||
|
|
||||||
|
/* --- Type scale (font-size) --- */
|
||||||
|
--text-h1: clamp(2.25rem, 5vw, 3.5rem); /* 36px → 56px */
|
||||||
|
--text-h2: clamp(1.875rem, 4vw, 2.75rem); /* 30px → 44px */
|
||||||
|
--text-h3: clamp(1.5rem, 3vw, 2.125rem); /* 24px → 34px */
|
||||||
|
--text-h4: clamp(1.25rem, 2vw, 1.625rem); /* 20px → 26px */
|
||||||
|
--text-h5: 1.125rem; /* 18px */
|
||||||
|
--text-h6: 1rem; /* 16px */
|
||||||
|
|
||||||
|
--text-body-lg: 1.125rem; /* 18px */
|
||||||
|
--text-body: 1rem; /* 16px */
|
||||||
|
--text-body-sm: 0.875rem; /* 14px */
|
||||||
|
--text-caption: 0.75rem; /* 12px */
|
||||||
|
--text-label: 0.8125rem; /* 13px */
|
||||||
|
--text-mono: 1rem; /* 16px — prices, SKUs */
|
||||||
|
|
||||||
|
/* --- Line heights --- */
|
||||||
|
--leading-h1: 1.10;
|
||||||
|
--leading-h2: 1.12;
|
||||||
|
--leading-h3: 1.15;
|
||||||
|
--leading-h4: 1.20;
|
||||||
|
--leading-h5: 1.25;
|
||||||
|
--leading-h6: 1.30;
|
||||||
|
|
||||||
|
--leading-body-lg: 1.65;
|
||||||
|
--leading-body: 1.60;
|
||||||
|
--leading-body-sm: 1.55;
|
||||||
|
--leading-caption: 1.45;
|
||||||
|
--leading-label: 1.40;
|
||||||
|
--leading-mono: 1.50;
|
||||||
|
|
||||||
|
/* --- Letter spacing --- */
|
||||||
|
--tracking-tight: -0.03em;
|
||||||
|
--tracking-snug: -0.01em;
|
||||||
|
--tracking-normal: 0em;
|
||||||
|
--tracking-wide: 0.03em;
|
||||||
|
--tracking-wider: 0.06em;
|
||||||
|
--tracking-widest: 0.12em; /* Use for ALL-CAPS labels / badges */
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Heading letter-spacing per theme */
|
||||||
|
[data-theme="industrial"] {
|
||||||
|
--heading-tracking-h1: -0.02em;
|
||||||
|
--heading-tracking-h2: -0.02em;
|
||||||
|
--heading-tracking-h3: -0.01em;
|
||||||
|
--heading-tracking-h4: 0em;
|
||||||
|
--heading-tracking-h5: 0.02em;
|
||||||
|
--heading-tracking-h6: 0.04em;
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="modern"] {
|
||||||
|
--heading-tracking-h1: -0.03em;
|
||||||
|
--heading-tracking-h2: -0.02em;
|
||||||
|
--heading-tracking-h3: -0.01em;
|
||||||
|
--heading-tracking-h4: 0em;
|
||||||
|
--heading-tracking-h5: 0em;
|
||||||
|
--heading-tracking-h6: 0.01em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
COMPONENT SHORTHAND TOKENS
|
||||||
|
Convenience aliases that combine multiple primitives. Components should
|
||||||
|
reference these rather than the primitives above.
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
:root {
|
||||||
|
|
||||||
|
/* --- Input / form fields --- */
|
||||||
|
/* These are intentionally left as CSS variable references so they resolve
|
||||||
|
correctly within whichever theme is active at runtime. */
|
||||||
|
|
||||||
|
/* (No :root overrides needed — components consume --color-* directly.) */
|
||||||
|
|
||||||
|
/* --- Focus ring --- */
|
||||||
|
--focus-ring: 0 0 0 3px var(--shadow-focus, rgba(245,166,35,0.40));
|
||||||
|
|
||||||
|
/* --- Content max widths --- */
|
||||||
|
--content-xs: 480px;
|
||||||
|
--content-sm: 640px;
|
||||||
|
--content-md: 768px;
|
||||||
|
--content-lg: 1024px;
|
||||||
|
--content-xl: 1280px;
|
||||||
|
--content-full: 100%;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
UTILITY — Scrollbar styles (opt-in via class)
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
.themed-scrollbar {
|
||||||
|
scrollbar-width: thin;
|
||||||
|
scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
|
||||||
|
}
|
||||||
|
|
||||||
|
.themed-scrollbar::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.themed-scrollbar::-webkit-scrollbar-track {
|
||||||
|
background: var(--scrollbar-track);
|
||||||
|
}
|
||||||
|
|
||||||
|
.themed-scrollbar::-webkit-scrollbar-thumb {
|
||||||
|
background-color: var(--scrollbar-thumb);
|
||||||
|
border-radius: var(--radius-full);
|
||||||
|
border: 2px solid var(--scrollbar-track);
|
||||||
|
}
|
||||||
|
|
||||||
|
.themed-scrollbar::-webkit-scrollbar-thumb:hover {
|
||||||
|
background-color: var(--scrollbar-thumb-hover);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
UTILITY — Dot-grid background (Theme B helper)
|
||||||
|
Apply class .bg-dot-grid to body or layout shell when using modern theme.
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
[data-theme="modern"] .bg-dot-grid {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
UTILITY — Industrial diagonal clip helpers (Theme A)
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
[data-theme="industrial"] .clip-top-right {
|
||||||
|
clip-path: polygon(0 0, calc(100% - 24px) 0, 100% 24px, 100% 100%, 0 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="industrial"] .clip-bottom-left {
|
||||||
|
clip-path: polygon(0 0, 100% 0, 100% 100%, 24px 100%, 0 calc(100% - 24px));
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="industrial"] .clip-corner {
|
||||||
|
clip-path: polygon(0 0, calc(100% - 16px) 0, 100% 16px, 100% 100%, 0 100%);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
GLASSMORPHISM TOKENS
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
[data-theme="industrial"] {
|
||||||
|
--glass-bg: rgba(26, 26, 26, 0.70);
|
||||||
|
--glass-bg-strong: rgba(26, 26, 26, 0.85);
|
||||||
|
--glass-border: rgba(255, 255, 255, 0.08);
|
||||||
|
--glass-blur: 16px;
|
||||||
|
--glass-highlight: rgba(245, 166, 35, 0.06);
|
||||||
|
|
||||||
|
--glow-color: rgba(245, 166, 35, 0.40);
|
||||||
|
--glow-color-soft: rgba(245, 166, 35, 0.15);
|
||||||
|
--glow-color-strong: rgba(245, 166, 35, 0.60);
|
||||||
|
|
||||||
|
--gradient-accent: linear-gradient(135deg, #F5A623 0%, #e8951a 50%, #d4850f 100%);
|
||||||
|
--gradient-text: linear-gradient(135deg, #F5A623 0%, #FFD080 50%, #F5A623 100%);
|
||||||
|
|
||||||
|
--canvas-grid-color: rgba(255, 255, 255, 0.06);
|
||||||
|
--canvas-star-color: rgba(245, 166, 35, 0.30);
|
||||||
|
--canvas-glow-color: rgba(245, 166, 35, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
[data-theme="modern"] {
|
||||||
|
--glass-bg: rgba(248, 249, 255, 0.70);
|
||||||
|
--glass-bg-strong: rgba(248, 249, 255, 0.85);
|
||||||
|
--glass-border: rgba(26, 26, 46, 0.08);
|
||||||
|
--glass-blur: 16px;
|
||||||
|
--glass-highlight: rgba(255, 107, 53, 0.04);
|
||||||
|
|
||||||
|
--glow-color: rgba(255, 107, 53, 0.35);
|
||||||
|
--glow-color-soft: rgba(255, 107, 53, 0.12);
|
||||||
|
--glow-color-strong: rgba(255, 107, 53, 0.55);
|
||||||
|
|
||||||
|
--gradient-accent: linear-gradient(135deg, #FF6B35 0%, #FF8F65 50%, #FF6B35 100%);
|
||||||
|
--gradient-text: linear-gradient(135deg, #FF6B35 0%, #FF8F65 50%, #e85520 100%);
|
||||||
|
|
||||||
|
--canvas-grid-color: rgba(26, 26, 46, 0.05);
|
||||||
|
--canvas-star-color: rgba(255, 107, 53, 0.20);
|
||||||
|
--canvas-glow-color: rgba(255, 107, 53, 0.06);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
ANIMATION KEYFRAMES
|
||||||
|
========================================================================== */
|
||||||
|
|
||||||
|
@keyframes nx-fade-up {
|
||||||
|
from { opacity: 0; transform: translateY(24px); }
|
||||||
|
to { opacity: 1; transform: translateY(0); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes nx-glow-pulse {
|
||||||
|
0%, 100% { box-shadow: 0 0 20px var(--glow-color-soft); }
|
||||||
|
50% { box-shadow: 0 0 40px var(--glow-color); }
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes nx-shimmer {
|
||||||
|
0% { background-position: -200% 0; }
|
||||||
|
100% { background-position: 200% 0; }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* ==========================================================================
|
||||||
|
END OF TOKENS FILE
|
||||||
|
nexus-autoparts-design/tokens/tokens.css
|
||||||
|
========================================================================== */
|
||||||
1
pos/static/js/accounting.min.js
vendored
Normal file
1
pos/static/js/accounting.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
pos/static/js/app-init.min.js
vendored
Normal file
1
pos/static/js/app-init.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
!function(){"use strict";var e=localStorage.getItem("pos_token");if(e){try{var t=JSON.parse(atob(e.split(".")[1]));if(1e3*t.exp<Date.now())return localStorage.removeItem("pos_token"),localStorage.removeItem("pos_employee"),void(window.location.href="/pos/login")}catch(e){return localStorage.removeItem("pos_token"),void(window.location.href="/pos/login")}var o={};try{o=JSON.parse(localStorage.getItem("pos_employee")||"{}")}catch(e){}var n=o.name||t.name||"Usuario",a=o.role||t.role||"",r="function"==typeof window.t?window.t:function(e){return e},i={owner:r("role_owner"),admin:r("role_admin"),cashier:r("role_cashier"),warehouse:r("role_warehouse"),accountant:r("role_accountant")}[a]||a,c=n.split(" ").map((function(e){return e[0]})).join("").toUpperCase().substring(0,2);document.querySelectorAll(".sidebar__user-name").forEach((function(e){e.textContent=n})),document.querySelectorAll(".sidebar__user-role").forEach((function(e){e.textContent=i})),document.querySelectorAll(".sidebar__user-avatar, .sidebar__avatar").forEach((function(e){e.textContent=c})),document.querySelectorAll(".profile-info__name").forEach((function(e){e.textContent=n})),document.querySelectorAll(".profile-info__role").forEach((function(e){e.textContent=i})),document.querySelectorAll(".theme-bar__label").forEach((function(e){-1===e.textContent.indexOf("Usuario:")&&-1===e.textContent.indexOf("Sucursal")||(e.textContent="Sucursal Principal — "+n)})),document.querySelectorAll(".status-bar .user-name, .status-info span").forEach((function(e){var t=e.textContent;["Hugo M.","Hugo García","J. Ramírez","José Ramírez","Carlos M.","Admin"].forEach((function(o){-1!==t.indexOf(o)&&(e.textContent=t.replace(o,n))}))}));var l=window.location.pathname;document.querySelectorAll(".nav-item, .nav-link").forEach((function(e){e.classList.remove("is-active","active"),(e.getAttribute("href")||"")===l&&(e.classList.add("is-active"),e.classList.add("active"))})),window.posLogout=function(){localStorage.removeItem("pos_token"),localStorage.removeItem("pos_employee"),localStorage.removeItem("pos_tenant_id"),localStorage.removeItem("pos_cart"),window.location.href="/pos/login"},document.querySelectorAll('[data-action="logout"], .btn-logout, .logout-btn').forEach((function(e){e.addEventListener("click",(function(e){e.preventDefault(),posLogout()}))}));var s=localStorage.getItem("pos_theme");s||(s=window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").matches?"industrial":"modern"),document.documentElement.setAttribute("data-theme",s),document.querySelectorAll(".theme-bar").forEach((function(e){e.style.display="none"})),window.posSetTheme=function(e){document.documentElement.setAttribute("data-theme",e),localStorage.setItem("pos_theme",e)},window.setTheme=window.posSetTheme,window.matchMedia&&window.matchMedia("(prefers-color-scheme: dark)").addEventListener("change",(function(e){if(!localStorage.getItem("pos_theme")){var t=e.matches?"industrial":"modern";document.documentElement.setAttribute("data-theme",t)}})),setTimeout((function(){document.documentElement.setAttribute("data-theme",s)}),100),window.POS_USER={name:n,role:a,roleLabel:i,initials:c,token:e,tenantId:t.tenant_id,employeeId:t.employee_id,branchId:t.branch_id,permissions:t.permissions||[]}}else window.location.href="/pos/login"}();
|
||||||
@@ -893,7 +893,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
var imgHtml = p.image_url
|
var imgHtml = p.image_url
|
||||||
? '<img src="' + esc(p.image_url) + '" alt="' + esc(p.name) + '">'
|
? '<img src="' + esc(p.image_url) + '" alt="' + esc(p.name) + '" loading="lazy" decoding="async">'
|
||||||
: '<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M8 7h8M8 12h8M8 17h5"/></svg>';
|
: '<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M8 7h8M8 12h8M8 17h5"/></svg>';
|
||||||
|
|
||||||
var tierClass = p.priority_tier === 1 ? ' part-card--tier1' : (p.priority_tier === 2 ? ' part-card--tier2' : '');
|
var tierClass = p.priority_tier === 1 ? ' part-card--tier1' : (p.priority_tier === 2 ? ' part-card--tier2' : '');
|
||||||
@@ -997,7 +997,7 @@
|
|||||||
: '';
|
: '';
|
||||||
|
|
||||||
var imgHtml = p.image_url
|
var imgHtml = p.image_url
|
||||||
? '<img src="' + esc(p.image_url) + '" alt="' + esc(p.name) + '">'
|
? '<img src="' + esc(p.image_url) + '" alt="' + esc(p.name) + '" loading="lazy" decoding="async">'
|
||||||
: '<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M8 7h8M8 12h8M8 17h5"/></svg>';
|
: '<svg width="48" height="48" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M8 7h8M8 12h8M8 17h5"/></svg>';
|
||||||
|
|
||||||
// Local-mode extras: manufacturer badge + priority tier indicator
|
// Local-mode extras: manufacturer badge + priority tier indicator
|
||||||
@@ -1125,7 +1125,7 @@
|
|||||||
html += '<div class="detail-oem">' + esc(p.oem_part_number) + '</div>';
|
html += '<div class="detail-oem">' + esc(p.oem_part_number) + '</div>';
|
||||||
html += '<div class="detail-name">' + esc(p.name) + '</div>';
|
html += '<div class="detail-name">' + esc(p.name) + '</div>';
|
||||||
if (p.description) html += '<div class="detail-desc">' + esc(p.description) + '</div>';
|
if (p.description) html += '<div class="detail-desc">' + esc(p.description) + '</div>';
|
||||||
if (p.image_url) html += '<div style="margin-top:var(--space-3);text-align:center;"><img src="' + esc(p.image_url) + '" alt="" style="max-width:100%;max-height:200px;object-fit:contain;border-radius:var(--radius-sm);"></div>';
|
if (p.image_url) html += '<div style="margin-top:var(--space-3);text-align:center;"><img src="' + esc(p.image_url) + '" alt="" loading="lazy" decoding="async" style="max-width:100%;max-height:200px;object-fit:contain;border-radius:var(--radius-sm);"></div>';
|
||||||
html += '</div>';
|
html += '</div>';
|
||||||
|
|
||||||
// Local stock
|
// Local stock
|
||||||
|
|||||||
1
pos/static/js/catalog.min.js
vendored
Normal file
1
pos/static/js/catalog.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
pos/static/js/chat.min.js
vendored
Normal file
1
pos/static/js/chat.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
pos/static/js/config.min.js
vendored
Normal file
1
pos/static/js/config.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
pos/static/js/customers.min.js
vendored
Normal file
1
pos/static/js/customers.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
pos/static/js/dashboard.min.js
vendored
Normal file
1
pos/static/js/dashboard.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
pos/static/js/diagrams.min.js
vendored
Normal file
1
pos/static/js/diagrams.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
pos/static/js/fleet.min.js
vendored
Normal file
1
pos/static/js/fleet.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
pos/static/js/i18n.min.js
vendored
Normal file
1
pos/static/js/i18n.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -549,7 +549,7 @@
|
|||||||
// Product image section
|
// Product image section
|
||||||
html += '<div style="text-align:center;margin-bottom:16px;padding-bottom:16px;border-bottom:1px solid var(--color-border);">';
|
html += '<div style="text-align:center;margin-bottom:16px;padding-bottom:16px;border-bottom:1px solid var(--color-border);">';
|
||||||
if (data.image_url) {
|
if (data.image_url) {
|
||||||
html += '<img src="' + esc(data.image_url) + '?t=' + Date.now() + '" alt="' + esc(data.name) + '" style="max-width:100%;max-height:220px;object-fit:contain;border-radius:var(--radius-sm);margin-bottom:8px;display:block;margin-left:auto;margin-right:auto;">';
|
html += '<img src="' + esc(data.image_url) + '?t=' + Date.now() + '" alt="' + esc(data.name) + '" loading="lazy" decoding="async" style="max-width:100%;max-height:220px;object-fit:contain;border-radius:var(--radius-sm);margin-bottom:8px;display:block;margin-left:auto;margin-right:auto;">';
|
||||||
html += '<div style="display:flex;gap:8px;justify-content:center;">';
|
html += '<div style="display:flex;gap:8px;justify-content:center;">';
|
||||||
html += '<button class="btn btn--ghost btn--sm" onclick="uploadItemImage(' + data.id + ')">Cambiar imagen</button>';
|
html += '<button class="btn btn--ghost btn--sm" onclick="uploadItemImage(' + data.id + ')">Cambiar imagen</button>';
|
||||||
html += '<button class="btn btn--ghost btn--sm" style="color:var(--color-error);" onclick="deleteItemImage(' + data.id + ')">Eliminar imagen</button>';
|
html += '<button class="btn btn--ghost btn--sm" style="color:var(--color-error);" onclick="deleteItemImage(' + data.id + ')">Eliminar imagen</button>';
|
||||||
|
|||||||
1
pos/static/js/inventory.min.js
vendored
Normal file
1
pos/static/js/inventory.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
pos/static/js/invoicing.min.js
vendored
Normal file
1
pos/static/js/invoicing.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
pos/static/js/kiosk.min.js
vendored
Normal file
1
pos/static/js/kiosk.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
!function(){"use strict";var e="pos_kiosk_mode";function n(){return window.matchMedia("(display-mode: standalone)").matches||!0===window.navigator.standalone}function t(){return void 0!==window.Capacitor&&window.Capacitor.isNativePlatform&&window.Capacitor.isNativePlatform()}function o(){var o=localStorage.getItem(e);return"true"===o||"false"!==o&&(n()||t())}var a=!1;function i(){if(!a){var e=document.documentElement,n=e.requestFullscreen||e.webkitRequestFullscreen||e.mozRequestFullScreen||e.msRequestFullscreen;n&&(n.call(e).catch((function(){})),a=!0)}}var c=null;async function r(){if("wakeLock"in navigator)try{(c=await navigator.wakeLock.request("screen")).addEventListener("release",(function(){c=null}))}catch(e){}}function u(){window.addEventListener("beforeunload",(function(e){o()&&(e.preventDefault(),e.returnValue="")})),document.addEventListener("contextmenu",(function(e){o()&&e.preventDefault()}));var e=["click","touchstart","keydown"];function n(){o()&&(i(),r()),e.forEach((function(e){document.removeEventListener(e,n)}))}e.forEach((function(e){document.addEventListener(e,n,{once:!1})})),function(){var e=localStorage.getItem("pos_token");if(e&&-1!==window.location.pathname.indexOf("/pos/login"))try{var n=e.split(".");if(3!==n.length)return;var t=JSON.parse(atob(n[1].replace(/-/g,"+").replace(/_/g,"/"))).exp;t&&1e3*t>Date.now()&&(window.location.href="/pos/")}catch(e){}}()}document.addEventListener("visibilitychange",(function(){"visible"===document.visibilityState&&o()&&!c&&r()})),window.NexusKiosk={isEnabled:o,isPWA:n,isCapacitor:t,enable:function(){localStorage.setItem(e,"true"),i(),r()},disable:function(){localStorage.setItem(e,"false"),c&&(c.release(),c=null),document.fullscreenElement&&document.exitFullscreen().catch((function(){}))},toggle:function(){return o()?window.NexusKiosk.disable():window.NexusKiosk.enable(),o()}},o()&&u(),window.addEventListener("storage",(function(n){n.key===e&&"true"===n.newValue&&u()}))}();
|
||||||
1
pos/static/js/login.min.js
vendored
Normal file
1
pos/static/js/login.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
!function(){"use strict";var t="",e=document.querySelectorAll("#pinDots .pin-dot"),n=document.getElementById("loginError"),o=new URLSearchParams(window.location.search).get("tenant")||localStorage.getItem("pos_tenant_id"),a=localStorage.getItem("pos_device_id");function i(){e.forEach((function(e,n){e.classList.toggle("filled",n<t.length)}))}a||(a="dev-"+Date.now()+"-"+Math.random().toString(36).substr(2,9),localStorage.setItem("pos_device_id",a)),window.addDigit=function(e){t.length>=4||(t+=e,i(),n.textContent="",4===t.length&&submitPin())},window.clearPin=function(){t="",i(),n.textContent=""},window.submitPin=function(){4===t.length&&(n.textContent="",fetch("/pos/api/auth/login",{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({tenant_id:parseInt(o),pin:t,device_id:a})}).then((function(t){return t.json().then((function(e){return{ok:t.ok,data:e}}))})).then((function(t){if(!t.ok)return n.textContent=t.data.error||"Error de autenticacion",void clearPin();localStorage.setItem("pos_token",t.data.token),localStorage.setItem("pos_employee",JSON.stringify(t.data.employee)),localStorage.setItem("pos_tenant_id",o),window.location.href="/pos/catalog"})).catch((function(){n.textContent="Error de conexion",clearPin()})))},document.addEventListener("keydown",(function(t){t.key>="0"&&t.key<="9"?addDigit(t.key):"Backspace"===t.key?clearPin():"Enter"===t.key&&submitPin()}));var r=localStorage.getItem("pos_token");r&&o&&(!function(t){try{var e=t.split(".");if(3!==e.length)return!1;var n=e[1].replace(/-/g,"+").replace(/_/g,"/"),o=JSON.parse(atob(n));return!!o.exp&&1e3*o.exp>Date.now()+3e4}catch(t){return!1}}(r)?(localStorage.removeItem("pos_token"),localStorage.removeItem("pos_employee")):window.location.href="/pos/catalog")}();
|
||||||
1
pos/static/js/native-bridge.min.js
vendored
Normal file
1
pos/static/js/native-bridge.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
!function(){"use strict";window.NexusNative={isNative:"undefined"!=typeof Capacitor,_scanStream:null,_scanVideo:null,async scanBarcode(){if(this.isNative)try{const{Camera:e}=await import("@capacitor/camera");return await e.getPhoto({quality:90,resultType:"base64"})}catch(e){return null}return"BarcodeDetector"in window?new Promise((async e=>{try{const t=await navigator.mediaDevices.getUserMedia({video:{facingMode:"environment",width:{ideal:1280},height:{ideal:720}}});this._scanStream=t;const a=document.createElement("div");a.id="barcode-scan-overlay",a.style.cssText="position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.9);z-index:99999;display:flex;flex-direction:column;align-items:center;justify-content:center;";const i=document.createElement("video");i.autoplay=!0,i.playsInline=!0,i.style.cssText="width:90%;max-width:500px;border-radius:12px;border:3px solid #F5A623;",i.srcObject=t,this._scanVideo=i;const o=document.createElement("p");o.textContent="Apunta al codigo de barras...",o.style.cssText="color:#fff;font-size:16px;margin-top:16px;font-family:sans-serif;";const s=document.createElement("button");s.textContent="Cancelar",s.style.cssText="margin-top:16px;padding:10px 24px;background:#F5A623;color:#000;border:none;border-radius:6px;font-size:15px;cursor:pointer;font-weight:bold;",s.onclick=()=>{this.stopScan(),a.remove(),e(null)},a.appendChild(i),a.appendChild(o),a.appendChild(s),document.body.appendChild(a);const n=new BarcodeDetector({formats:["ean_13","ean_8","code_128","code_39","qr_code","upc_a","upc_e"]}),r=async()=>{if(this._scanStream){try{const t=await n.detect(i);if(t.length>0){const i=t[0].rawValue;return o.textContent="Codigo detectado: "+i,o.style.color="#4CAF50",void setTimeout((()=>{this.stopScan(),a.remove(),e(i)}),400)}}catch(e){}requestAnimationFrame(r)}};i.onloadedmetadata=()=>{i.play(),requestAnimationFrame(r)}}catch(t){console.error("Camera access error:",t),alert("No se pudo acceder a la camara: "+t.message),e(null)}})):(alert("Tu navegador no soporta escaneo de codigos de barras. Usa Chrome 83+ o un dispositivo movil."),null)},stopScan(){this._scanStream&&(this._scanStream.getTracks().forEach((e=>e.stop())),this._scanStream=null),this._scanVideo=null;var e=document.getElementById("barcode-scan-overlay");e&&e.remove()},async registerPush(){if(!this.isNative)return null;try{const{PushNotifications:e}=await import("@capacitor/push-notifications");"granted"===(await e.requestPermissions()).receive&&await e.register(),e.addListener("registration",(e=>{console.log("Push token:",e.value)})),e.addListener("pushNotificationReceived",(e=>{console.log("Push received:",e)}))}catch(e){console.log("Push not available:",e)}},async vibrate(){if(this.isNative)try{const{Haptics:e,ImpactStyle:t}=await import("@capacitor/haptics");await e.impact({style:t.Light})}catch(e){}},async setupStatusBar(){if(this.isNative)try{const{StatusBar:e,Style:t}=await import("@capacitor/status-bar");await e.setStyle({style:t.Dark}),await e.setBackgroundColor({color:"#0d0d0d"})}catch(e){}}},window.NexusNative.isNative&&(window.NexusNative.setupStatusBar(),window.NexusNative.registerPush())}();
|
||||||
1
pos/static/js/offline-banner.min.js
vendored
Normal file
1
pos/static/js/offline-banner.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
!function(){"use strict";var n=document.getElementById("offlineBanner"),e=document.getElementById("offlineBannerText");if(n&&e){var t=null;navigator.onLine||o(),window.addEventListener("offline",o),window.addEventListener("online",(function(){clearTimeout(t),n.className="banner banner--success",n.style.display="flex",n.style.animation="slideDown 0.35s ease-out forwards",e.innerHTML="<strong>Conexion restaurada</strong> — Sincronizando datos...",t=setTimeout((function(){n.style.animation="slideUp 0.3s ease-in forwards",n.addEventListener("animationend",(function e(){n.style.display="none",n.removeEventListener("animationend",e)}),{once:!0})}),3e3)}))}function o(){clearTimeout(t),n.className="banner banner--error",n.style.display="flex",n.style.animation="slideDown 0.35s ease-out forwards",e.innerHTML="<strong>Conexion perdida</strong> — Intentando reconectar..."}}();
|
||||||
1
pos/static/js/onboarding.min.js
vendored
Normal file
1
pos/static/js/onboarding.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
pos/static/js/pos-utils.min.js
vendored
Normal file
1
pos/static/js/pos-utils.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
pos/static/js/pos.min.js
vendored
Normal file
1
pos/static/js/pos.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
pos/static/js/printer.min.js
vendored
Normal file
1
pos/static/js/printer.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
window.NexusPrinter=function(){"use strict";let e=null,t=null,r=null,n=null,a=null;async function i(){try{"usb"===a&&e&&await e.close(),"serial"===a&&(n&&(n.releaseLock(),n=null),r&&(await r.close(),r=null))}catch(e){}e=null,t=null,a=null,localStorage.removeItem("nexus_printer")}async function o(r){if(!a)return!1;try{if("usb"===a&&e&&null!==t){const n=512;for(let a=0;a<r.length;a+=n)await e.transferOut(t,r.slice(a,a+n));return!0}if("serial"===a&&n)return await n.write(r),!0}catch(e){console.error("[NexusPrinter] send error:",e),await i()}return!1}function l(){try{localStorage.setItem("nexus_printer",a||"")}catch(e){}}return{connect:async function(){if("usb"in navigator)try{e=await navigator.usb.requestDevice({filters:[]}),await e.open(),null===e.configuration&&await e.selectConfiguration(1);const r=e.configuration.interfaces[0];await e.claimInterface(r.interfaceNumber);const n=r.alternates[0].endpoints.find((e=>"out"===e.direction&&"bulk"===e.type));if(n)return t=n.endpointNumber,a="usb",l(),{ok:!0,type:"usb",name:e.productName||"USB Printer"}}catch(t){e=null}if("serial"in navigator)try{return r=await navigator.serial.requestPort(),await r.open({baudRate:9600}),n=r.writable.getWriter(),a="serial",l(),{ok:!0,type:"serial",name:"Serial Printer"}}catch(e){r=null,n=null}return{ok:!1,error:"No printer connected. Use a browser that supports WebUSB or Web Serial (Chrome/Edge)."}},disconnect:i,isConnected:function(){return null!==a},sendRaw:o,printSale:async function(e,t){t=t||80;const r=localStorage.getItem("pos_token"),n=await fetch("/pos/api/sales/"+e+"/print",{method:"POST",headers:{Authorization:"Bearer "+r,"Content-Type":"application/json"},body:JSON.stringify({printer_type:"escpos_raw",width:t})});if(!n.ok)return console.error("[NexusPrinter] backend error",n.status),!1;const a=await n.arrayBuffer();return o(new Uint8Array(a))}}}();
|
||||||
1
pos/static/js/push.min.js
vendored
Normal file
1
pos/static/js/push.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
!function(){"use strict";var e={};try{e=JSON.parse(localStorage.getItem("pos_employee")||"{}")}catch(e){}var o=e.role||"";if("owner"===o||"admin"===o)if("serviceWorker"in navigator&&"PushManager"in window){var r=localStorage.getItem("pos_token");r&&("complete"===document.readyState?setTimeout(i,2e3):window.addEventListener("load",(function(){setTimeout(i,2e3)})))}else console.log("[Push] Browser does not support push notifications");function t(e){for(var o=(e+"=".repeat((4-e.length%4)%4)).replace(/-/g,"+").replace(/_/g,"/"),r=window.atob(o),t=new Uint8Array(r.length),i=0;i<r.length;++i)t[i]=r.charCodeAt(i);return t}async function i(){try{var e=await navigator.serviceWorker.register("/pos/static/sw-push.js",{scope:"/pos/"});console.log("[Push] Service worker registered");var o=await fetch("/pos/api/push/vapid-key",{headers:{Authorization:"Bearer "+r}});if(!o.ok)return void console.log("[Push] VAPID key not available:",o.status);var i=(await o.json()).public_key;if(!i)return;if("granted"!==await Notification.requestPermission())return void console.log("[Push] Permission denied");var a=await e.pushManager.subscribe({userVisibleOnly:!0,applicationServerKey:t(i)});(await fetch("/pos/api/push/subscribe",{method:"POST",headers:{"Content-Type":"application/json",Authorization:"Bearer "+r},body:JSON.stringify({subscription:a.toJSON()})})).ok&&console.log("[Push] Subscribed successfully")}catch(e){console.log("[Push] Setup error:",e)}}}();
|
||||||
1
pos/static/js/reports.min.js
vendored
Normal file
1
pos/static/js/reports.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
pos/static/js/sidebar.min.js
vendored
Normal file
1
pos/static/js/sidebar.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
pos/static/js/sync-engine.min.js
vendored
Normal file
1
pos/static/js/sync-engine.min.js
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
!function(){"use strict";var n="sync_queue",e="inventory_cache",t="cached_parts",r=null;function o(){return new Promise((function(o,c){if(r)o(r);else{var i=indexedDB.open("nexus_pos_offline",2);i.onupgradeneeded=function(r){var o=r.target.result;if(o.objectStoreNames.contains(n)||o.createObjectStore(n,{keyPath:"id",autoIncrement:!0}),!o.objectStoreNames.contains(e)){var c=o.createObjectStore(e,{keyPath:"item_id"});c.createIndex("sku","sku",{unique:!1}),c.createIndex("name","name",{unique:!1})}if(!o.objectStoreNames.contains(t)){var i=o.createObjectStore(t,{keyPath:"part_number"});i.createIndex("name","name",{unique:!1}),i.createIndex("category","category",{unique:!1})}},i.onsuccess=function(n){r=n.target.result,o(r)},i.onerror=function(){c(i.error)}}}))}function c(){return o().then((function(e){return new Promise((function(t,r){var o=e.transaction(n,"readonly").objectStore(n).getAll();o.onsuccess=function(){t(o.result)},o.onerror=function(){r(o.error)}}))})).then((function(e){if(!e.length)return Promise.resolve({synced:0});var t=Promise.resolve(),r=0,c=0;return e.forEach((function(e){t=t.then((function(){var t={method:e.method,headers:{"Content-Type":"application/json"}};return e.body&&(t.body=JSON.stringify(e.body)),fetch(e.url,t).then((function(t){if(t.ok)return r++,i=e.id,o().then((function(e){return new Promise((function(t,r){var o=e.transaction(n,"readwrite");o.objectStore(n).delete(i),o.oncomplete=function(){t()},o.onerror=function(){r(o.error)}}))}));var i;c++})).catch((function(){c++}))}))})),t.then((function(){return{synced:r,failed:c,total:e.length}}))}))}window.addEventListener("online",(function(){console.log("[SyncEngine] Online — processing queue..."),c().then((function(n){n.synced>0&&console.log("[SyncEngine] Synced "+n.synced+" operations")})).catch((function(n){console.error("[SyncEngine] Queue processing error:",n)}))})),window.addEventListener("offline",(function(){console.log("[SyncEngine] Offline — operations will be queued")})),"serviceWorker"in navigator&&navigator.serviceWorker.addEventListener("message",(function(n){n.data&&"SYNC_REQUESTED"===n.data.type&&c()})),o().catch((function(n){console.error("[SyncEngine] Failed to open IndexedDB:",n)})),window.SyncEngine={queueOperation:function(e,t,r){return o().then((function(o){return new Promise((function(c,i){var a=o.transaction(n,"readwrite");a.objectStore(n).add({url:e,method:t,body:r||null,timestamp:Date.now()}),a.oncomplete=function(){c()},a.onerror=function(){i(a.error)}}))}))},processQueue:c,getQueueCount:function(){return o().then((function(e){return new Promise((function(t,r){var o=e.transaction(n,"readonly").objectStore(n).count();o.onsuccess=function(){t(o.result)},o.onerror=function(){r(o.error)}}))}))},isOnline:function(){return navigator.onLine},cacheInventory:function(){return fetch("/pos/api/sync/inventory").then((function(n){if(!n.ok)throw new Error("Sync inventory failed: "+n.status);return n.json()})).then((function(n){var t=n.items||[];return o().then((function(n){return new Promise((function(r,o){var c=n.transaction(e,"readwrite"),i=c.objectStore(e);i.clear(),t.forEach((function(n){i.put(n)})),c.oncomplete=function(){r(t.length)},c.onerror=function(){o(c.error)}}))}))}))},getCachedInventory:function(n){return o().then((function(t){return new Promise((function(r,o){var c=t.transaction(e,"readonly").objectStore(e).getAll();c.onsuccess=function(){var e=c.result;if(n){var t=n.toLowerCase(),o=e.filter((function(n){return n.sku&&-1!==n.sku.toLowerCase().indexOf(t)||n.name&&-1!==n.name.toLowerCase().indexOf(t)||n.barcode&&-1!==n.barcode.toLowerCase().indexOf(t)}));r(o)}else r(e)},c.onerror=function(){o(c.error)}}))}))},cacheTopParts:function(){return fetch("/pos/api/sync/top-parts").then((function(n){if(!n.ok)throw new Error("Sync top-parts failed: "+n.status);return n.json()})).then((function(n){var e=n.parts||[];return o().then((function(n){return new Promise((function(r,o){var c=n.transaction(t,"readwrite"),i=c.objectStore(t);i.clear(),e.forEach((function(n){i.put(n)})),c.oncomplete=function(){console.log("[SyncEngine] Cached "+e.length+" top parts"),r(e.length)},c.onerror=function(){o(c.error)}}))}))}))},searchCachedParts:function(n){return o().then((function(e){return new Promise((function(r,o){var c=e.transaction(t,"readonly").objectStore(t).getAll();c.onsuccess=function(){var e=c.result;if(n){var t=n.toLowerCase(),o=e.filter((function(n){return n.part_number&&-1!==n.part_number.toLowerCase().indexOf(t)||n.name&&-1!==n.name.toLowerCase().indexOf(t)||n.category&&-1!==n.category.toLowerCase().indexOf(t)||n.brand&&-1!==n.brand.toLowerCase().indexOf(t)}));r(o)}else r(e)},c.onerror=function(){o(c.error)}}))}))}}}();
|
||||||
1
pos/static/js/whatsapp.min.js
vendored
Normal file
1
pos/static/js/whatsapp.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
47
scripts/minify-assets.sh
Executable file
47
scripts/minify-assets.sh
Executable file
@@ -0,0 +1,47 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# Minify JS and CSS assets for production deploy
|
||||||
|
# Run this before restarting nginx / gunicorn
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
echo "=== Minifying JS assets ==="
|
||||||
|
for f in /home/Autopartes/pos/static/js/*.js; do
|
||||||
|
base=$(basename "$f" .js)
|
||||||
|
out="/home/Autopartes/pos/static/js/${base}.min.js"
|
||||||
|
echo " $base.js -> ${base}.min.js"
|
||||||
|
terser "$f" -o "$out" --compress --mangle 2>/dev/null || cp "$f" "$out"
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "=== Minifying CSS assets ==="
|
||||||
|
for f in /home/Autopartes/pos/static/css/*.css; do
|
||||||
|
base=$(basename "$f" .css)
|
||||||
|
out="/home/Autopartes/pos/static/css/${base}.min.css"
|
||||||
|
echo " $base.css -> ${base}.min.css"
|
||||||
|
if command -v csso >/dev/null 2>&1; then
|
||||||
|
csso "$f" -o "$out" 2>/dev/null || cp "$f" "$out"
|
||||||
|
else
|
||||||
|
cp "$f" "$out"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "=== Minifying Dashboard assets ==="
|
||||||
|
for f in /home/Autopartes/dashboard/*.js; do
|
||||||
|
base=$(basename "$f" .js)
|
||||||
|
out="/home/Autopartes/dashboard/${base}.min.js"
|
||||||
|
echo " $base.js -> ${base}.min.js"
|
||||||
|
terser "$f" -o "$out" --compress --mangle 2>/dev/null || cp "$f" "$out"
|
||||||
|
done
|
||||||
|
|
||||||
|
for f in /home/Autopartes/dashboard/*.css; do
|
||||||
|
base=$(basename "$f" .css)
|
||||||
|
out="/home/Autopartes/dashboard/${base}.min.css"
|
||||||
|
echo " $base.css -> ${base}.min.css"
|
||||||
|
if command -v csso >/dev/null 2>&1; then
|
||||||
|
csso "$f" -o "$out" 2>/dev/null || cp "$f" "$out"
|
||||||
|
else
|
||||||
|
cp "$f" "$out"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
echo "=== Done ==="
|
||||||
|
echo "Minified files ready. Nginx will serve .min.js/.min.css when available."
|
||||||
84
scripts/warm_vehicle_cache.py
Executable file
84
scripts/warm_vehicle_cache.py
Executable file
@@ -0,0 +1,84 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Warm Redis cache for vehicle info (part_vehicle_preview alternative).
|
||||||
|
|
||||||
|
Runs in batches over all parts in the catalog, populating
|
||||||
|
nexus:vehicle:{part_id} keys in Redis. This eliminates the
|
||||||
|
DISTINCT ON + 4 JOINs query on vehicle_parts (2B rows) for
|
||||||
|
cached parts.
|
||||||
|
|
||||||
|
Usage:
|
||||||
|
export MASTER_DB_URL="postgresql://..."
|
||||||
|
export REDIS_URL="redis://localhost:6379/0"
|
||||||
|
python3 warm_vehicle_cache.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
import os, sys, json, time
|
||||||
|
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'pos'))
|
||||||
|
|
||||||
|
import psycopg2
|
||||||
|
import redis
|
||||||
|
|
||||||
|
def _fix_dsn(dsn):
|
||||||
|
if dsn and 'host=' not in dsn and '@/' in dsn:
|
||||||
|
dsn = dsn.replace('@/', '@localhost/')
|
||||||
|
return dsn
|
||||||
|
|
||||||
|
|
||||||
|
MASTER_DB_URL = _fix_dsn(os.environ.get('MASTER_DB_URL', 'postgresql://postgres@localhost/nexus_autoparts'))
|
||||||
|
REDIS_URL = os.environ.get('REDIS_URL', 'redis://localhost:6379/0')
|
||||||
|
BATCH_SIZE = 5000
|
||||||
|
TTL_SECONDS = 3600
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print("Connecting to master DB and Redis...")
|
||||||
|
conn = psycopg2.connect(MASTER_DB_URL)
|
||||||
|
cur = conn.cursor()
|
||||||
|
r = redis.from_url(REDIS_URL, decode_responses=True)
|
||||||
|
r.ping()
|
||||||
|
|
||||||
|
# Get all part_ids
|
||||||
|
cur.execute("SELECT id_part FROM parts WHERE oem_part_number IS NOT NULL ORDER BY id_part")
|
||||||
|
all_ids = [r[0] for r in cur.fetchall()]
|
||||||
|
total = len(all_ids)
|
||||||
|
print(f"Total parts to warm: {total}")
|
||||||
|
|
||||||
|
processed = 0
|
||||||
|
cached = 0
|
||||||
|
start = time.time()
|
||||||
|
|
||||||
|
for i in range(0, total, BATCH_SIZE):
|
||||||
|
batch = all_ids[i:i + BATCH_SIZE]
|
||||||
|
cur.execute("""
|
||||||
|
SELECT DISTINCT ON (vp.part_id)
|
||||||
|
vp.part_id, b.name_brand, m.name_model, y.year_car
|
||||||
|
FROM vehicle_parts vp
|
||||||
|
JOIN model_year_engine mye ON mye.id_mye = vp.model_year_engine_id
|
||||||
|
JOIN models m ON m.id_model = mye.model_id
|
||||||
|
JOIN brands b ON b.id_brand = m.brand_id
|
||||||
|
JOIN years y ON y.id_year = mye.year_id
|
||||||
|
WHERE vp.part_id = ANY(%s)
|
||||||
|
ORDER BY vp.part_id, y.year_car DESC
|
||||||
|
""", (batch,))
|
||||||
|
|
||||||
|
pipe = r.pipeline()
|
||||||
|
batch_cached = 0
|
||||||
|
for row in cur.fetchall():
|
||||||
|
info = f"{row[1]} {row[2]} {row[3]}"
|
||||||
|
pipe.setex(f'nexus:vehicle:{row[0]}', TTL_SECONDS, info)
|
||||||
|
batch_cached += 1
|
||||||
|
pipe.execute()
|
||||||
|
|
||||||
|
processed += len(batch)
|
||||||
|
cached += batch_cached
|
||||||
|
elapsed = time.time() - start
|
||||||
|
rate = processed / elapsed if elapsed > 0 else 0
|
||||||
|
print(f" [{processed}/{total}] cached={batch_cached} ({rate:.0f}/s)")
|
||||||
|
|
||||||
|
cur.close()
|
||||||
|
conn.close()
|
||||||
|
print(f"\nDone. Cached {cached} vehicle entries in {elapsed:.0f}s")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
Reference in New Issue
Block a user