const MAX_BYTES = 10 * 1024 * 1024; const ALLOWED_EXTENSIONS = /\.(jpe?g|png|heic)$/i; const form = document.querySelector('#upload-form'); const input = document.querySelector('#image-input'); const dropZone = document.querySelector('#drop-zone'); const fileRow = document.querySelector('#file-row'); const fileName = document.querySelector('#file-name'); const fileSize = document.querySelector('#file-size'); const preview = document.querySelector('#preview'); const removeButton = document.querySelector('#remove-button'); const submitButton = document.querySelector('#submit-button'); const status = document.querySelector('#status'); let previewUrl = null; function setStatus(message = '', type = '') { status.textContent = message; status.className = `status ${type}`.trim(); } function clearSelection() { input.value = ''; fileRow.hidden = true; dropZone.hidden = false; submitButton.disabled = true; if (previewUrl) URL.revokeObjectURL(previewUrl); previewUrl = null; preview.removeAttribute('src'); } function validate(file) { if (!file) return 'Choose an image to continue.'; if (!ALLOWED_EXTENSIONS.test(file.name)) return 'Choose a JPG, PNG, or HEIC image.'; if (file.size === 0) return 'The selected image is empty.'; if (file.size > MAX_BYTES) return 'The image must be 10 MB or smaller.'; return ''; } function showFile(file) { const error = validate(file); if (error) { clearSelection(); setStatus(error, 'error'); return; } setStatus(); fileName.textContent = file.name; fileSize.textContent = `${(file.size / 1024 / 1024).toFixed(2)} MB`; if (previewUrl) URL.revokeObjectURL(previewUrl); previewUrl = URL.createObjectURL(file); preview.src = previewUrl; fileRow.hidden = false; dropZone.hidden = true; submitButton.disabled = false; } input.addEventListener('change', () => showFile(input.files[0])); removeButton.addEventListener('click', () => { clearSelection(); setStatus(); }); for (const eventName of ['dragenter', 'dragover']) { dropZone.addEventListener(eventName, (event) => { event.preventDefault(); dropZone.classList.add('is-dragging'); }); } for (const eventName of ['dragleave', 'drop']) { dropZone.addEventListener(eventName, (event) => { event.preventDefault(); dropZone.classList.remove('is-dragging'); }); } dropZone.addEventListener('drop', (event) => { const files = event.dataTransfer.files; if (files.length !== 1) { setStatus('Drop exactly one image.', 'error'); return; } const transfer = new DataTransfer(); transfer.items.add(files[0]); input.files = transfer.files; showFile(files[0]); }); form.addEventListener('submit', async (event) => { event.preventDefault(); const file = input.files[0]; const error = validate(file); if (error) { setStatus(error, 'error'); return; } submitButton.disabled = true; submitButton.querySelector('span').textContent = 'Submitting…'; setStatus('Securely sending your image…'); const body = new FormData(); body.append('image', file); try { const response = await fetch(window.location.pathname, { method: 'POST', body }); const result = await response.json(); if (!response.ok) throw new Error(result.message || 'Upload failed.'); clearSelection(); setStatus(`${result.message} Receipt: ${result.receipt}`, 'success'); } catch (error) { submitButton.disabled = false; setStatus(error.message || 'Upload failed. Please try again.', 'error'); } finally { submitButton.querySelector('span').textContent = 'Submit image'; } });