diff --git a/wwwroot/poc-checkout.html b/wwwroot/poc-checkout.html
index c843b6f..102861a 100644
--- a/wwwroot/poc-checkout.html
+++ b/wwwroot/poc-checkout.html
@@ -943,6 +943,96 @@
border-color: #ccc;
}
+ /* ==========================================
+ BARCODE SCANNER
+ ========================================== */
+ .scan-btn {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 10px;
+ width: 100%;
+ padding: 14px 20px;
+ margin-top: 12px;
+ font-size: 13px;
+ font-weight: 600;
+ color: var(--color-teal);
+ background: color-mix(in srgb, var(--color-teal) 8%, white);
+ border: 2px dashed var(--color-teal);
+ border-radius: 6px;
+ cursor: pointer;
+ transition: all 200ms ease;
+ }
+
+ .scan-btn:hover {
+ background: color-mix(in srgb, var(--color-teal) 15%, white);
+ border-style: solid;
+ }
+
+ .scan-btn.scanning {
+ border-color: #1976d2;
+ color: #1976d2;
+ background: color-mix(in srgb, #1976d2 8%, white);
+ animation: pulse-border 1.5s ease-in-out infinite;
+ }
+
+ @keyframes pulse-border {
+ 0%, 100% { border-color: #1976d2; }
+ 50% { border-color: #64b5f6; }
+ }
+
+ .scan-btn svg {
+ width: 18px;
+ height: 18px;
+ fill: currentColor;
+ }
+
+ .scanner-input-hidden {
+ position: absolute;
+ left: -9999px;
+ opacity: 0;
+ }
+
+ /* Debug codes in sidebar */
+ .debug-codes {
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ padding: 12px 16px;
+ border-top: 1px solid var(--color-border);
+ font-size: 11px;
+ color: var(--color-text-secondary);
+ }
+
+ .debug-codes strong {
+ color: var(--color-text);
+ margin-bottom: 4px;
+ font-size: 10px;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+ }
+
+ .debug-codes div {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ }
+
+ .debug-codes code {
+ font-family: var(--font-mono);
+ font-size: 10px;
+ background: var(--color-surface);
+ padding: 2px 6px;
+ border-radius: 3px;
+ cursor: pointer;
+ user-select: all;
+ }
+
+ .debug-codes code:hover {
+ background: var(--color-teal);
+ color: white;
+ }
+
@@ -992,6 +1082,13 @@
Styling›
Olaplex›
+
+
Test EAN-koder:
+
5012345678900 Olaplex No.4
+
8710447489109 Redken Shampoo
+
3474636610143 Kérastase
+
0000000000000 Ukendt
+
@@ -1176,6 +1273,14 @@
+
+
+
+
+
@@ -1653,6 +1758,226 @@
document.addEventListener('keydown', e => {
if (e.key === 'Escape') closePanel();
});
+
+ // ==========================================
+ // BARCODE SCANNER
+ // ==========================================
+
+ // Mock product database for scanning
+ const scanProducts = {
+ '5012345678900': {
+ name: 'Olaplex No.4 Bond Maintenance Shampoo',
+ price: 299,
+ size: '250ml'
+ },
+ '8710447489109': {
+ name: 'Redken All Soft Shampoo',
+ price: 249,
+ size: '300ml'
+ },
+ '3474636610143': {
+ name: 'Kérastase Elixir Ultime',
+ price: 425,
+ size: '100ml'
+ },
+ '0850018802239': {
+ name: 'Olaplex No.7 Bonding Oil',
+ price: 319,
+ size: '30ml'
+ }
+ };
+
+ // Scanner elements
+ const scanButton = document.getElementById('scanButton');
+ const scannerInput = document.getElementById('scannerInput');
+
+ let isScanning = false;
+ let scannedCode = '';
+ let scanTimeout = null;
+
+ // Start scanning
+ scanButton.addEventListener('click', () => {
+ if (isScanning) return;
+
+ isScanning = true;
+ scannedCode = '';
+ scanButton.classList.add('scanning');
+ scanButton.innerHTML = `
+
+ SCANNING...
+ `;
+ scannerInput.value = '';
+ scannerInput.focus();
+ });
+
+ // Handle scanner input
+ scannerInput.addEventListener('input', (e) => {
+ scannedCode = e.target.value;
+
+ if (scanTimeout) clearTimeout(scanTimeout);
+
+ // After 200ms of no input, consider scan complete
+ scanTimeout = setTimeout(() => {
+ if (scannedCode.length >= 8) {
+ processScannedCode(scannedCode);
+ }
+ }, 200);
+ });
+
+ // Handle Enter key from scanner
+ scannerInput.addEventListener('keydown', (e) => {
+ if (e.key === 'Enter' || e.key === 'Tab') {
+ e.preventDefault();
+ if (scannedCode.length >= 8) {
+ clearTimeout(scanTimeout);
+ processScannedCode(scannedCode);
+ }
+ }
+ });
+
+ // Handle blur - reset if no code scanned
+ scannerInput.addEventListener('blur', () => {
+ if (isScanning && scannedCode.length === 0) {
+ setTimeout(() => {
+ if (isScanning && scannedCode.length === 0) {
+ resetScanner();
+ }
+ }, 300);
+ }
+ });
+
+ // Reset scanner to ready state
+ function resetScanner() {
+ isScanning = false;
+ scannedCode = '';
+ scanButton.classList.remove('scanning');
+ scanButton.innerHTML = `
+
+ SCAN PRODUKT
+ `;
+ }
+
+ // Process scanned code
+ async function processScannedCode(code) {
+ // Show loading state
+ scanButton.innerHTML = `
+
+ HENTER...
+ `;
+
+ // Simulate API delay
+ await new Promise(r => setTimeout(r, 600));
+
+ // Look up product
+ const foundProduct = scanProducts[code] || null;
+
+ if (foundProduct) {
+ // Add to cart
+ addScannedProductToCart(foundProduct, code);
+
+ // Show success briefly
+ scanButton.style.borderColor = '#43a047';
+ scanButton.style.color = '#43a047';
+ scanButton.innerHTML = `
+
+ TILFØJET!
+ `;
+
+ setTimeout(() => {
+ scanButton.style.borderColor = '';
+ scanButton.style.color = '';
+ resetScanner();
+ }, 800);
+ } else {
+ // Show not found
+ scanButton.style.borderColor = '#f59e0b';
+ scanButton.style.color = '#f59e0b';
+ scanButton.innerHTML = `
+
+ IKKE FUNDET
+ `;
+
+ setTimeout(() => {
+ scanButton.style.borderColor = '';
+ scanButton.style.color = '';
+ resetScanner();
+ }, 1500);
+ }
+ }
+
+ // Add scanned product to cart visually
+ function addScannedProductToCart(product, ean) {
+ const cartSectionItems = document.querySelector('.cart-section:last-of-type .cart-section-items');
+
+ const newItem = document.createElement('div');
+ newItem.className = 'cart-item';
+ newItem.setAttribute('onclick', 'toggleCartItem(this)');
+ newItem.setAttribute('data-base-price', product.price);
+ newItem.innerHTML = `
+
+
+
+ 1
+
+
+
+
${product.name}
+
${product.size}
+
+
+
${product.price} kr
+
${product.price} kr
+
-0 kr rabat
+
+
+
+
+ `;
+
+ // Insert at end of items list
+ cartSectionItems.appendChild(newItem);
+
+ // Flash the new item
+ newItem.style.animation = 'flash 0.5s ease';
+
+ // Re-attach discount type handlers
+ newItem.querySelectorAll('.discount-type-btn').forEach(btn => {
+ btn.addEventListener('click', (e) => {
+ e.stopPropagation();
+ toggleDiscountType(btn);
+ });
+ });
+ }
+
+ // Add animations
+ const scannerStyle = document.createElement('style');
+ scannerStyle.textContent = `
+ @keyframes flash {
+ 0% { background-color: rgba(0, 137, 123, 0.2); }
+ 100% { background-color: transparent; }
+ }
+ @keyframes spin {
+ to { transform: rotate(360deg); }
+ }
+ `;
+ document.head.appendChild(scannerStyle);