document.addEventListener('DOMContentLoaded', () => { // --- DOM Element References --- const videoContainer = document.getElementById('alp-video-container'); const video = document.getElementById('alp-video'); const canvas = document.getElementById('alp-canvas'); const overlay = document.getElementById('alp-viewfinder-overlay'); const overlayText = document.getElementById('alp-overlay-text'); const cameraSelector = document.getElementById('alp-camera-selector'); const manualInputForm = document.getElementById('alp-manual-form'); const productInfoSection = document.getElementById('alp-product-info'); const productNameEl = document.getElementById('alp-product-name'); const productPricesEl = document.getElementById('alp-product-prices'); const existingPhotosSection = document.getElementById('alp-existing-photos'); const existingPhotosContainer = document.getElementById('alp-photos-container'); const messageArea = document.getElementById('alp-message-area'); // --- State Management --- const AppState = { IDLE: 'idle', // Camera off, welcome message READY_TO_SCAN: 'ready_to_scan', // Camera on, waiting for tap to scan SCANNING: 'scanning', // Actively looking for barcode PRODUCT_FOUND: 'product_found', // Product found, waiting for tap to take photo UPLOADING: 'uploading' // Photo is being sent to server }; let currentState = AppState.IDLE; let currentStream = null; let barcodeDetector = null; let currentProductId = null; const ajaxUrl = window.addLivePhotoAjaxUrl || ''; // --- Initialization --- if (!('BarcodeDetector' in window)) { showMessage('Barcode Detector API is not supported. Please use manual input.', true); } else { barcodeDetector = new BarcodeDetector({ formats: ['ean_13'] }); } if (!navigator.mediaDevices) { showMessage('Camera access is not supported in this browser.', true); } else { populateCameraSelector(); } updateUIForState(AppState.IDLE); // Set initial UI state // --- Event Listeners --- videoContainer.addEventListener('click', handleViewfinderTap); cameraSelector.addEventListener('change', handleCameraChange); manualInputForm.addEventListener('submit', handleManualSubmit); existingPhotosContainer.addEventListener('click', handleDeleteClick); // --- Core Logic --- function handleViewfinderTap() { switch (currentState) { case AppState.IDLE: startCamera(); break; case AppState.READY_TO_SCAN: detectBarcode(); break; case AppState.PRODUCT_FOUND: takePhoto(); break; } } function updateUIForState(newState, customText = null) { currentState = newState; let textContent = ''; overlay.style.display = 'flex'; switch (newState) { case AppState.IDLE: textContent = "Tap to Start Camera"; break; case AppState.READY_TO_SCAN: textContent = "Tap to Scan Barcode"; break; case AppState.SCANNING: textContent = `
`; break; case AppState.PRODUCT_FOUND: textContent = "Tap to Take Picture"; break; case AppState.UPLOADING: textContent = "Uploading..."; break; } overlayText.innerHTML = customText || textContent; } async function startCamera() { if (currentStream) return; const constraints = { video: { deviceId: cameraSelector.value ? { exact: cameraSelector.value } : undefined, facingMode: 'environment' } }; try { currentStream = await navigator.mediaDevices.getUserMedia(constraints); video.srcObject = currentStream; await video.play(); updateUIForState(AppState.READY_TO_SCAN); } catch (err) { console.error('Error accessing camera:', err); stopCamera(); // Ensure everything is reset updateUIForState(AppState.IDLE, 'Camera Error. Tap to retry.'); } } function stopCamera() { if (currentStream) { currentStream.getTracks().forEach(track => track.stop()); currentStream = null; } video.srcObject = null; updateUIForState(AppState.IDLE); } async function detectBarcode() { if (!barcodeDetector || currentState !== AppState.READY_TO_SCAN) return; updateUIForState(AppState.SCANNING); try { const barcodes = await barcodeDetector.detect(video); if (barcodes.length > 0) { searchProduct(barcodes[0].rawValue); } else { showMessage('No barcode found. Please try again.', true); updateUIForState(AppState.READY_TO_SCAN); } } catch (err) { console.error('Barcode detection error:', err); showMessage('Error during barcode detection.', true); updateUIForState(AppState.READY_TO_SCAN); } } function takePhoto() { if (!currentStream || !currentProductId || currentState !== AppState.PRODUCT_FOUND) return; updateUIForState(AppState.UPLOADING); const targetWidth = 800, targetHeight = 800; canvas.width = targetWidth; canvas.height = targetHeight; const ctx = canvas.getContext('2d'); const videoWidth = video.videoWidth, videoHeight = video.videoHeight; const size = Math.min(videoWidth, videoHeight); const x = (videoWidth - size) / 2, y = (videoHeight - size) / 2; ctx.drawImage(video, x, y, size, size, 0, 0, targetWidth, targetHeight); const imageData = canvas.toDataURL('image/webp', 0.8); uploadImage(imageData); } function resetForNextProduct() { currentProductId = null; productInfoSection.style.display = 'none'; existingPhotosSection.style.display = 'none'; existingPhotosContainer.innerHTML = ''; updateUIForState(AppState.READY_TO_SCAN); } // --- AJAX and Helper Functions --- async function searchProduct(identifier) { const formData = new FormData(); formData.append('action', 'searchProduct'); formData.append('identifier', identifier); try { const response = await fetch(ajaxUrl, { method: 'POST', body: formData }); const result = await response.json(); if (result.success) { const product = result.data; currentProductId = product.id_product; displayProductInfo(product); updateUIForState(AppState.PRODUCT_FOUND); } else { showMessage(result.message, true); updateUIForState(AppState.READY_TO_SCAN); } } catch (err) { showMessage('Network error searching for product.', true); updateUIForState(AppState.READY_TO_SCAN); } } async function uploadImage(imageData) { const formData = new FormData(); formData.append('action', 'uploadImage'); formData.append('id_product', currentProductId); formData.append('imageData', imageData); try { const response = await fetch(ajaxUrl, { method: 'POST', body: formData }); const result = await response.json(); if (result.success) { showMessage(result.message, false); appendNewPhoto(result.data.new_photo); setTimeout(resetForNextProduct, 1500); // Pause to show success, then reset } else { showMessage(result.message, true); updateUIForState(AppState.PRODUCT_FOUND); // Allow user to try photo again } } catch (err) { showMessage('Network error uploading photo.', true); updateUIForState(AppState.PRODUCT_FOUND); } } async function populateCameraSelector() { /* (This function can remain from previous versions) */ } function handleCameraChange() { /* (This function can remain from previous versions) */ } function handleManualSubmit(e) { /* (This function can remain from previous versions) */ } function handleDeleteClick(e) { /* (This function can remain from previous versions) */ } function displayProductInfo(product) { /* (This function can remain from previous versions) */ } function appendNewPhoto(photo) { /* (This function can remain from previous versions) */ } function showMessage(text, isError = false) { /* (This function can remain from previous versions) */ } // --- Re-pasting the helper functions for completeness --- async function populateCameraSelector() { try { const devices = await navigator.mediaDevices.enumerateDevices(); const videoDevices = devices.filter(device => device.kind === 'videoinput'); cameraSelector.innerHTML = ''; videoDevices.forEach((device, index) => { const option = document.createElement('option'); option.value = device.deviceId; option.textContent = device.label || `Camera ${index + 1}`; cameraSelector.appendChild(option); }); const preferredCameraId = localStorage.getItem('addLivePhoto_preferredCameraId'); if (preferredCameraId && cameraSelector.querySelector(`option[value="${preferredCameraId}"]`)) { cameraSelector.value = preferredCameraId; } } catch (err) { console.error('Error enumerating devices:', err); } } function handleCameraChange() { localStorage.setItem('addLivePhoto_preferredCameraId', cameraSelector.value); if (currentStream) { // If camera is active, restart it with the new selection stopCamera(); startCamera(); } } function handleManualSubmit(e) { e.preventDefault(); const identifier = document.getElementById('alp-manual-identifier').value.trim(); if (identifier) { showMessage(`Searching for: ${identifier}...`); searchProduct(identifier); } } function handleDeleteClick(e) { if (e.target && e.target.classList.contains('delete-photo-btn')) { const button = e.target; const imageName = button.dataset.imageName; const productId = button.dataset.productId; if (confirm(`Are you sure you want to delete this photo?`)) { // Simplified delete without a dedicated function const formData = new FormData(); formData.append('action', 'deleteImage'); formData.append('id_product', productId); formData.append('image_name', imageName); fetch(ajaxUrl, { method: 'POST', body: formData }) .then(res => res.json()) .then(result => { if (result.success) { showMessage(result.message, false); button.closest('.photo-thumb').remove(); } else { showMessage(result.message, true); } }).catch(err => showMessage('Network error deleting photo.', true)); } } } function displayProductInfo(product) { productNameEl.textContent = `[ID: ${product.id_product}] ${product.name}`; let pricesHtml = `Wholesale: ${product.wholesale_price} | Sale: ${product.retail_price}`; if (product.discounted_price) { pricesHtml += ` | Discounted: ${product.discounted_price}`; } productPricesEl.innerHTML = pricesHtml; renderExistingPhotos(product.existing_photos, product.id_product); productInfoSection.style.display = 'block'; } function renderExistingPhotos(photos, productId) { existingPhotosContainer.innerHTML = ''; if (photos && photos.length > 0) { existingPhotosSection.style.display = 'block'; photos.forEach(photo => appendNewPhoto(photo, productId)); } else { existingPhotosSection.style.display = 'none'; } } function appendNewPhoto(photo, productId = currentProductId) { const thumbDiv = document.createElement('div'); thumbDiv.className = 'photo-thumb'; thumbDiv.innerHTML = ` Live photo `; existingPhotosContainer.prepend(thumbDiv); existingPhotosSection.style.display = 'block'; } function showMessage(text, isError = false) { messageArea.textContent = text; messageArea.className = isError ? 'alert alert-danger' : 'alert alert-info'; messageArea.style.display = 'block'; setTimeout(() => { messageArea.style.display = 'none'; }, 4000); // Message disappears after 4s } });