From 004cca7cccda9b29b6c83b8b3f20375a482fabdb Mon Sep 17 00:00:00 2001 From: consultoria-as Date: Wed, 1 Apr 2026 08:29:14 +0000 Subject: [PATCH] fix(pos): rewrite inventory.js to match design system HTML structure Co-Authored-By: Claude Opus 4.6 (1M context) --- pos/static/js/inventory.js | 499 ++++++++++++++++++++++++----------- pos/templates/inventory.html | 405 ++++++++++++++++++++-------- 2 files changed, 631 insertions(+), 273 deletions(-) diff --git a/pos/static/js/inventory.js b/pos/static/js/inventory.js index c0a0228..8835ef8 100644 --- a/pos/static/js/inventory.js +++ b/pos/static/js/inventory.js @@ -1,90 +1,133 @@ // /home/Autopartes/pos/static/js/inventory.js -// Inventory management UI: CRUD, purchases, adjustments, transfers, physical count, alerts +// Inventory management UI — rewritten to match design-system HTML structure +// Panels: panel-stock, panel-entradas, panel-salidas, panel-traspasos, panel-ajustes, panel-conteos, panel-alertas (function () { 'use strict'; - const API = '/pos/api/inventory'; - const token = localStorage.getItem('pos_token'); + var API = '/pos/api/inventory'; + var token = localStorage.getItem('pos_token'); if (!token) { window.location.href = '/pos/login'; return; } - const headers = { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' }; - let currentPage = 1; - let currentSearch = ''; - let draftCountId = null; + var headers = { 'Authorization': 'Bearer ' + token, 'Content-Type': 'application/json' }; + var currentPage = 1; + var currentSearch = ''; + var draftCountId = null; // --- API helper --- - async function apiFetch(url, opts) { - const resp = await fetch(url, Object.assign({ headers: headers }, opts || {})); - if (resp.status === 401) { localStorage.removeItem('pos_token'); window.location.href = '/pos/login'; return null; } - return resp.json(); + function apiFetch(url, opts) { + return fetch(url, Object.assign({ headers: headers }, opts || {})) + .then(function (resp) { + if (resp.status === 401) { + localStorage.removeItem('pos_token'); + window.location.href = '/pos/login'; + return null; + } + return resp.json(); + }); } - // --- Tab switching --- - document.querySelectorAll('.tab').forEach(function (tab) { - tab.addEventListener('click', function () { - document.querySelectorAll('.tab').forEach(function (t) { t.classList.remove('active'); }); - document.querySelectorAll('.tab-content').forEach(function (c) { c.classList.remove('active'); }); - tab.classList.add('active'); - document.getElementById('tab-' + tab.dataset.tab).classList.add('active'); + // --- Helpers --- + function fmt(n) { return (parseFloat(n) || 0).toFixed(2); } + function esc(s) { + if (!s) return ''; + var d = document.createElement('div'); + d.textContent = s; + return d.innerHTML; + } - if (tab.dataset.tab === 'alerts') loadAlerts(); - }); - }); + // ===================================================================== + // TAB SWITCHING — uses design-system switchTab() already in the HTML. + // We hook into it to trigger data loads when tabs are activated. + // ===================================================================== - // --- Products --- - async function loadItems(page, search) { + var _origSwitchTab = window.switchTab; + window.switchTab = function (name) { + if (typeof _origSwitchTab === 'function') _origSwitchTab(name); + if (name === 'alertas') loadAlerts(); + if (name === 'stock') loadItems(currentPage); + }; + + // ===================================================================== + // STOCK / PRODUCTS (panel-stock) + // ===================================================================== + + function loadItems(page, search) { currentPage = page || 1; currentSearch = search !== undefined ? search : currentSearch; var params = new URLSearchParams({ page: currentPage, per_page: 50 }); if (currentSearch) params.set('q', currentSearch); - var data = await apiFetch(API + '/items?' + params.toString()); - if (!data) return; + apiFetch(API + '/items?' + params.toString()).then(function (data) { + if (!data) return; - var tbody = document.getElementById('productTableBody'); - var items = data.data || []; - if (!items.length) { tbody.innerHTML = 'Sin productos'; return; } + var tbody = document.getElementById('productTableBody'); + var items = data.data || []; + if (!items.length) { + tbody.innerHTML = 'Sin productos'; + document.getElementById('productPagination').innerHTML = ''; + return; + } - tbody.innerHTML = items.map(function (it) { - return '' + - '' + esc(it.barcode) + '' + - '' + esc(it.part_number) + '' + - '' + esc(it.name) + '' + - '' + esc(it.brand) + '' + - '' + it.stock + '' + - '$' + fmt(it.cost) + '' + - '$' + fmt(it.price_1) + '' + - '$' + fmt(it.price_2) + '' + - '$' + fmt(it.price_3) + '' + - '' + esc(it.location) + '' + - ' ' + - '' + - ''; - }).join(''); + tbody.innerHTML = items.map(function (it) { + return '' + + '' + esc(it.barcode) + '' + + '' + esc(it.part_number) + '' + + '' + esc(it.name) + '' + + '' + esc(it.brand) + '' + + '' + it.stock + '' + + '$' + fmt(it.cost) + '' + + '$' + fmt(it.price_1) + '' + + '$' + fmt(it.price_2) + '' + + '$' + fmt(it.price_3) + '' + + '' + esc(it.location) + '' + + '' + + ' ' + + '' + + ''; + }).join(''); - // Pagination - var pg = data.pagination || {}; - var pgEl = document.getElementById('productPagination'); - if (pg.total_pages > 1) { - pgEl.innerHTML = '' + - '' + pg.page + ' / ' + pg.total_pages + ' (' + pg.total + ' items)' + - ''; - } else { - pgEl.innerHTML = '' + (pg.total || 0) + ' productos'; - } + // Pagination + var pg = data.pagination || {}; + var pgEl = document.getElementById('productPagination'); + if (pg.total_pages > 1) { + pgEl.innerHTML = + ''; + } else { + pgEl.innerHTML = '' + (pg.total || 0) + ' productos'; + } + }); } // Search var searchInput = document.getElementById('productSearch'); var searchTimeout; - searchInput.addEventListener('input', function () { - clearTimeout(searchTimeout); - searchTimeout = setTimeout(function () { loadItems(1, searchInput.value.trim()); }, 350); - }); + if (searchInput) { + searchInput.addEventListener('input', function () { + clearTimeout(searchTimeout); + searchTimeout = setTimeout(function () { + loadItems(1, searchInput.value.trim()); + }, 350); + }); + } - // --- Create item --- - async function createItem() { + // ===================================================================== + // CREATE ITEM (createModal) + // ===================================================================== + + function showCreateModal() { + document.getElementById('createModal').classList.add('is-open'); + } + function closeCreateModal() { + document.getElementById('createModal').classList.remove('is-open'); + document.getElementById('createResult').innerHTML = ''; + } + + function createItem() { var data = { part_number: document.getElementById('newPartNumber').value.trim(), name: document.getElementById('newName').value.trim(), @@ -98,19 +141,33 @@ initial_stock: parseInt(document.getElementById('newInitialStock').value) || 0, location: document.getElementById('newLocation').value.trim() }; - if (!data.part_number || !data.name) { document.getElementById('createResult').innerHTML = 'Numero de parte y nombre son obligatorios'; return; } - - var result = await apiFetch(API + '/items', { method: 'POST', body: JSON.stringify(data) }); - if (result && result.id) { - document.getElementById('createResult').innerHTML = 'Creado ID ' + result.id + ' | Barcode: ' + result.barcode + ''; - loadItems(currentPage); - } else { - document.getElementById('createResult').innerHTML = '' + (result ? result.error || 'Error' : 'Error de red') + ''; + if (!data.part_number || !data.name) { + document.getElementById('createResult').innerHTML = 'Numero de parte y nombre son obligatorios'; + return; } + apiFetch(API + '/items', { method: 'POST', body: JSON.stringify(data) }).then(function (result) { + if (result && result.id) { + document.getElementById('createResult').innerHTML = 'Creado ID ' + result.id + ' | Barcode: ' + result.barcode + ''; + loadItems(currentPage); + } else { + document.getElementById('createResult').innerHTML = '' + (result ? result.error || 'Error' : 'Error de red') + ''; + } + }); } - // --- Purchase --- - async function recordPurchase() { + // ===================================================================== + // PURCHASE / ENTRADA (purchaseModal) + // ===================================================================== + + function showPurchaseModal() { + document.getElementById('purchaseModal').classList.add('is-open'); + } + function closePurchaseModal() { + document.getElementById('purchaseModal').classList.remove('is-open'); + document.getElementById('purchaseResult').innerHTML = ''; + } + + function recordPurchase() { var data = { inventory_id: parseInt(document.getElementById('purchaseItemId').value), quantity: parseInt(document.getElementById('purchaseQty').value), @@ -119,32 +176,58 @@ notes: document.getElementById('purchaseNotes').value.trim() }; if (!data.inventory_id || !data.quantity || !data.unit_cost) { - document.getElementById('purchaseResult').innerHTML = 'Complete todos los campos'; return; + document.getElementById('purchaseResult').innerHTML = 'Complete todos los campos obligatorios'; + return; } - var result = await apiFetch(API + '/purchase', { method: 'POST', body: JSON.stringify(data) }); - document.getElementById('purchaseResult').innerHTML = result && result.operation_id - ? 'Compra registrada (op #' + result.operation_id + ')' - : '' + (result ? result.error || 'Error' : 'Error de red') + ''; + apiFetch(API + '/purchase', { method: 'POST', body: JSON.stringify(data) }).then(function (result) { + document.getElementById('purchaseResult').innerHTML = result && result.operation_id + ? 'Compra registrada (op #' + result.operation_id + ')' + : '' + (result ? result.error || 'Error' : 'Error de red') + ''; + }); } - // --- Adjustment --- - async function recordAdjustment() { + // ===================================================================== + // ADJUSTMENT / AJUSTE (adjustmentModal) + // ===================================================================== + + function showAdjustmentModal() { + document.getElementById('adjustmentModal').classList.add('is-open'); + } + function closeAdjustmentModal() { + document.getElementById('adjustmentModal').classList.remove('is-open'); + document.getElementById('adjustResult').innerHTML = ''; + } + + function recordAdjustment() { var data = { inventory_id: parseInt(document.getElementById('adjustItemId').value), quantity: parseInt(document.getElementById('adjustQty').value), reason: document.getElementById('adjustReason').value.trim() }; if (!data.inventory_id || data.quantity === undefined || !data.reason) { - document.getElementById('adjustResult').innerHTML = 'Complete todos los campos (razon obligatoria)'; return; + document.getElementById('adjustResult').innerHTML = 'Complete todos los campos (razon obligatoria)'; + return; } - var result = await apiFetch(API + '/adjustment', { method: 'POST', body: JSON.stringify(data) }); - document.getElementById('adjustResult').innerHTML = result && result.operation_id - ? 'Ajuste registrado (op #' + result.operation_id + ')' - : '' + (result ? result.error || 'Error' : 'Error de red') + ''; + apiFetch(API + '/adjustment', { method: 'POST', body: JSON.stringify(data) }).then(function (result) { + document.getElementById('adjustResult').innerHTML = result && result.operation_id + ? 'Ajuste registrado (op #' + result.operation_id + ')' + : '' + (result ? result.error || 'Error' : 'Error de red') + ''; + }); } - // --- Transfer --- - async function recordTransfer() { + // ===================================================================== + // TRANSFER / TRASPASO (transferModal) + // ===================================================================== + + function showTransferModal() { + document.getElementById('transferModal').classList.add('is-open'); + } + function closeTransferModal() { + document.getElementById('transferModal').classList.remove('is-open'); + document.getElementById('transferResult').innerHTML = ''; + } + + function recordTransfer() { var data = { inventory_id: parseInt(document.getElementById('transferItemId').value), from_branch_id: parseInt(document.getElementById('transferFrom').value), @@ -153,27 +236,44 @@ notes: document.getElementById('transferNotes').value.trim() }; if (!data.inventory_id || !data.from_branch_id || !data.to_branch_id || !data.quantity) { - document.getElementById('transferResult').innerHTML = 'Complete todos los campos'; return; + document.getElementById('transferResult').innerHTML = 'Complete todos los campos'; + return; } - var result = await apiFetch(API + '/transfer', { method: 'POST', body: JSON.stringify(data) }); - document.getElementById('transferResult').innerHTML = result && result.out_operation_id - ? 'Transferencia registrada' - : '' + (result ? result.error || 'Error' : 'Error de red') + ''; + apiFetch(API + '/transfer', { method: 'POST', body: JSON.stringify(data) }).then(function (result) { + document.getElementById('transferResult').innerHTML = result && result.out_operation_id + ? 'Transferencia registrada' + : '' + (result ? result.error || 'Error' : 'Error de red') + ''; + }); + } + + // ===================================================================== + // PHYSICAL COUNT / CONTEO (countModal) + // ===================================================================== + + function showCountModal() { + document.getElementById('countModal').classList.add('is-open'); + // Pre-add one line if empty + if (!document.querySelectorAll('#countLines .count-row').length) { + addCountLine(); + } + } + function closeCountModal() { + document.getElementById('countModal').classList.remove('is-open'); } - // --- Physical Count (two-phase) --- function addCountLine() { var container = document.getElementById('countLines'); var row = document.createElement('div'); row.className = 'count-row'; - row.innerHTML = '' + - '' + - ''; + row.innerHTML = + '' + + '' + + ''; container.appendChild(row); } - async function startPhysicalCount() { - var rows = document.querySelectorAll('.count-row'); + function startPhysicalCount() { + var rows = document.querySelectorAll('#countLines .count-row'); var items = []; rows.forEach(function (row) { var invId = parseInt(row.querySelector('.count-inv-id').value); @@ -182,85 +282,152 @@ }); if (!items.length) { alert('Agregue al menos una linea'); return; } - var result = await apiFetch(API + '/physical-count/start', { method: 'POST', body: JSON.stringify({ items: items }) }); - if (!result || !result.count_id) { - document.getElementById('countResults').innerHTML = '' + (result ? result.error || 'Error' : 'Error de red') + ''; - return; - } + apiFetch(API + '/physical-count/start', { method: 'POST', body: JSON.stringify({ items: items }) }).then(function (result) { + if (!result || !result.count_id) { + document.getElementById('countResults').innerHTML = '' + (result ? result.error || 'Error' : 'Error de red') + ''; + return; + } - draftCountId = result.count_id; - var html = '

Borrador #' + result.count_id + ' — ' + result.message + '

'; - html += ''; - (result.results || []).forEach(function (r) { - var color = r.difference === 0 ? '#16a34a' : (r.difference < 0 ? '#dc2626' : '#ca8a04'); - html += ''; + draftCountId = result.count_id; + var html = '

Borrador #' + result.count_id + ' — ' + esc(result.message) + '

'; + html += '
IDEsperadoContadoDiferencia
' + r.inventory_id + '' + r.expected + '' + r.counted + '' + (r.difference > 0 ? '+' : '') + r.difference + '
'; + (result.results || []).forEach(function (r) { + var color = r.difference === 0 ? 'var(--color-success)' : (r.difference < 0 ? 'var(--color-error)' : 'var(--color-warning)'); + html += ''; + }); + html += '
IDEsperadoContadoDiferencia
' + r.inventory_id + '' + r.expected + '' + r.counted + '' + (r.difference > 0 ? '+' : '') + r.difference + '
'; + html += '
'; + html += ''; + html += ''; + html += '
'; + document.getElementById('countResults').innerHTML = html; }); - html += ''; - html += ''; - html += ' '; - document.getElementById('countResults').innerHTML = html; } - async function approvePhysicalCount() { + function approvePhysicalCount() { if (!draftCountId) { alert('No hay borrador activo'); return; } - var result = await apiFetch(API + '/physical-count/approve', { method: 'POST', body: JSON.stringify({ count_id: draftCountId }) }); - if (result && result.status === 'approved') { - document.getElementById('countResults').innerHTML = '' + result.message + ''; - draftCountId = null; - } else { - document.getElementById('countResults').innerHTML += '
' + (result ? result.error || 'Error' : 'Error de red') + ''; - } + apiFetch(API + '/physical-count/approve', { method: 'POST', body: JSON.stringify({ count_id: draftCountId }) }).then(function (result) { + if (result && result.status === 'approved') { + document.getElementById('countResults').innerHTML = '' + esc(result.message) + ''; + draftCountId = null; + } else { + document.getElementById('countResults').innerHTML += '
' + (result ? result.error || 'Error' : 'Error de red') + ''; + } + }); } function cancelDraft() { draftCountId = null; - document.getElementById('countResults').innerHTML = 'Borrador cancelado'; + document.getElementById('countResults').innerHTML = 'Borrador cancelado'; } - // --- Alerts --- - async function loadAlerts() { - var data = await apiFetch(API + '/alerts'); - if (!data) return; - var el = document.getElementById('alertsList'); - var alerts = data.data || []; - if (!alerts.length) { el.innerHTML = '

Sin alertas activas

'; return; } + // ===================================================================== + // ALERTS (panel-alertas) + // ===================================================================== - el.innerHTML = alerts.map(function (a) { - var cls = a.severity === 'critical' ? 'alert-critical' : (a.severity === 'warning' ? 'alert-warning' : 'alert-info'); - var icon = a.type === 'zero' ? 'AGOTADO' : (a.type === 'low' ? 'BAJO' : 'EXCESO'); - return '
' + - '
[' + icon + '] ' + esc(a.part_number) + ' — ' + esc(a.name) + ' | Stock: ' + a.stock + - (a.min_stock ? ' (min: ' + a.min_stock + ')' : '') + (a.max_stock ? ' (max: ' + a.max_stock + ')' : '') + '
' + - 'Sucursal ' + a.branch_id + '
'; - }).join(''); + function loadAlerts() { + apiFetch(API + '/alerts').then(function (data) { + if (!data) return; + var alerts = data.data || []; + var container = document.getElementById('alertsContent'); + if (!container) return; + + if (!alerts.length) { + container.innerHTML = '

Sin alertas activas

'; + return; + } + + var html = ''; + + // Group by severity + var critical = alerts.filter(function (a) { return a.severity === 'critical'; }); + var warning = alerts.filter(function (a) { return a.severity === 'warning'; }); + var info = alerts.filter(function (a) { return a.severity !== 'critical' && a.severity !== 'warning'; }); + + if (critical.length) { + html += '
Criticas
' + critical.length + '
'; + html += '
'; + critical.forEach(function (a) { + var icon = a.type === 'zero' ? 'AGOTADO' : (a.type === 'low' ? 'BAJO' : a.type.toUpperCase()); + html += buildAlertCard(a, icon, 'critical'); + }); + html += '
'; + } + + if (warning.length) { + html += '
Advertencias
' + warning.length + '
'; + html += '
'; + warning.forEach(function (a) { + html += buildAlertCard(a, 'EXCESO', 'warning'); + }); + html += '
'; + } + + if (info.length) { + html += '
Informativas
' + info.length + '
'; + html += '
'; + info.forEach(function (a) { + html += buildAlertCard(a, 'INFO', 'info'); + }); + html += '
'; + } + + container.innerHTML = html; + }); } - // --- History modal --- - async function viewHistory(itemId) { - var data = await apiFetch(API + '/items/' + itemId + '/history'); - if (!data) return; - var history = data.data || []; - var html = ''; - if (!history.length) { html = '

Sin movimientos

'; } - else { - html = ''; - history.forEach(function (h) { - var qtyColor = h.quantity > 0 ? '#16a34a' : '#dc2626'; - html += ''; - }); - html += '
FechaTipoCantidadCostoEmpleadoNotas
' + h.date + '' + h.type + '' + (h.quantity > 0 ? '+' : '') + h.quantity + '' + (h.cost ? '$' + fmt(h.cost) : '-') + '' + esc(h.employee) + '' + esc(h.notes) + '
'; - } - document.getElementById('historyContent').innerHTML = html; - document.getElementById('historyModal').classList.add('show'); + function buildAlertCard(a, icon, level) { + var cls = level === 'critical' ? 'alert-card--critical' : (level === 'warning' ? 'alert-card--warning' : 'alert-card--info'); + return '
' + + '
' + + '
' + + '
[' + icon + '] ' + esc(a.part_number) + ' — ' + esc(a.name) + '
' + + '
Stock: ' + a.stock + + (a.min_stock ? ' (min: ' + a.min_stock + ')' : '') + + (a.max_stock ? ' (max: ' + a.max_stock + ')' : '') + + ' · Sucursal ' + a.branch_id + '
' + + '
'; } - function closeHistoryModal() { document.getElementById('historyModal').classList.remove('show'); } + // ===================================================================== + // HISTORY MODAL + // ===================================================================== - // --- Create modal --- - function showCreateModal() { document.getElementById('createModal').classList.add('show'); } - function closeCreateModal() { document.getElementById('createModal').classList.remove('show'); } + function viewHistory(itemId) { + apiFetch(API + '/items/' + itemId + '/history').then(function (data) { + if (!data) return; + var history = data.data || []; + var html = ''; + if (!history.length) { + html = '

Sin movimientos

'; + } else { + html = ''; + history.forEach(function (h) { + var qtyColor = h.quantity > 0 ? 'var(--color-success)' : 'var(--color-error)'; + html += '' + + '' + + '' + + '' + + '' + + '' + + '' + + ''; + }); + html += '
FechaTipoCantidadCostoEmpleadoNotas
' + esc(h.date) + '' + esc(h.type) + '' + (h.quantity > 0 ? '+' : '') + h.quantity + '' + (h.cost ? '$' + fmt(h.cost) : '—') + '' + esc(h.employee) + '' + esc(h.notes) + '
'; + } + document.getElementById('historyContent').innerHTML = html; + document.getElementById('historyModal').classList.add('is-open'); + }); + } + + function closeHistoryModal() { + document.getElementById('historyModal').classList.remove('is-open'); + } + + // ===================================================================== + // BARCODE LABEL PRINT + // ===================================================================== - // --- Barcode label --- function printBarcode(barcode, partNumber, name) { var w = window.open('', '_blank', 'width=400,height=250'); w.document.write('Etiqueta'); @@ -272,20 +439,27 @@ w.print(); } - // --- Helpers --- - function fmt(n) { return (parseFloat(n) || 0).toFixed(2); } - function esc(s) { if (!s) return ''; var d = document.createElement('div'); d.textContent = s; return d.innerHTML; } + // ===================================================================== + // EXPOSE GLOBALS (for onclick handlers in HTML) + // ===================================================================== - // --- Expose globals --- window._loadItems = function (p) { loadItems(p); }; window.viewHistory = viewHistory; window.closeHistoryModal = closeHistoryModal; window.showCreateModal = showCreateModal; window.closeCreateModal = closeCreateModal; window.createItem = createItem; + window.showPurchaseModal = showPurchaseModal; + window.closePurchaseModal = closePurchaseModal; window.recordPurchase = recordPurchase; + window.showAdjustmentModal = showAdjustmentModal; + window.closeAdjustmentModal = closeAdjustmentModal; window.recordAdjustment = recordAdjustment; + window.showTransferModal = showTransferModal; + window.closeTransferModal = closeTransferModal; window.recordTransfer = recordTransfer; + window.showCountModal = showCountModal; + window.closeCountModal = closeCountModal; window.addCountLine = addCountLine; window.startPhysicalCount = startPhysicalCount; window.approvePhysicalCount = approvePhysicalCount; @@ -293,6 +467,9 @@ window.loadAlerts = loadAlerts; window.printBarcode = printBarcode; - // --- Init --- + // ===================================================================== + // INIT — load stock on page load + // ===================================================================== + loadItems(1); })(); diff --git a/pos/templates/inventory.html b/pos/templates/inventory.html index 3e5270a..413e50b 100644 --- a/pos/templates/inventory.html +++ b/pos/templates/inventory.html @@ -1172,6 +1172,141 @@ padding: var(--space-1); opacity: 0.7; color: inherit; } .banner__dismiss:hover { opacity: 1; } + + /* ========================================================================= + INVENTORY MODALS + ========================================================================= */ + + .inv-modal-overlay { + display: none; + position: fixed; + inset: 0; + z-index: 9000; + background: rgba(0,0,0,0.6); + backdrop-filter: blur(4px); + align-items: center; + justify-content: center; + } + + .inv-modal-overlay.is-open { + display: flex; + } + + .inv-modal { + background: var(--color-bg-elevated); + border: 1px solid var(--color-border); + border-radius: var(--radius-lg); + width: 520px; + max-height: 85vh; + overflow-y: auto; + box-shadow: 0 20px 60px rgba(0,0,0,0.4); + } + + .inv-modal--wide { + width: 700px; + } + + .inv-modal__header { + display: flex; + align-items: center; + justify-content: space-between; + padding: var(--space-4) var(--space-5); + border-bottom: 1px solid var(--color-border); + } + + .inv-modal__header h3 { + font-family: var(--font-heading); + font-weight: var(--heading-weight-primary); + font-size: var(--text-h5); + color: var(--color-text-primary); + margin: 0; + } + + .inv-modal__close { + background: none; + border: none; + font-size: 1.5rem; + color: var(--color-text-muted); + cursor: pointer; + padding: 0 var(--space-1); + line-height: 1; + } + + .inv-modal__close:hover { + color: var(--color-text-primary); + } + + .inv-modal__body { + padding: var(--space-4) var(--space-5); + } + + .inv-modal__footer { + display: flex; + justify-content: flex-end; + gap: var(--space-3); + padding: var(--space-3) var(--space-5); + border-top: 1px solid var(--color-border); + } + + .inv-form-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: var(--space-3); + } + + .inv-field { + display: flex; + flex-direction: column; + gap: var(--space-1); + } + + .inv-field--full { + grid-column: 1 / -1; + } + + .inv-field label { + font-size: var(--text-caption); + font-weight: var(--font-weight-semibold); + color: var(--color-text-muted); + letter-spacing: var(--tracking-wide); + text-transform: uppercase; + } + + .inv-field input { + padding: var(--space-2) var(--space-3); + background: var(--color-surface-1); + border: 1px solid var(--color-border); + border-radius: var(--radius-md); + color: var(--color-text-primary); + font-family: var(--font-body); + font-size: var(--text-body-sm); + } + + .inv-field input:focus { + outline: none; + border-color: var(--color-primary); + box-shadow: 0 0 0 2px var(--color-primary-muted); + } + + .count-row { + display: flex; + gap: var(--space-2); + align-items: center; + margin-bottom: var(--space-2); + } + + .count-row input { + padding: var(--space-2) var(--space-3); + background: var(--color-surface-1); + border: 1px solid var(--color-border); + border-radius: var(--radius-md); + color: var(--color-text-primary); + font-family: var(--font-body); + font-size: var(--text-body-sm); + } + + /* History table inside modal */ + .inv-modal .data-table { width: 100%; } @@ -1430,7 +1565,7 @@
- @@ -1801,7 +1845,7 @@
- @@ -1888,7 +1932,7 @@
- @@ -1983,7 +2027,7 @@
- @@ -2072,7 +2116,7 @@ TAB 7 — ALERTAS =================================================================== -->
- +
Stock Bajo @@ -2251,6 +2295,7 @@
+
@@ -2334,6 +2379,142 @@ })(); + + + +
+
+
+

Nuevo Producto

+ +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+
+ + +
+
+
+

Registrar Compra / Entrada

+ +
+
+
+
+
+
+
+
+
+
+
+ +
+
+ + +
+
+
+

Nuevo Traspaso

+ +
+
+
+
+
+
+
+
+
+
+
+ +
+
+ + +
+
+
+

Nuevo Ajuste

+ +
+
+
+
+
+
+
+
+
+ +
+
+ + +
+
+
+

Conteo Físico

+ +
+
+
+ +
+
+ +
+
+ + +
+
+
+

Historial de Movimientos

+ +
+
+
+ +
+
+