refactoring
This commit is contained in:
168
web/js/ui.js
Normal file
168
web/js/ui.js
Normal file
@ -0,0 +1,168 @@
|
||||
// UI management functions
|
||||
|
||||
function showStatus(message, type = 'loading') {
|
||||
const statusBar = document.getElementById('statusBar');
|
||||
statusBar.className = `status-bar visible ${type}`;
|
||||
|
||||
if (type === 'loading') {
|
||||
statusBar.innerHTML = `<span class="loading-spinner"></span>${message}`;
|
||||
} else {
|
||||
statusBar.textContent = message;
|
||||
}
|
||||
|
||||
if (type !== 'loading') {
|
||||
setTimeout(() => {
|
||||
statusBar.classList.remove('visible');
|
||||
}, 3000);
|
||||
}
|
||||
}
|
||||
|
||||
function updateProgress(current, total) {
|
||||
const progressInfo = document.getElementById('progressInfo');
|
||||
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) {
|
||||
const resultsList = document.getElementById('resultsList');
|
||||
const resultsCount = document.querySelector('.results-count');
|
||||
|
||||
if (listings.length === 0) {
|
||||
resultsList.innerHTML = '<div class="no-results">Keine Inserate gefunden</div>';
|
||||
resultsCount.textContent = 'Keine Ergebnisse';
|
||||
return;
|
||||
}
|
||||
|
||||
resultsCount.textContent = `${listings.length} Inserat${listings.length !== 1 ? 'e' : ''}`;
|
||||
|
||||
resultsList.innerHTML = listings.map(listing => `
|
||||
<div class="result-item" data-id="${listing.id}">
|
||||
${listing.image ? `<img src="${listing.image}" class="result-image" alt="${listing.title}">` : '<div class="result-image"></div>'}
|
||||
<div class="result-content">
|
||||
<div class="result-title">${listing.title}</div>
|
||||
<div class="result-price">${listing.price} €</div>
|
||||
<div class="result-meta">
|
||||
<div class="result-location">
|
||||
<span>📍</span>
|
||||
<span>${listing.address || listing.zip_code}</span>
|
||||
</div>
|
||||
<div class="result-date">${formatDate(listing.date_added)}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`).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');
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user