import Quagga from '@ericblade/quagga2' export const useBarcodeScanner = (onScan, targetElementGetter) => { const useScannerMode = ref(false) const showScanner = ref(false) const scannerError = ref('') const isProcessingScan = ref(false) const isInitialized = ref(false) // Load scanner preference from localStorage onMounted(() => { if (typeof window !== 'undefined') { const savedPreference = localStorage.getItem('useBarcodeScanner') useScannerMode.value = savedPreference === 'true' if (useScannerMode.value) { // Delay to allow component to mount setTimeout(() => { startScanner() }, 200) } } }) // Cleanup on unmount onBeforeUnmount(() => { stopScanner() }) const startScanner = async () => { if (!useScannerMode.value) return showScanner.value = true scannerError.value = '' try { // Wait for component to mount await nextTick() // Get the target element from the DOM let targetElement = null let retries = 0 while (!targetElement && retries < 20) { if (targetElementGetter) { targetElement = targetElementGetter() } else { // Fallback: find by class name targetElement = document.querySelector('.scanner-video-container-inline') } if (!targetElement) { await new Promise(resolve => setTimeout(resolve, 100)) retries++ } } if (!targetElement) { scannerError.value = 'Video element not found' return } // Initialize Quagga with back camera preference Quagga.init({ inputStream: { type: 'LiveStream', target: targetElement, constraints: { facingMode: 'environment', // Always prefer back camera width: { min: 640, ideal: 1280, max: 1920 }, height: { min: 480, ideal: 720, max: 1080 } } }, locator: { patchSize: 'medium', halfSample: true }, numOfWorkers: navigator.hardwareConcurrency || 4, decoder: { readers: [ 'code_128_reader', 'ean_reader', 'ean_8_reader', 'code_39_reader', 'code_39_vin_reader', 'codabar_reader', 'upc_reader', 'upc_e_reader', 'i2of5_reader' ], multiple: false }, locate: true }, (err) => { if (err) { console.error('Quagga initialization error:', err) scannerError.value = 'Failed to start camera: ' + err.message return } isInitialized.value = true Quagga.start() // Add detection handler Quagga.onDetected((result) => { if (result && result.codeResult && !isProcessingScan.value) { isProcessingScan.value = true const scannedCode = result.codeResult.code // Call the callback with scanned code if (onScan) { onScan(scannedCode) } // Prevent scanning for 1.15 seconds to avoid duplicate scans setTimeout(() => { isProcessingScan.value = false }, 1150) } }) }) } catch (err) { console.error('Scanner error:', err) scannerError.value = 'Failed to start camera: ' + err.message } } const stopScanner = () => { if (isInitialized.value) { Quagga.stop() Quagga.offDetected() isInitialized.value = false } showScanner.value = false scannerError.value = '' } return { useScannerMode, showScanner, scannerError, isProcessingScan, startScanner, stopScanner } }