// UI management functions function showStatus(message, type = 'loading') { const statusBar = document.getElementById('statusBar'); statusBar.className = `status-bar visible ${type}`; if (type === 'loading') { statusBar.innerHTML = `${message}`; } else { statusBar.textContent = message; } if (type !== 'loading') { setTimeout(() => { statusBar.classList.remove('visible'); }, 3000); } } function updateProgress(current, total) { const isMobile = window.innerWidth < 768; if (isMobile) { // Mobile progress bar const progressInfo = document.getElementById('progressInfo'); const progressFill = progressInfo.querySelector('.progress-fill-mobile'); const progressText = progressInfo.querySelector('.progress-text-mobile'); if (total === 0) { progressInfo.classList.remove('active'); return; } progressInfo.classList.add('active'); const percentage = (current / total) * 100; progressFill.style.width = percentage + '%'; progressText.textContent = `${current}/${total} Inserate`; } else { // Desktop progress bar const progressInfo = document.getElementById('progressInfoDesktop'); const progressFill = progressInfo.querySelector('.progress-fill'); const progressText = progressInfo.querySelector('.progress-text'); const etaText = progressInfo.querySelector('.eta-text'); if (total === 0) { progressInfo.classList.remove('active'); return; } progressInfo.classList.add('active'); const percentage = (current / total) * 100; progressFill.style.width = percentage + '%'; progressText.textContent = `Inserate werden geladen: ${current}/${total}`; // Calculate ETA if (AppState.scrapeStartTime && current > 0) { const elapsed = (Date.now() - AppState.scrapeStartTime) / 1000; const avgTimePerListing = elapsed / current; const remaining = total - current; const etaSeconds = Math.round(avgTimePerListing * remaining); const minutes = Math.floor(etaSeconds / 60); const seconds = etaSeconds % 60; if (minutes > 0) { etaText.textContent = `Noch ca. ${minutes}m ${seconds}s`; } else { etaText.textContent = `Noch ca. ${seconds}s`; } } } } function highlightSelectedListing() { document.querySelectorAll('.result-item').forEach(item => { const itemId = parseInt(item.dataset.id); if (itemId === AppState.selectedListingId) { item.classList.add('selected'); item.scrollIntoView({ behavior: 'smooth', block: 'nearest' }); } else { item.classList.remove('selected'); } }); } function formatDate(dateString) { if (!dateString) return 'Unbekanntes Datum'; const date = new Date(dateString); return date.toLocaleDateString('de-DE'); } function renderResults(listings) { // Only render on desktop if (window.innerWidth < 768) return; const resultsList = document.getElementById('resultsList'); const resultsCount = document.querySelector('.results-count'); if (listings.length === 0) { resultsList.innerHTML = '
Keine Inserate gefunden
'; resultsCount.textContent = 'Keine Ergebnisse'; return; } resultsCount.textContent = `${listings.length} Inserat${listings.length !== 1 ? 'e' : ''}`; resultsList.innerHTML = listings.map(listing => `
${listing.image ? `${listing.title}` : '
'}
${listing.title}
${listing.price} €
📍 ${listing.address || listing.zip_code}
${formatDate(listing.date_added)}
`).join(''); // Add click handlers document.querySelectorAll('.result-item').forEach(item => { item.addEventListener('click', () => { const id = parseInt(item.dataset.id); const listing = listings.find(l => l.id === id); if (listing) { AppState.selectedListingId = id; highlightSelectedListing(); if (listing.lat && listing.lon) { AppState.map.setView([listing.lat, listing.lon], 13); const marker = AppState.markers.find(m => m.getLatLng().lat === listing.lat && m.getLatLng().lng === listing.lon ); if (marker) { marker.openPopup(); } } window.open(listing.url, '_blank'); } }); }); } function sortListings(listings, sortBy) { const sorted = [...listings]; switch (sortBy) { case 'price-asc': sorted.sort((a, b) => a.price - b.price); break; case 'price-desc': sorted.sort((a, b) => b.price - a.price); break; case 'date-asc': sorted.sort((a, b) => new Date(a.date_added || 0) - new Date(b.date_added || 0)); break; case 'date-desc': sorted.sort((a, b) => new Date(b.date_added || 0) - new Date(a.date_added || 0)); break; } return sorted; } // Privacy modal function initPrivacyModal() { const modal = document.getElementById('privacyModal'); const link = document.getElementById('privacyLink'); const close = document.querySelector('.modal-close'); link.addEventListener('click', (e) => { e.preventDefault(); modal.classList.add('show'); }); close.addEventListener('click', () => { modal.classList.remove('show'); }); window.addEventListener('click', (e) => { if (e.target === modal) { modal.classList.remove('show'); } }); } // Mobile popup function initMobilePopup() { const closeBtn = document.getElementById('mobilePopupClose'); const popup = document.getElementById('mobilePopup'); closeBtn.addEventListener('click', () => { popup.classList.remove('show'); }); } // Format price inputs with € suffix function initPriceInputs() { const minPrice = document.getElementById('minPrice'); const maxPrice = document.getElementById('maxPrice'); function formatPriceInput(input) { input.addEventListener('blur', (e) => { if (e.target.value) { // Store the numeric value const value = e.target.value.replace(/[^\d]/g, ''); if (value) { e.target.dataset.value = value; } } }); input.addEventListener('focus', (e) => { // Show only numeric value when focused if (e.target.dataset.value) { e.target.value = e.target.dataset.value; } }); } formatPriceInput(minPrice); formatPriceInput(maxPrice); }