Checkout UI fixes
This commit is contained in:
@@ -427,6 +427,29 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="modal fade" id="notFoundModal" tabindex="-1">
|
||||||
|
<div class="modal-dialog modal-dialog-centered">
|
||||||
|
<div class="modal-content border-warning">
|
||||||
|
<div class="modal-header border-0 pb-0">
|
||||||
|
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body text-center pt-0 pb-4">
|
||||||
|
<i class="bi bi-question-circle text-warning mb-3" style="font-size: 3rem;"></i>
|
||||||
|
<h4 class="mb-2">Producto No Registrado</h4>
|
||||||
|
<p class="text-muted px-3 small">El código <strong id="not-found-barcode" style="color: var(--text-main);"></strong> no existe en la base de datos.</p>
|
||||||
|
|
||||||
|
<div class="d-flex flex-column gap-2 px-3 mt-4">
|
||||||
|
<button class="btn btn-accent w-100 py-2" onclick="goToInventory()">
|
||||||
|
<i class="bi bi-database-add me-2"></i>Registrar en Inventario
|
||||||
|
</button>
|
||||||
|
<button class="btn btn-outline-secondary w-100 py-2" onclick="openTempProduct()">
|
||||||
|
<i class="bi bi-cart-plus me-2"></i>Venta Temporal (Solo por esta vez)
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<nav class="navbar navbar-expand-md sticky-top px-3 mb-3">
|
<nav class="navbar navbar-expand-md sticky-top px-3 mb-3">
|
||||||
<span class="navbar-brand">SekiPOS <small class="text-muted fw-normal"
|
<span class="navbar-brand">SekiPOS <small class="text-muted fw-normal"
|
||||||
style="font-size:0.65rem;">Caja</small></span>
|
style="font-size:0.65rem;">Caja</small></span>
|
||||||
@@ -547,17 +570,50 @@
|
|||||||
<script>
|
<script>
|
||||||
let editingCartIndex = null;
|
let editingCartIndex = null;
|
||||||
let itemIndexToRemove = null;
|
let itemIndexToRemove = null;
|
||||||
|
|
||||||
|
let missingProductData = null;
|
||||||
|
let tempBarcode = null; // Stores the real barcode if we do a temp sale
|
||||||
|
|
||||||
const socket = io();
|
const socket = io();
|
||||||
let cart = [];
|
let cart = [];
|
||||||
let pendingProduct = null;
|
let pendingProduct = null;
|
||||||
const clp = new Intl.NumberFormat('es-CL', { style: 'currency', currency: 'CLP', minimumFractionDigits: 0 });
|
const clp = new Intl.NumberFormat('es-CL', { style: 'currency', currency: 'CLP', minimumFractionDigits: 0 });
|
||||||
|
|
||||||
socket.on('scan_error', (data) => {
|
socket.on('scan_error', (data) => {
|
||||||
if (confirm("Producto no encontrado. ¿Deseas crearlo?")) {
|
missingProductData = data;
|
||||||
window.location.href = `/?barcode=${data.barcode}`;
|
document.getElementById('not-found-barcode').innerText = data.barcode;
|
||||||
}
|
|
||||||
|
const modal = bootstrap.Modal.getOrCreateInstance(document.getElementById('notFoundModal'));
|
||||||
|
modal.show();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function goToInventory() {
|
||||||
|
if (missingProductData) {
|
||||||
|
window.location.href = `/?barcode=${missingProductData.barcode}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function openTempProduct() {
|
||||||
|
bootstrap.Modal.getInstance(document.getElementById('notFoundModal')).hide();
|
||||||
|
|
||||||
|
// Save the actual scanned barcode so it prints on the receipt instead of "MANUAL-XXX"
|
||||||
|
tempBarcode = missingProductData.barcode;
|
||||||
|
|
||||||
|
// Pre-fill the name if the OpenFoodFacts API managed to find it
|
||||||
|
document.getElementById('custom-name').value = missingProductData.name || '';
|
||||||
|
document.getElementById('custom-price').value = '';
|
||||||
|
document.getElementById('custom-unit').value = 'unit';
|
||||||
|
|
||||||
|
const customModal = bootstrap.Modal.getOrCreateInstance(document.getElementById('customProductModal'));
|
||||||
|
customModal.show();
|
||||||
|
|
||||||
|
// Auto-focus the price if we already have a name, otherwise focus the name
|
||||||
|
setTimeout(() => {
|
||||||
|
if (missingProductData.name) document.getElementById('custom-price').focus();
|
||||||
|
else document.getElementById('custom-name').focus();
|
||||||
|
}, 500);
|
||||||
|
}
|
||||||
|
|
||||||
function renderCart() {
|
function renderCart() {
|
||||||
const tbody = document.getElementById('cart-items');
|
const tbody = document.getElementById('cart-items');
|
||||||
tbody.innerHTML = '';
|
tbody.innerHTML = '';
|
||||||
@@ -935,6 +991,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
function openCustomProductModal() {
|
function openCustomProductModal() {
|
||||||
|
|
||||||
|
tempBarcode = null; // Clear any leftover scanned barcodes
|
||||||
|
|
||||||
// Scrub the inputs clean
|
// Scrub the inputs clean
|
||||||
document.getElementById('custom-name').value = '';
|
document.getElementById('custom-name').value = '';
|
||||||
document.getElementById('custom-price').value = '';
|
document.getElementById('custom-price').value = '';
|
||||||
@@ -955,12 +1014,12 @@
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a unique dummy barcode so the cart math doesn't implode
|
// Use the scanned barcode if it exists, otherwise generate a fake one
|
||||||
const fakeBarcode = `MANUAL-${Date.now().toString().slice(-6)}`;
|
const finalBarcode = tempBarcode ? tempBarcode : `MANUAL-${Date.now().toString().slice(-6)}`;
|
||||||
|
|
||||||
const customProduct = {
|
const customProduct = {
|
||||||
barcode: fakeBarcode,
|
barcode: finalBarcode,
|
||||||
name: `* ${nameInput}`, // Adds a star to the receipt so you know it was manual
|
name: `* ${nameInput}`,
|
||||||
price: priceInput,
|
price: priceInput,
|
||||||
image: '',
|
image: '',
|
||||||
stock: 0,
|
stock: 0,
|
||||||
@@ -968,7 +1027,6 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (unitInput === 'kg') {
|
if (unitInput === 'kg') {
|
||||||
// Send it to your existing weight modal flow
|
|
||||||
pendingProduct = customProduct;
|
pendingProduct = customProduct;
|
||||||
bootstrap.Modal.getInstance(document.getElementById('customProductModal')).hide();
|
bootstrap.Modal.getInstance(document.getElementById('customProductModal')).hide();
|
||||||
|
|
||||||
@@ -976,10 +1034,11 @@
|
|||||||
weightModal.show();
|
weightModal.show();
|
||||||
setTimeout(() => document.getElementById('weight-input').focus(), 500);
|
setTimeout(() => document.getElementById('weight-input').focus(), 500);
|
||||||
} else {
|
} else {
|
||||||
// Add directly as 1 unit
|
|
||||||
addToCart(customProduct, 1);
|
addToCart(customProduct, 1);
|
||||||
bootstrap.Modal.getInstance(document.getElementById('customProductModal')).hide();
|
bootstrap.Modal.getInstance(document.getElementById('customProductModal')).hide();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
tempBarcode = null; // Reset it after adding
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the listener is intact
|
// Ensure the listener is intact
|
||||||
@@ -1047,9 +1106,21 @@
|
|||||||
|
|
||||||
// Initialize on load
|
// Initialize on load
|
||||||
(function initTheme() {
|
(function initTheme() {
|
||||||
const savedTheme = localStorage.getItem('theme') || 'light';
|
const savedTheme = localStorage.getItem('theme');
|
||||||
applyTheme(savedTheme);
|
if (savedTheme) {
|
||||||
|
applyTheme(savedTheme);
|
||||||
|
} else {
|
||||||
|
const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||||
|
applyTheme(prefersDark ? 'dark' : 'light');
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
// Listen for OS theme changes in real-time
|
||||||
|
window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {
|
||||||
|
if (!localStorage.getItem('theme')) {
|
||||||
|
applyTheme(e.matches ? 'dark' : 'light');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
loadCart();
|
loadCart();
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
Reference in New Issue
Block a user