document.addEventListener('DOMContentLoaded', () => { // 1. Elements const video = document.getElementById('alp-video'); const canvas = document.getElementById('alp-canvas'); const overlayText = document.getElementById('alp-status-text'); const stepScan = document.getElementById('alp-step-scan'); const stepAction = document.getElementById('alp-step-action'); // Product Data Elements const productNameEl = document.getElementById('alp-product-name'); const photoListEl = document.getElementById('alp-photo-list'); // Buttons const manualForm = document.getElementById('alp-manual-form'); const btnExpiry = document.getElementById('btn-snap-expiry'); const btnPkg = document.getElementById('btn-snap-packaging'); const btnReset = document.getElementById('btn-reset'); // State let currentStream = null; let barcodeDetector = null; let isScanning = false; let currentProductId = null; // 2. Initialize initBarcodeDetector(); startCamera(); // 3. Event Listeners manualForm.addEventListener('submit', (e) => { e.preventDefault(); const val = document.getElementById('alp-manual-input').value.trim(); if(val) fetchProduct(val); }); btnReset.addEventListener('click', resetApp); btnExpiry.addEventListener('click', () => takePhoto('expiry')); btnPkg.addEventListener('click', () => takePhoto('packaging')); // --- Core Functions --- async function initBarcodeDetector() { if ('BarcodeDetector' in window) { // Check supported formats const formats = await BarcodeDetector.getSupportedFormats(); if (formats.includes('ean_13')) { barcodeDetector = new BarcodeDetector({ formats: ['ean_13'] }); console.log('BarcodeDetector ready'); } else { overlayText.textContent = "EAN13 not supported by device"; } } else { console.warn('BarcodeDetector API not supported in this browser'); overlayText.textContent = "Auto-scan not supported. Use manual input."; } } async function startCamera() { try { const constraints = { video: { facingMode: 'environment', // Rear camera width: { ideal: 1280 }, height: { ideal: 720 } } }; currentStream = await navigator.mediaDevices.getUserMedia(constraints); video.srcObject = currentStream; // Wait for video to be ready video.onloadedmetadata = () => { video.play(); if(barcodeDetector) { isScanning = true; overlayText.textContent = "Scan Barcode..."; scanLoop(); } else { overlayText.textContent = "Camera Ready (Manual Mode)"; } }; } catch (err) { console.error(err); overlayText.textContent = "Camera Access Denied or Error"; } } async function scanLoop() { if (!isScanning || !barcodeDetector || currentProductId) return; try { const barcodes = await barcodeDetector.detect(video); if (barcodes.length > 0) { const code = barcodes[0].rawValue; isScanning = false; // Stop scanning fetchProduct(code); return; } } catch (e) { // Detection error (common in loop) } // Scan every 200ms to save battery setTimeout(() => requestAnimationFrame(scanLoop), 200); } function fetchProduct(identifier) { overlayText.textContent = "Searching..."; isScanning = false; const fd = new FormData(); fd.append('action', 'searchProduct'); fd.append('identifier', identifier); fetch(window.alpAjaxUrl, { method: 'POST', body: fd }) .then(res => res.json()) .then(data => { if (data.success) { loadProductView(data.data); } else { alert(data.message || 'Product not found'); resetApp(); // Go back to scanning } }) .catch(err => { console.error(err); alert('Network Error'); resetApp(); }); } function loadProductView(productData) { currentProductId = productData.id_product; productNameEl.textContent = `[${productData.reference}] ${productData.name}`; renderPhotos(productData.existing_photos); // Switch View stepScan.style.display = 'none'; stepAction.style.display = 'block'; } function takePhoto(type) { if (!currentProductId) return; // Capture frame const w = video.videoWidth; const h = video.videoHeight; // Crop to square (center) const size = Math.min(w, h); const x = (w - size) / 2; const y = (h - size) / 2; canvas.width = 800; canvas.height = 800; const ctx = canvas.getContext('2d'); ctx.drawImage(video, x, y, size, size, 0, 0, 800, 800); const dataUrl = canvas.toDataURL('image/webp', 0.8); // Upload const fd = new FormData(); fd.append('action', 'uploadImage'); fd.append('id_product', currentProductId); fd.append('image_type', type); fd.append('imageData', dataUrl); // Visual feedback const btn = (type === 'expiry') ? btnExpiry : btnPkg; const originalText = btn.innerHTML; btn.innerHTML = 'Uploading...'; btn.disabled = true; fetch(window.alpAjaxUrl, { method: 'POST', body: fd }) .then(res => res.json()) .then(data => { if (data.success) { // Add new photo to list without reload addPhotoToDom(data.photo); // Flash success message alert(`Saved as ${type}!`); } else { alert('Error: ' + data.message); } }) .catch(err => alert('Upload failed')) .finally(() => { btn.innerHTML = originalText; btn.disabled = false; }); } function renderPhotos(photos) { photoListEl.innerHTML = ''; if(photos && photos.length) { photos.forEach(addPhotoToDom); } else { photoListEl.innerHTML = '
No photos yet.
'; } } function addPhotoToDom(photo) { // Remove "No photos" msg if exists if (photoListEl.querySelector('p')) photoListEl.innerHTML = ''; const div = document.createElement('div'); div.className = 'alp-thumb'; const badgeClass = (photo.type === 'expiry') ? 'badge-success' : 'badge-info'; div.innerHTML = `