Spaces:
Sleeping
Sleeping
| document.addEventListener('DOMContentLoaded', () => { | |
| const uploadArea = document.getElementById('upload-area'); | |
| const fileInput = document.getElementById('file-input'); | |
| const imagePreview = document.getElementById('image-preview'); | |
| const uploadContent = document.querySelector('.upload-content'); | |
| const analyzeBtn = document.getElementById('analyze-btn'); | |
| const btnText = document.querySelector('.btn-text'); | |
| const loader = document.querySelector('.loader'); | |
| const resultsPlaceholder = document.getElementById('results-placeholder'); | |
| const resultsContent = document.getElementById('results-content'); | |
| const mainDisease = document.getElementById('main-disease'); | |
| const chartCanvas = document.getElementById('resultsChart'); | |
| const cropsGallery = document.getElementById('crops-gallery'); | |
| let selectedFile = null; | |
| let chartInstance = null; | |
| // Theming Colors for the Chart | |
| const chartColors = [ | |
| '#2f855a', // Green | |
| '#8b5a2b', // Brown | |
| '#48bb78', // Light Green | |
| '#b7791f', // Yellow-Brown | |
| '#276749' // Dark Green | |
| ]; | |
| // Drag and Drop Events | |
| ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { | |
| uploadArea.addEventListener(eventName, preventDefaults, false); | |
| }); | |
| function preventDefaults(e) { | |
| e.preventDefault(); | |
| e.stopPropagation(); | |
| } | |
| ['dragenter', 'dragover'].forEach(eventName => { | |
| uploadArea.addEventListener(eventName, () => uploadArea.classList.add('dragover'), false); | |
| }); | |
| ['dragleave', 'drop'].forEach(eventName => { | |
| uploadArea.addEventListener(eventName, () => uploadArea.classList.remove('dragover'), false); | |
| }); | |
| uploadArea.addEventListener('drop', handleDrop, false); | |
| uploadArea.addEventListener('click', () => fileInput.click()); | |
| fileInput.addEventListener('change', handleFileSelect); | |
| function handleDrop(e) { | |
| const dt = e.dataTransfer; | |
| const file = dt.files[0]; | |
| handleFile(file); | |
| } | |
| function handleFileSelect(e) { | |
| const file = e.target.files[0]; | |
| handleFile(file); | |
| } | |
| function handleFile(file) { | |
| if (file && file.type.startsWith('image/')) { | |
| selectedFile = file; | |
| // Show preview | |
| const reader = new FileReader(); | |
| reader.onload = (e) => { | |
| imagePreview.src = e.target.result; | |
| imagePreview.classList.remove('hidden'); | |
| uploadContent.classList.add('hidden'); | |
| analyzeBtn.disabled = false; | |
| } | |
| reader.readAsDataURL(file); | |
| } else { | |
| alert('Por favor, selecione um arquivo de imagem válido.'); | |
| } | |
| } | |
| analyzeBtn.addEventListener('click', async () => { | |
| if (!selectedFile) return; | |
| // UI Loading State | |
| analyzeBtn.disabled = true; | |
| btnText.innerHTML = '<i class="ph-bold ph-scan"></i> Analisando...'; | |
| loader.classList.remove('hidden'); | |
| resultsPlaceholder.querySelector('p').textContent = 'Processando IA (pode levar alguns segundos)...'; | |
| resultsContent.classList.add('hidden'); | |
| resultsPlaceholder.classList.remove('hidden'); | |
| const formData = new FormData(); | |
| formData.append('file', selectedFile); | |
| try { | |
| const response = await fetch('/predict', { | |
| method: 'POST', | |
| body: formData | |
| }); | |
| if (!response.ok) { | |
| const errData = await response.json(); | |
| throw new Error(errData.error || 'Erro na análise da imagem.'); | |
| } | |
| const data = await response.json(); | |
| updateResults(data); | |
| } catch (error) { | |
| alert(error.message); | |
| resultsPlaceholder.querySelector('p').textContent = 'Falha na análise. Tente novamente.'; | |
| } finally { | |
| analyzeBtn.disabled = false; | |
| btnText.innerHTML = '<i class="ph-bold ph-scan"></i> Analisar Folha'; | |
| loader.classList.add('hidden'); | |
| } | |
| }); | |
| function updateResults(data) { | |
| // Hide placeholder, show results | |
| resultsPlaceholder.classList.add('hidden'); | |
| resultsContent.classList.remove('hidden'); | |
| // Update Text | |
| mainDisease.textContent = data.mais_frequente; | |
| // Render Chart | |
| renderChart(data.contagem); | |
| // Render Crops | |
| renderCrops(data.imagens || []); | |
| } | |
| function renderCrops(imagens) { | |
| cropsGallery.innerHTML = ''; | |
| if (imagens.length === 0) { | |
| cropsGallery.innerHTML = '<p style="color:var(--text-secondary); width: 100%;">Nenhum recorte gerado.</p>'; | |
| return; | |
| } | |
| const timestamp = Date.now(); | |
| imagens.forEach(imgData => { | |
| const card = document.createElement('div'); | |
| card.className = 'crop-card'; | |
| const img = document.createElement('img'); | |
| // Cache buster for new analyses | |
| img.src = `${imgData.url}?t=${timestamp}`; | |
| img.alt = 'Recorte detectado'; | |
| const label = document.createElement('div'); | |
| label.className = 'crop-card-label'; | |
| label.textContent = imgData.classe; | |
| card.appendChild(img); | |
| card.appendChild(label); | |
| cropsGallery.appendChild(card); | |
| }); | |
| } | |
| function renderChart(counts) { | |
| const labels = Object.keys(counts); | |
| const data = Object.values(counts); | |
| if (chartInstance) { | |
| chartInstance.destroy(); | |
| } | |
| chartInstance = new Chart(chartCanvas, { | |
| type: 'doughnut', | |
| data: { | |
| labels: labels, | |
| datasets: [{ | |
| data: data, | |
| backgroundColor: chartColors, | |
| borderWidth: 0, | |
| hoverOffset: 4 | |
| }] | |
| }, | |
| options: { | |
| responsive: true, | |
| maintainAspectRatio: false, | |
| plugins: { | |
| legend: { | |
| position: 'right', | |
| labels: { | |
| color: '#4a5568', | |
| font: { family: "'Outfit', sans-serif", size: 10 }, | |
| padding: 10, | |
| boxWidth: 12 | |
| } | |
| }, | |
| tooltip: { | |
| backgroundColor: 'rgba(255, 255, 255, 0.95)', | |
| titleColor: '#2d3748', | |
| bodyColor: '#2d3748', | |
| borderColor: 'rgba(47, 133, 90, 0.2)', | |
| borderWidth: 1, | |
| padding: 10, | |
| titleFont: { family: "'Outfit', sans-serif", size: 12, weight: 'bold' }, | |
| bodyFont: { family: "'Outfit', sans-serif", size: 12 } | |
| } | |
| }, | |
| cutout: '70%' | |
| } | |
| }); | |
| } | |
| }); | |