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; // Tabs const tabIndividual = document.getElementById('tab-individual'); const tabBatch = document.getElementById('tab-batch'); const wrapperIndividual = document.getElementById('wrapper-individual'); const wrapperBatch = document.getElementById('wrapper-batch'); const tabAppCafe = document.getElementById('tab-appcafe'); const wrapperAppCafe = document.getElementById('wrapper-appcafe'); // Supabase Auth Elements const appcafeLoginForm = document.getElementById('appcafe-login-form'); const appcafeEmailInput = document.getElementById('appcafe-email'); const appcafePasswordInput = document.getElementById('appcafe-password'); const appcafeLoginBtn = document.getElementById('appcafe-login-btn'); const appcafeBtnText = appcafeLoginBtn?.querySelector('.btn-text'); const appcafeLoader = document.getElementById('appcafe-loader'); const appcafeError = document.getElementById('appcafe-error'); const appcafeLoginContainer = document.getElementById('appcafe-login-container'); const appcafeLoggedIn = document.getElementById('appcafe-logged-in'); const appcafeLogoutBtn = document.getElementById('appcafe-logout-btn'); const appcafePromo = document.querySelector('.appcafe-promo'); // Dashboard Buttons const btnOlharFotos = document.getElementById('btn-olhar-fotos'); const btnMapaCalor = document.getElementById('btn-mapa-calor'); const btnDashboardIndividual = document.getElementById('btn-dashboard-individual'); const btnDashboardLote = document.getElementById('btn-dashboard-lote'); // Gallery Modal const galleryModal = document.getElementById('gallery-modal'); const closeGalleryBtn = document.getElementById('close-gallery-btn'); const galleryContainer = document.getElementById('gallery-container'); const galleryLoader = document.getElementById('gallery-loader'); // Gallery Filters const filterDesc = document.getElementById('filter-desc'); const filterDateStart = document.getElementById('filter-date-start'); const filterDateEnd = document.getElementById('filter-date-end'); const clearFiltersBtn = document.getElementById('clear-filters-btn'); const galleryFilters = document.getElementById('gallery-filters'); let currentGalleryPictures = []; let selectedGalleryPictures = new Map(); // Gallery Actions const galleryActionBar = document.getElementById('gallery-action-bar'); const selectedCountText = document.getElementById('selected-count'); const btnCancelSelection = document.getElementById('btn-cancel-selection'); const btnAnalyzeSelected = document.getElementById('btn-analyze-selected'); const btnDeleteSelected = document.getElementById('btn-delete-selected'); const btnSelectAll = document.getElementById('select-all-btn'); const analyzeActionText = document.getElementById('analyze-action-text'); const analyzeActionLoader = document.getElementById('analyze-action-loader'); const analyzeActionHint = document.getElementById('analyze-action-hint'); // Heatmap let isHeatmapMode = false; let diseaseDictionary = {}; // id_disease -> name const heatmapModal = document.getElementById('heatmap-modal'); const closeHeatmapBtn = document.getElementById('close-heatmap-btn'); const heatmapDiseaseFilter = document.getElementById('heatmap-disease-filter'); const heatmapStyleFilter = document.getElementById('heatmap-style-filter'); let leafletMapInstance = null; let currentHeatLayer = null; let supabaseClient = null; if (window.supabase && window.SUPABASE_URL && window.SUPABASE_ANON_KEY) { supabaseClient = window.supabase.createClient(window.SUPABASE_URL, window.SUPABASE_ANON_KEY); } // Batch UI Elements const batchUploadArea = document.getElementById('batch-upload-area'); const batchFileInput = document.getElementById('batch-file-input'); const batchImagePreview = document.getElementById('batch-image-preview'); const batchUploadContent = document.getElementById('batch-upload-content'); const batchAnalyzeBtn = document.getElementById('batch-analyze-btn'); const batchBtnText = document.getElementById('batch-btn-text'); const batchLoader = document.getElementById('batch-loader'); const batchResultsPlaceholder = document.getElementById('batch-results-placeholder'); const batchResultsContent = document.getElementById('batch-results-content'); const batchTotalLeaves = document.getElementById('batch-total-leaves'); const batchMainDisease = document.getElementById('batch-main-disease'); const batchChartCanvas = document.getElementById('batchResultsChart'); let selectedBatchFiles = []; let batchChartInstance = 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 = ' 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 = ' 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; // --- Save Result Logic --- const saveContainer = document.getElementById('save-result-container-individual'); const saveBtn = document.getElementById('btn-save-result-individual'); const saveMsg = document.getElementById('save-result-msg-individual'); if (selectedFile && selectedFile.supabasePictureId && data.mais_frequente !== "Nenhuma detecção encontrada" && supabaseClient) { saveContainer.classList.remove('hidden'); saveBtn.style.display = 'inline-flex'; saveMsg.style.display = 'none'; const newSaveBtn = saveBtn.cloneNode(true); saveBtn.parentNode.replaceChild(newSaveBtn, saveBtn); newSaveBtn.addEventListener('click', async () => { newSaveBtn.disabled = true; newSaveBtn.innerHTML = ' Salvando...'; try { const diseaseName = data.mais_frequente; // Find disease ID const { data: diseaseData, error: dError } = await supabaseClient.from('diseases').select('id').eq('name', diseaseName).single(); if (dError || !diseaseData) throw new Error("Doença não encontrada no banco de dados."); // Update picture const { error: pError } = await supabaseClient.from('pictures').update({ id_disease: diseaseData.id }).eq('id', selectedFile.supabasePictureId); if (pError) throw new Error("Erro ao atualizar a foto."); newSaveBtn.style.display = 'none'; saveMsg.innerHTML = ' Resultado salvo com sucesso no banco de dados!'; saveMsg.style.color = "#2f855a"; saveMsg.style.display = 'block'; } catch (e) { newSaveBtn.disabled = false; newSaveBtn.innerHTML = ' Tentar Novamente'; saveMsg.textContent = e.message; saveMsg.style.color = "#e53e3e"; saveMsg.style.display = 'block'; } }); } else { if (saveContainer) saveContainer.classList.add('hidden'); } // Render Chart renderChart(data.contagem); // Render Crops renderCrops(data.imagens || []); } function renderCrops(imagens) { cropsGallery.innerHTML = ''; if (imagens.length === 0) { cropsGallery.innerHTML = '
Nenhum recorte gerado.
'; 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%' } }); } // --- BATCH LOGIC --- // Tab Switching tabIndividual.addEventListener('click', () => { tabIndividual.classList.add('active'); tabBatch.classList.remove('active'); if (tabAppCafe) tabAppCafe.classList.remove('active'); wrapperIndividual.classList.remove('hidden'); wrapperBatch.classList.add('hidden'); if (wrapperAppCafe) wrapperAppCafe.classList.add('hidden'); }); tabBatch.addEventListener('click', () => { tabBatch.classList.add('active'); tabIndividual.classList.remove('active'); if (tabAppCafe) tabAppCafe.classList.remove('active'); wrapperBatch.classList.remove('hidden'); wrapperIndividual.classList.add('hidden'); if (wrapperAppCafe) wrapperAppCafe.classList.add('hidden'); }); if (tabAppCafe) { tabAppCafe.addEventListener('click', () => { tabAppCafe.classList.add('active'); tabIndividual.classList.remove('active'); tabBatch.classList.remove('active'); wrapperAppCafe.classList.remove('hidden'); wrapperIndividual.classList.add('hidden'); wrapperBatch.classList.add('hidden'); checkSession(); }); } // Drag and Drop for Batch ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => { batchUploadArea.addEventListener(eventName, preventDefaults, false); }); ['dragenter', 'dragover'].forEach(eventName => { batchUploadArea.addEventListener(eventName, () => batchUploadArea.classList.add('dragover'), false); }); ['dragleave', 'drop'].forEach(eventName => { batchUploadArea.addEventListener(eventName, () => batchUploadArea.classList.remove('dragover'), false); }); batchUploadArea.addEventListener('drop', handleBatchDrop, false); batchUploadArea.addEventListener('click', () => batchFileInput.click()); batchFileInput.addEventListener('change', handleBatchFileSelect); function handleBatchDrop(e) { const dt = e.dataTransfer; handleBatchFiles(dt.files); } function handleBatchFileSelect(e) { handleBatchFiles(e.target.files); } function handleBatchFiles(files) { const validFiles = Array.from(files).filter(file => file.type.startsWith('image/')); if (validFiles.length > 0) { selectedBatchFiles = validFiles; batchImagePreview.innerHTML = ''; validFiles.forEach(file => { const reader = new FileReader(); reader.onload = (e) => { const img = document.createElement('img'); img.src = e.target.result; batchImagePreview.appendChild(img); }; reader.readAsDataURL(file); }); batchImagePreview.classList.remove('hidden'); batchUploadContent.classList.add('hidden'); batchAnalyzeBtn.disabled = false; } else { alert('Por favor, selecione arquivos de imagem válidos.'); } } batchAnalyzeBtn.addEventListener('click', async () => { if (selectedBatchFiles.length === 0) return; // UI Loading State batchAnalyzeBtn.disabled = true; batchBtnText.innerHTML = ' Analisando Lote...'; batchLoader.classList.remove('hidden'); batchResultsPlaceholder.querySelector('p').textContent = `Processando ${selectedBatchFiles.length} imagens (isso vai levar um tempo)...`; batchResultsContent.classList.add('hidden'); batchResultsPlaceholder.classList.remove('hidden'); const formData = new FormData(); selectedBatchFiles.forEach(file => { formData.append('files', file); }); try { const response = await fetch('/predict_batch', { method: 'POST', body: formData }); if (!response.ok) { const errData = await response.json(); throw new Error(errData.error || 'Erro na análise em lote.'); } const data = await response.json(); updateBatchResults(data); } catch (error) { alert(error.message); batchResultsPlaceholder.querySelector('p').textContent = 'Falha na análise em lote. Tente novamente.'; } finally { batchAnalyzeBtn.disabled = false; batchBtnText.innerHTML = ' Analisar Lote'; batchLoader.classList.add('hidden'); } }); function updateBatchResults(data) { batchResultsPlaceholder.classList.add('hidden'); batchResultsContent.classList.remove('hidden'); // --- Save Result Logic para Lote --- const saveContainer = document.getElementById('save-result-container-batch'); const saveBtn = document.getElementById('btn-save-result-batch'); const saveMsg = document.getElementById('save-result-msg-batch'); const hasSupabaseFiles = selectedBatchFiles && selectedBatchFiles.some(f => f.supabasePictureId); if (hasSupabaseFiles && data.detalhes && data.detalhes.length > 0 && supabaseClient) { saveContainer.classList.remove('hidden'); saveBtn.style.display = 'inline-flex'; saveMsg.style.display = 'none'; const newSaveBtn = saveBtn.cloneNode(true); saveBtn.parentNode.replaceChild(newSaveBtn, saveBtn); newSaveBtn.addEventListener('click', async () => { newSaveBtn.disabled = true; newSaveBtn.innerHTML = ' Salvando...'; try { const { data: allDiseases, error: dError } = await supabaseClient.from('diseases').select('id, name'); if (dError) throw new Error("Erro ao buscar doenças."); const diseaseMap = {}; allDiseases.forEach(d => { diseaseMap[d.name] = d.id; }); let savedCount = 0; for (const result of data.detalhes) { if (result.mais_frequente === "Nenhuma detecção encontrada") continue; const diseaseId = diseaseMap[result.mais_frequente]; if (!diseaseId) continue; const matchingFile = selectedBatchFiles.find(f => f.name === result.filename); if (matchingFile && matchingFile.supabasePictureId) { await supabaseClient.from('pictures').update({ id_disease: diseaseId }).eq('id', matchingFile.supabasePictureId); savedCount++; } } newSaveBtn.style.display = 'none'; saveMsg.innerHTML = ` ${savedCount} resultado(s) salvo(s) com sucesso!`; saveMsg.style.color = "#2f855a"; saveMsg.style.display = 'block'; } catch (e) { newSaveBtn.disabled = false; newSaveBtn.innerHTML = ' Tentar Novamente'; saveMsg.textContent = e.message; saveMsg.style.color = "#e53e3e"; saveMsg.style.display = 'block'; } }); } else { if (saveContainer) saveContainer.classList.add('hidden'); } batchTotalLeaves.textContent = data.total_folhas; if (data.folhas_com_deteccao > 0) { let mainDisease = "Nenhuma detectada"; let maxQtd = -1; let maxPct = 0; for (const [doenca, stats] of Object.entries(data.estatisticas)) { if (stats.quantidade > maxQtd) { maxQtd = stats.quantidade; maxPct = stats.porcentagem; mainDisease = doenca; } } batchMainDisease.textContent = `${mainDisease} (${maxPct}%)`; } else { batchMainDisease.textContent = "Nenhuma detectada"; } renderBatchChart(data.estatisticas); } function renderBatchChart(estatisticas) { const labels = []; const dataValues = []; // Adiciona a porcentagem direto no label (Ex: Ferrugem (60.0%)) e usa o valor para plotar for (const [doenca, stats] of Object.entries(estatisticas)) { labels.push(`${doenca} (${stats.porcentagem}%)`); dataValues.push(stats.porcentagem); } if (batchChartInstance) { batchChartInstance.destroy(); } batchChartInstance = new Chart(batchChartCanvas, { type: 'doughnut', data: { labels: labels, datasets: [{ data: dataValues, 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: { callbacks: { label: function(context) { let label = context.label || ''; return ' ' + label; } }, 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%' } }); } // --- SUPABASE AUTH LOGIC --- async function checkSession() { if (!supabaseClient) return; const { data, error } = await supabaseClient.auth.getSession(); if (data && data.session) { showLoggedInState(data.session.user); } else { showLoginState(); } } function showLoggedInState(user) { if (appcafeLoginContainer) appcafeLoginContainer.classList.add('hidden'); if (appcafePromo) appcafePromo.classList.add('hidden'); if (appcafeLoggedIn) appcafeLoggedIn.classList.remove('hidden'); const welcomeText = document.getElementById('welcome-user-text'); if (welcomeText && user) { const name = user.user_metadata?.display_name || user.email.split('@')[0]; welcomeText.textContent = `Bem-vindo(a), ${name}`; } } function showLoginState() { if (appcafeLoginContainer) appcafeLoginContainer.classList.remove('hidden'); if (appcafePromo) appcafePromo.classList.remove('hidden'); if (appcafeLoggedIn) appcafeLoggedIn.classList.add('hidden'); if (appcafeError) appcafeError.style.display = 'none'; if (appcafeEmailInput) appcafeEmailInput.value = ''; if (appcafePasswordInput) appcafePasswordInput.value = ''; } if (appcafeLoginForm) { appcafeLoginForm.addEventListener('submit', async (e) => { e.preventDefault(); if (!supabaseClient) return; const email = appcafeEmailInput.value; const password = appcafePasswordInput.value; // UI Loading State appcafeLoginBtn.disabled = true; if (appcafeBtnText) appcafeBtnText.textContent = 'Entrando...'; if (appcafeLoader) appcafeLoader.classList.remove('hidden'); if (appcafeError) appcafeError.style.display = 'none'; const { data, error } = await supabaseClient.auth.signInWithPassword({ email: email, password: password, }); appcafeLoginBtn.disabled = false; if (appcafeBtnText) appcafeBtnText.textContent = 'Entrar'; if (appcafeLoader) appcafeLoader.classList.add('hidden'); if (error) { if (appcafeError) { appcafeError.textContent = error.message === 'Invalid login credentials' ? 'E-mail ou senha incorretos.' : error.message; appcafeError.style.display = 'block'; } } else { showLoggedInState(); } }); } const appcafeForgotPassword = document.getElementById('appcafe-forgot-password'); if (appcafeForgotPassword) { appcafeForgotPassword.addEventListener('click', async (e) => { e.preventDefault(); if (!supabaseClient) return; const email = appcafeEmailInput.value; if (!email) { alert('Por favor, preencha o campo de e-mail para recuperar a senha.'); return; } try { appcafeLoginBtn.disabled = true; if (appcafeBtnText) appcafeBtnText.textContent = 'Enviando...'; const { error } = await supabaseClient.auth.resetPasswordForEmail(email, { redirectTo: window.location.origin + '/redefinir-senha' }); if (error) throw error; alert('E-mail de recuperação enviado! Verifique sua caixa de entrada.'); } catch (err) { alert('Erro ao enviar e-mail: ' + err.message); } finally { appcafeLoginBtn.disabled = false; if (appcafeBtnText) appcafeBtnText.textContent = 'Entrar'; } }); } if (appcafeLogoutBtn) { appcafeLogoutBtn.addEventListener('click', async () => { if (!supabaseClient) return; await supabaseClient.auth.signOut(); showLoginState(); }); } // Inicializa verificando a sessão caso inicie na tab (embora comece escondida) checkSession(); // --- FILTER LOGIC --- function renderGalleryCards(picturesList) { galleryContainer.innerHTML = ''; if (picturesList && picturesList.length > 0) { picturesList.forEach(pic => { const isSelected = selectedGalleryPictures.has(pic.picture); const card = document.createElement('div'); card.style = `background: #1e293b; border-radius: 12px; overflow: hidden; box-shadow: 0 10px 15px -3px rgba(0,0,0,0.5); display: flex; flex-direction: column; border: 2px solid ${isSelected ? 'var(--accent-primary)' : 'rgba(255,255,255,0.1)'}; transition: all 0.2s; cursor: pointer; position: relative; transform: ${isSelected ? 'scale(0.98)' : 'scale(1)'};`; // Overlay for selection checkmark const checkOverlay = document.createElement('div'); checkOverlay.style = `position: absolute; top: 10px; right: 10px; width: 30px; height: 30px; border-radius: 50%; background: ${isSelected ? 'var(--accent-primary)' : 'rgba(0,0,0,0.5)'}; border: 2px solid white; display: flex; align-items: center; justify-content: center; z-index: 5; transition: background 0.2s;`; checkOverlay.innerHTML = ``; card.onmouseover = () => { if (!selectedGalleryPictures.has(pic.picture)) card.style.transform = "translateY(-5px)"; }; card.onmouseout = () => { if (!selectedGalleryPictures.has(pic.picture)) card.style.transform = "translateY(0)"; }; card.addEventListener('click', () => { if (selectedGalleryPictures.has(pic.picture)) { selectedGalleryPictures.delete(pic.picture); card.style.border = "2px solid rgba(255,255,255,0.1)"; card.style.transform = "translateY(0)"; checkOverlay.style.background = "rgba(0,0,0,0.5)"; checkOverlay.querySelector('i').style.display = "none"; } else { selectedGalleryPictures.set(pic.picture, pic.id); card.style.border = "2px solid var(--accent-primary)"; card.style.transform = "scale(0.98)"; checkOverlay.style.background = "var(--accent-primary)"; checkOverlay.querySelector('i').style.display = "block"; } updateGalleryActionBar(); }); const imgContainer = document.createElement('div'); imgContainer.style = "width: 100%; height: 220px; background: #0f172a; overflow: hidden; position: relative;"; const img = document.createElement('img'); img.src = pic.picture; img.alt = "Foto do Café"; img.style = "width: 100%; height: 100%; object-fit: cover;"; const info = document.createElement('div'); info.style = "padding: 20px; display: flex; flex-direction: column; flex-grow: 1; justify-content: space-between;"; const dateText = document.createElement('p'); const dataFormatada = new Date(pic.data).toLocaleDateString('pt-BR', { day: '2-digit', month: 'short', year: 'numeric', hour: '2-digit', minute: '2-digit' }); dateText.innerHTML = ` ${dataFormatada}`; dateText.style = "margin: 0 0 8px 0; font-size: 0.85rem; color: #94a3b8; display: flex; align-items: center; gap: 6px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px;"; const descText = document.createElement('p'); descText.innerHTML = pic.description ? `"${pic.description}"` : 'Sem descrição fornecida.'; descText.style = "margin: 0 0 10px 0; font-size: 1rem; color: #f8fafc; font-style: italic; line-height: 1.5;"; let diseaseHtml = ''; if (pic.id_disease && diseaseDictionary[pic.id_disease]) { diseaseHtml = ` ${diseaseDictionary[pic.id_disease]}`; } imgContainer.appendChild(img); card.appendChild(checkOverlay); info.appendChild(dateText); info.appendChild(descText); if (diseaseHtml) { const dWrap = document.createElement('div'); dWrap.innerHTML = diseaseHtml; info.appendChild(dWrap); } card.appendChild(imgContainer); card.appendChild(info); galleryContainer.appendChild(card); }); } else { galleryContainer.innerHTML = 'Nenhuma foto encontrada.
Erro ao carregar fotos: ${error.message}
`; } }); } if (closeGalleryBtn) { closeGalleryBtn.addEventListener('click', () => { galleryModal.classList.add('hidden'); galleryModal.style.display = 'none'; }); } });