window.Upload = { frontFile: null, backFile: null, isProcessing: false, init: function () { var self = this; // Doc type selector — show/hide back upload var docTypeSelect = Utils.$('#doc-type-select'); docTypeSelect.addEventListener('change', function () { self._updateLayout(); self._updateProcessButton(); }); // Front drop zone this._setupDropZone('front'); // Back drop zone this._setupDropZone('back'); // Process button Utils.$('#btn-process').addEventListener('click', function () { self.processDocument(); }); this._updateLayout(); }, _setupDropZone: function (side) { var self = this; var dropZone = Utils.$('#drop-zone-' + side); var fileInput = Utils.$('#file-input-' + side); // Click to browse (skip in camera-only mode) dropZone.addEventListener('click', function (e) { if (e.target.classList.contains('btn-remove')) return; if (e.target.classList.contains('btn-camera')) return; if (dropZone.classList.contains('camera-only')) return; if (!self.isProcessing) fileInput.click(); }); dropZone.addEventListener('keydown', function (e) { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); if (dropZone.classList.contains('camera-only')) return; if (!self.isProcessing) fileInput.click(); } }); // File input change fileInput.addEventListener('change', function () { if (fileInput.files.length > 0) { self._setFile(side, fileInput.files[0]); } fileInput.value = ''; }); // Drag and drop dropZone.addEventListener('dragover', function (e) { e.preventDefault(); if (!dropZone.classList.contains('camera-only')) { dropZone.classList.add('drag-active'); } }); dropZone.addEventListener('dragleave', function () { dropZone.classList.remove('drag-active'); }); dropZone.addEventListener('drop', function (e) { e.preventDefault(); dropZone.classList.remove('drag-active'); if (dropZone.classList.contains('camera-only')) return; if (!self.isProcessing && e.dataTransfer.files.length > 0) { self._setFile(side, e.dataTransfer.files[0]); } }); // Remove button var removeBtn = Utils.$('.btn-remove', dropZone); if (removeBtn) { removeBtn.addEventListener('click', function (e) { e.stopPropagation(); self._clearFile(side); }); } // Camera buttons (may be multiple: one in content, one in camera-only) var cameraBtns = dropZone.querySelectorAll('.btn-camera'); for (var i = 0; i < cameraBtns.length; i++) { cameraBtns[i].addEventListener('click', function (e) { e.stopPropagation(); if (!self.isProcessing) { Camera.open(side); } }); } }, _setFile: function (side, file) { var check = Utils.validateFile(file); if (!check.valid) { Utils.showToast(check.error, 'error'); return; } if (side === 'front') { this.frontFile = file; } else { this.backFile = file; } // Show preview var dropZone = Utils.$('#drop-zone-' + side); var content = Utils.$('.drop-zone-content', dropZone); var preview = Utils.$('.drop-zone-preview', dropZone); var thumb = Utils.$('.preview-thumb', dropZone); var nameEl = Utils.$('.preview-name', dropZone); content.classList.add('hidden'); var cameraOnly = Utils.$('.drop-zone-camera-only', dropZone); if (cameraOnly) cameraOnly.classList.add('hidden'); preview.classList.remove('hidden'); thumb.src = URL.createObjectURL(file); nameEl.textContent = file.name; this._updateProcessButton(); }, _clearFile: function (side) { if (side === 'front') { this.frontFile = null; } else { this.backFile = null; } var dropZone = Utils.$('#drop-zone-' + side); var content = Utils.$('.drop-zone-content', dropZone); var preview = Utils.$('.drop-zone-preview', dropZone); var thumb = Utils.$('.preview-thumb', dropZone); var cameraOnly = Utils.$('.drop-zone-camera-only', dropZone); var docType = Utils.$('#doc-type-select').value; if (side === 'front' && docType === 'product_serial' && cameraOnly) { content.classList.add('hidden'); cameraOnly.classList.remove('hidden'); } else { content.classList.remove('hidden'); if (cameraOnly) cameraOnly.classList.add('hidden'); } preview.classList.add('hidden'); if (thumb.src) { URL.revokeObjectURL(thumb.src); thumb.src = ''; } this._updateProcessButton(); }, _updateLayout: function () { var docType = Utils.$('#doc-type-select').value; var backGroup = Utils.$('#back-upload-group'); var frontLabel = Utils.$('.upload-label'); var isSerial = docType === 'product_serial'; if (docType === 'sa_id_card') { backGroup.classList.remove('hidden'); } else { backGroup.classList.add('hidden'); this._clearFile('back'); } if (frontLabel) { frontLabel.innerHTML = isSerial ? 'Product Image *' : 'Front Image *'; } // Serial mode: camera-only for front drop zone var frontDropZone = Utils.$('#drop-zone-front'); if (isSerial) { this._clearFile('front'); frontDropZone.classList.add('camera-only'); } else { var frontPreview = Utils.$('.drop-zone-preview', frontDropZone); if (frontPreview.classList.contains('hidden')) { Utils.$('.drop-zone-content', frontDropZone).classList.remove('hidden'); } var frontCameraOnly = Utils.$('.drop-zone-camera-only', frontDropZone); if (frontCameraOnly) frontCameraOnly.classList.add('hidden'); frontDropZone.classList.remove('camera-only'); } }, _updateProcessButton: function () { var btn = Utils.$('#btn-process'); btn.disabled = !this.frontFile || this.isProcessing; }, processDocument: function () { if (!this.frontFile || this.isProcessing) return; var self = this; var docType = Utils.$('#doc-type-select').value; this.isProcessing = true; this._updateProcessButton(); Results.showLoading(this.frontFile); var startTime = performance.now(); var isSerial = docType === 'product_serial'; var apiCall = isSerial ? Api.scanSerial(this.frontFile) : Api.processDocument(this.frontFile, docType, this.backFile); apiCall .then(function (result) { var duration = performance.now() - startTime; if (isSerial) { Results.renderSerial(result, self.frontFile); } else { Results.render(result, self.frontFile); } History.add(History.buildEntry(result, self.frontFile, duration, null)); Utils.showToast(isSerial ? 'Serial number processed' : 'Document processed successfully', 'success'); }) .catch(function (err) { var duration = performance.now() - startTime; Results.showError(err.message); History.add(History.buildEntry(null, self.frontFile, duration, err.message)); if (err.status === 401) { Utils.$('#api-key-input').classList.add('error'); Utils.showToast('Authentication failed. Check your API key.', 'error'); } else if (err.status === 429) { Utils.showToast('Rate limit exceeded. Wait a moment and try again.', 'warning'); } else { Utils.showToast(err.message, 'error'); } }) .finally(function () { self.isProcessing = false; self._updateProcessButton(); }); }, };