// Version: 01.10.2025 15:30 (Fix DB Autofill Logic) import * as api from './api.js'; import * as ui from './ui.js'; import * as lexicon from './lexikon.js'; // === Globaler Zustand === const AppState = { plants: [], settings: {}, incorrectGuesses: [], currentView: 'grid', currentSort: 'date_desc', fullAiAnalysisResult: null, pendingEnrichmentPromise: null }; // === Haupt-Initialisierung === async function loadInitialData() { try { const [plants, settings] = await Promise.all([ api.getPlants(), api.getUserSettings() ]); AppState.plants = plants; AppState.settings = settings; renderDashboard(); } catch (error) { console.error('Fehler beim Laden der Anfangsdaten:', error); ui.showToast('App-Daten konnten nicht geladen werden.', true); } } function renderDashboard() { const sortedPlants = sortPlants(AppState.plants, AppState.currentSort); const dashboard = document.getElementById('plant-dashboard'); if (dashboard) { const useCompactView = window.matchMedia("(max-width: 768px)").matches && AppState.plants.length >= 3; dashboard.classList.toggle('compact-view-enabled', useCompactView); ui.renderPlantDashboard(sortedPlants, dashboard); } } function sortPlants(plants, sortValue) { const sorted = [...plants]; switch (sortValue) { case 'name_asc': sorted.sort((a, b) => a.name.localeCompare(b.name)); break; case 'watering_due': sorted.sort((a, b) => { const dateA = a.next_watering_due ? new Date(a.next_watering_due) : new Date('9999-12-31'); const dateB = b.next_watering_due ? new Date(b.next_watering_due) : new Date('9999-12-31'); return dateA - dateB; }); break; default: // 'date_desc' sorted.sort((a, b) => { if (String(a.id).startsWith('temp-')) return -1; if (String(b.id).startsWith('temp-')) return 1; return b.id - a.id; }); break; } return sorted; } // === Event Handler === function fileToBase64(file) { return new Promise((resolve, reject) => { const reader = new FileReader(); reader.readAsDataURL(file); reader.onload = () => resolve(reader.result.split(',')[1]); reader.onerror = error => reject(error); }); } // --- Dashboard & Globale Aktionen --- async function handleDashboardClick(e) { const actionElement = e.target.closest('[data-action]'); if (!actionElement) return; if (actionElement.dataset.action === 'show-lexicon') { e.preventDefault(); e.stopPropagation(); const latinName = actionElement.dataset.latinName; if (latinName) lexicon.openLexiconDetailModal(latinName); return; } const card = e.target.closest('[data-id]'); if (!card) return; const plantId = parseInt(card.dataset.id, 10); const plant = AppState.plants.find(p => p.id === plantId); if (!plant) return; switch (actionElement.dataset.action) { case 'show-detail': ui.openPlantDetailOverlay(plant); break; case 'flip': case 'flip-back': e.target.closest('.plant-card-inner')?.classList.toggle('is-flipped'); break; case 'show-care-tips': e.target.closest('.plant-list-item')?.classList.toggle('tips-visible'); break; case 'toggle-menu': e.stopPropagation(); const menuContainer = actionElement.closest('.menu-container'); menuContainer.parentElement.closest('[data-id]')?.classList.add('menu-open'); menuContainer.querySelector('.menu-dropdown')?.classList.toggle('show'); break; case 'edit': ui.closePlantDetailOverlay(); setTimeout(() => ui.openPlantModal(plant, getModalHandlers()), 50); break; case 'delete': if (confirm(`Möchtest du "${plant.name}" wirklich löschen?`)) { try { await api.deletePlant(plantId); ui.closePlantDetailOverlay(); ui.showToast(`"${plant.name}" wurde gelöscht.`); await loadInitialData(); } catch (error) { ui.showToast('Löschen fehlgeschlagen.', true); } } break; case 'water': case 'fertilize': e.stopPropagation(); actionElement.disabled = true; actionElement.classList.add('is-loading'); try { const { plant: updatedPlant } = await api.recordCareAction(plantId, actionElement.dataset.action); const plantIndex = AppState.plants.findIndex(p => p.id === plantId); if (plantIndex !== -1) AppState.plants[plantIndex] = updatedPlant; renderDashboard(); if (document.getElementById('plantDetailOverlay').classList.contains('show')) ui.openPlantDetailOverlay(updatedPlant); ui.showToast(actionElement.dataset.action === 'water' ? 'Gegossen!' : 'Gedüngt!'); } catch (error) { ui.showToast(`Aktion fehlgeschlagen.`, true); } finally { actionElement.disabled = false; actionElement.classList.remove('is-loading'); } break; case 'open-diary': ui.closePlantDetailOverlay(); setTimeout(() => ui.openDiaryModal(plant), e.target.closest('.plant-detail-overlay') ? 300 : 0); break; } } async function handleSaveSettings(settingsToUpdate) { try { await api.updateUserSettings(settingsToUpdate); AppState.settings = {...AppState.settings, ...settingsToUpdate}; ui.showToast('Einstellungen gespeichert!'); } catch (error) { ui.showToast('Einstellungen konnten nicht gespeichert werden.', true); } } // --- Modal-spezifische Handler --- function getModalHandlers() { return { onPhotoSelect: handlePhotoSelection, onAnalyze: handleAiAnalysis, onLatinNameBlur: handleLatinNameBlur, onSubmit: handlePlantFormSubmit }; } async function handlePhotoSelection(e) { const plantPhotoInput = e.target; if (plantPhotoInput.files && plantPhotoInput.files[0]) { const reader = new FileReader(); reader.onload = function() { ui.showImagePreview(reader.result); ui.showAiSection(); ui.clearAiResults(document.getElementById('plantForm')); AppState.incorrectGuesses = []; } reader.readAsDataURL(plantPhotoInput.files[0]); } } async function handleAiAnalysis() { const plantPhotoInput = document.getElementById('plantPhoto'); const analyzeBtn = document.getElementById('analyzeBtn'); const plantForm = document.getElementById('plantForm'); const file = plantPhotoInput.files[0]; if (!file) return; analyzeBtn.disabled = true; AppState.pendingEnrichmentPromise = null; let cleanupAnimation = () => {}; try { cleanupAnimation = ui.runAiAnimation(document.getElementById('imagePreviewContainer')); const base64Image = await fileToBase64(file); const fullAnalysis = await api.performInitialAnalysis(base64Image, AppState.incorrectGuesses); AppState.fullAiAnalysisResult = fullAnalysis; cleanupAnimation(); const identification = fullAnalysis.identification; if (!identification || identification.name.toLowerCase() === 'unbekannt' || identification.confidence_score < 75) { ui.showToast(identification?.identification_notes || 'Pflanze nicht sicher erkannt.', true); } else { ui.populateFormWithIdentification(plantForm, identification); plantForm.querySelector('#aiFullResponse').value = JSON.stringify(fullAnalysis); AppState.incorrectGuesses.push(identification.latin_name.trim().toLowerCase()); document.getElementById('retryAiBtn').style.display = 'block'; const settings = AppState.settings; const needsEnrichmentForUI = settings.auto_fill_watering || settings.auto_fill_fertilizing || settings.auto_fill_care_tips; // Definiere den Promise, der die Daten holt const enrichmentPromise = api.getLexiconEntryDetails(identification.latin_name) .catch(() => api.aiEnrich(identification.latin_name)); // Weise den Promise dem globalen Zustand zu, damit die Speicherfunktion darauf warten kann AppState.pendingEnrichmentPromise = enrichmentPromise; // Führe die Logik aus, sobald der Promise erfüllt ist enrichmentPromise.then(enrichmentData => { if (enrichmentData) { // Befülle das versteckte Feld IMMER plantForm.querySelector('#aiEnrichmentResponse').value = JSON.stringify(enrichmentData); // Befülle die sichtbaren Felder NUR, wenn der Nutzer es wünscht if (settings.auto_fill_watering && plantForm.querySelector('#wateringInterval').value === '') { plantForm.querySelector('#wateringInterval').value = enrichmentData.watering_interval_days || ''; } if (settings.auto_fill_fertilizing && plantForm.querySelector('#fertilizingInterval').value === '') { plantForm.querySelector('#fertilizingInterval').value = enrichmentData.fertilizing_interval_days || ''; } if (settings.auto_fill_care_tips && plantForm.querySelector('#careTips').value === '') { plantForm.querySelector('#careTips').value = enrichmentData.care_tips || ''; } } }).catch(err => console.error("Fehler bei der Datenanreicherung:", err)); // Zeige den Lade-Spinner NUR, wenn der Nutzer die Daten auch sehen will if (needsEnrichmentForUI) { ui.showEnrichmentLoader(true); enrichmentPromise.finally(() => ui.showEnrichmentLoader(false)); } } } catch (error) { ui.showToast(error.message, true); cleanupAnimation(); } finally { analyzeBtn.disabled = false; } } async function handleLatinNameBlur(e) { const latinName = e.target.value.trim(); if (latinName) { try { const lexiconEntry = await api.getLexiconEntryDetails(latinName); // Hier nutzen wir dieselbe Logik wie oben, um die Felder basierend auf den Settings zu befüllen const form = document.getElementById('plantForm'); const settings = AppState.settings; if (settings.auto_fill_watering && form.querySelector('#wateringInterval').value === '') { form.querySelector('#wateringInterval').value = lexiconEntry.watering_interval_days || ''; } if (settings.auto_fill_fertilizing && form.querySelector('#fertilizingInterval').value === '') { form.querySelector('#fertilizingInterval').value = lexiconEntry.fertilizing_interval_days || ''; } if (settings.auto_fill_care_tips && form.querySelector('#careTips').value === '') { form.querySelector('#careTips').value = lexiconEntry.care_tips || ''; } form.querySelector('#aiEnrichmentResponse').value = JSON.stringify(lexiconEntry); ui.showToast('Daten aus Lexikon geladen!', false); } catch (error) { /* Kein Eintrag gefunden, still bleiben */ } } } function handlePlantFormSubmit(e) { e.preventDefault(); const form = e.target; const formData = new FormData(form); const tempId = `temp-${Date.now()}`; const tempPlant = { id: tempId, name: formData.get('name'), photo: document.getElementById('imagePreview').src, latin_name: formData.get('latin_name'), watering_interval: null, fertilizing_interval: null, next_watering_due: null, next_fertilizing_due: null, care_tips: 'Wird geladen...' }; AppState.plants.unshift(tempPlant); renderDashboard(); ui.closePlantModal(); ui.showToast('Pflanze wird gespeichert...'); (async () => { try { if (AppState.pendingEnrichmentPromise) { await AppState.pendingEnrichmentPromise; } const { plant: finalPlant } = await api.savePlant(new FormData(form)); const plantIndex = AppState.plants.findIndex(p => p.id === tempId); if (plantIndex !== -1) { AppState.plants[plantIndex] = finalPlant; } else { AppState.plants.shift(); AppState.plants.unshift(finalPlant); } renderDashboard(); ui.showToast('Pflanze erfolgreich gespeichert!'); } catch (error) { ui.showToast(`Speichern fehlgeschlagen: ${error.message}`, true); AppState.plants = AppState.plants.filter(p => p.id !== tempId); renderDashboard(); } finally { AppState.pendingEnrichmentPromise = null; } })(); } // === Initialisierung der globalen Listener === function init() { document.getElementById('addPlantBtn')?.addEventListener('click', () => ui.openPlantModal(null, getModalHandlers())); document.getElementById('settingsBtn')?.addEventListener('click', async () => { try { const settings = await api.getUserSettings(); AppState.settings = settings; ui.openSettingsModal(settings, handleSaveSettings); } catch(e){ ui.showToast("Einstellungen konnten nicht geladen werden.", true); } }); document.getElementById('plant-dashboard')?.addEventListener('click', handleDashboardClick); document.getElementById('sortSelect')?.addEventListener('change', (e) => { AppState.currentSort = e.target.value; renderDashboard(); }); document.getElementById('viewGridBtn')?.addEventListener('click', () => { AppState.currentView = 'grid'; document.getElementById('plant-dashboard')?.classList.remove('list-view'); document.getElementById('viewGridBtn')?.classList.add('active'); document.getElementById('viewListBtn')?.classList.remove('active'); }); document.getElementById('viewListBtn')?.addEventListener('click', () => { AppState.currentView = 'list'; document.getElementById('plant-dashboard')?.classList.add('list-view'); document.getElementById('viewListBtn')?.classList.add('active'); document.getElementById('viewGridBtn')?.classList.remove('active'); }); const plantDetailOverlay = document.getElementById('plantDetailOverlay'); plantDetailOverlay?.addEventListener('click', (e) => { if (e.target === plantDetailOverlay) { ui.closePlantDetailOverlay(); } else { handleDashboardClick(e); } }); document.getElementById('plantDetailCloseBtn')?.addEventListener('click', ui.closePlantDetailOverlay); window.addEventListener('click', (e) => { if (!e.target.closest('.menu-container')) { document.querySelectorAll('.menu-dropdown.show').forEach(menu => { menu.classList.remove('show'); menu.closest('[data-id]')?.classList.remove('menu-open'); }); } }); loadInitialData(); } document.addEventListener('DOMContentLoaded', init);