improve listing and search on home

This commit is contained in:
2026-02-01 14:01:08 +01:00
parent 05c2a46796
commit c6f2839fc1
8 changed files with 162 additions and 25 deletions

View File

@@ -10,44 +10,95 @@ class PageHome extends HTMLElement {
this.listings = []
this.loading = true
this.error = null
this.currentFilters = {}
}
connectedCallback() {
this.render()
this.setupEventListeners()
this.loadListings()
this.unsubscribe = i18n.subscribe(() => this.render())
this.unsubscribe = i18n.subscribe(() => {
this.render()
this.setupEventListeners()
})
}
disconnectedCallback() {
if (this.unsubscribe) this.unsubscribe()
}
setupEventListeners() {
const searchBox = this.querySelector('search-box')
// Listen for filter changes (live update)
searchBox?.addEventListener('filter-change', (e) => {
this.currentFilters = e.detail
this.loadListings()
})
// Listen for search submit (with query)
searchBox?.addEventListener('search', (e) => {
e.preventDefault()
e.stopPropagation()
this.currentFilters = e.detail
this.loadListings()
return false
})
}
async loadListings() {
try {
this.loading = true
this.error = null
this.render()
this.updateListingsSection()
const response = await listingsService.getRecentListings(12)
const filters = {
search: this.currentFilters.query || undefined,
category: this.currentFilters.category || undefined,
limit: 20
}
const response = await listingsService.getListingsWithFilters(filters)
this.listings = response.items || []
this.loading = false
this.render()
this.updateListingsSection()
} catch (err) {
console.error('Failed to load listings:', err)
this.error = err.message
this.loading = false
this.render()
this.updateListingsSection()
}
}
updateListingsSection() {
const listingsGrid = this.querySelector('.listings-grid')
const sectionTitle = this.querySelector('.listings-title')
if (listingsGrid) {
listingsGrid.innerHTML = this.renderListings()
}
if (sectionTitle) {
sectionTitle.textContent = this.getListingsTitle()
}
}
getListingsTitle() {
if (this.currentFilters.query || this.currentFilters.category) {
const count = this.listings.length
return t('search.resultsCount', { count }) || `${count} Ergebnisse`
}
return t('home.recentListings')
}
render() {
this.innerHTML = /* html */`
<section class="search-section">
<search-box></search-box>
<search-box no-navigate></search-box>
</section>
<section class="recent-listings">
<h2>${t('home.recentListings')}</h2>
<h2 class="listings-title">${this.getListingsTitle()}</h2>
<div class="listings-grid">
${this.renderListings()}
</div>
@@ -57,21 +108,21 @@ class PageHome extends HTMLElement {
renderListings() {
if (this.loading) {
return /* html */`<p class="loading-text">${t('common.loading') || 'Laden...'}</p>`
return /* html */`<p class="loading-text">${t('common.loading')}</p>`
}
if (this.error) {
return /* html */`<p class="error-text">${t('common.error') || 'Fehler beim Laden'}</p>`
return /* html */`<p class="error-text">${t('common.error')}</p>`
}
if (this.listings.length === 0) {
return /* html */`<p class="empty-text">${t('home.noListings') || 'Keine Anzeigen gefunden'}</p>`
return /* html */`<p class="empty-text">${t('home.noListings')}</p>`
}
return this.listings.map(listing => {
const imageId = listing.images?.[0]?.directus_files_id?.id || listing.images?.[0]?.directus_files_id
const imageUrl = imageId ? directus.getThumbnailUrl(imageId, 300) : ''
const locationName = listing.location?.name || listing.location || ''
const locationName = listing.location?.name || ''
return /* html */`
<listing-card
@@ -97,12 +148,21 @@ style.textContent = /* css */`
}
/* Listings */
page-home section {
page-home .recent-listings {
margin-bottom: var(--space-xl);
}
page-home section h2 {
page-home .listings-title {
margin-bottom: var(--space-lg);
}
page-home .loading-text,
page-home .error-text,
page-home .empty-text {
text-align: center;
padding: var(--space-xl);
color: var(--color-text-muted);
grid-column: 1 / -1;
}
`
document.head.appendChild(style)

View File

@@ -30,6 +30,9 @@ class PageListing extends HTMLElement {
this.listing = await directus.getListing(this.listingId)
this.loadFavoriteState()
// Increment view counter (async, don't wait)
directus.incrementListingViews(this.listingId)
// Load other listings from same seller
if (this.listing?.user_created) {
await this.loadSellerListings()
@@ -159,6 +162,7 @@ class PageListing extends HTMLElement {
<div class="listing-meta">
${this.listing.condition ? `<span class="meta-item">${this.getConditionLabel(this.listing.condition)}</span>` : ''}
${this.listing.shipping ? `<span class="meta-item">📦 ${t('listing.shippingAvailable')}</span>` : ''}
<span class="meta-item views-item"><span class="views-icon">👁</span> ${this.formatViews(this.listing.views || 0)}</span>
<span class="meta-item meta-date">${t('listing.postedOn')} ${createdDate}</span>
</div>
</header>
@@ -442,6 +446,13 @@ class PageListing extends HTMLElement {
return labels[condition] || condition
}
formatViews(count) {
if (count === 1) {
return `1 ${t('listing.viewSingular')}`
}
return `${count} ${t('listing.viewPlural')}`
}
formatDescription(text) {
if (!text) return ''
return text
@@ -604,6 +615,10 @@ style.textContent = /* css */`
page-listing .meta-date {
margin-left: auto;
}
page-listing .views-icon {
filter: grayscale(1);
}
page-listing .listing-description,
page-listing .listing-location-section {

View File

@@ -337,8 +337,8 @@ class SearchBox extends HTMLElement {
this.selectedCategory = ''
this.selectedSubcategory = ''
this._expandedCategory = ''
this.saveFiltersToStorage()
categoryMenu?.classList.remove('open')
this.emitFilterChange()
this.render()
this.setupEventListeners()
})
@@ -350,8 +350,8 @@ class SearchBox extends HTMLElement {
this.selectedCategory = item.dataset.category
this.selectedSubcategory = item.dataset.subcategory
this._expandedCategory = ''
this.saveFiltersToStorage()
categoryMenu?.classList.remove('open')
this.emitFilterChange()
this.render()
this.setupEventListeners()
})
@@ -366,7 +366,7 @@ class SearchBox extends HTMLElement {
this.useCurrentLocation = false
this.selectedCountry = e.target.value
}
this.saveFiltersToStorage()
this.emitFilterChange()
this.render()
this.setupEventListeners()
}
@@ -377,7 +377,7 @@ class SearchBox extends HTMLElement {
// Radius select handler (both desktop and mobile)
const handleRadiusChange = (e) => {
this.selectedRadius = parseInt(e.target.value)
this.saveFiltersToStorage()
this.emitFilterChange()
}
radiusSelect?.addEventListener('change', handleRadiusChange)
@@ -410,6 +410,28 @@ class SearchBox extends HTMLElement {
measurer.remove()
}
getFilterDetails() {
return {
query: this.searchQuery,
category: this.selectedCategory,
subcategory: this.selectedSubcategory,
country: this.selectedCountry,
useCurrentLocation: this.useCurrentLocation,
lat: this.currentLat,
lng: this.currentLng,
radius: this.selectedRadius
}
}
emitFilterChange() {
this.saveFiltersToStorage()
this.dispatchEvent(new CustomEvent('filter-change', {
bubbles: true,
detail: this.getFilterDetails()
}))
}
handleSearch() {
const params = new URLSearchParams()
@@ -432,14 +454,7 @@ class SearchBox extends HTMLElement {
bubbles: true,
cancelable: true,
detail: {
query: this.searchQuery,
category: this.selectedCategory,
subcategory: this.selectedSubcategory,
country: this.selectedCountry,
useCurrentLocation: this.useCurrentLocation,
lat: this.currentLat,
lng: this.currentLng,
radius: this.selectedRadius,
...this.getFilterDetails(),
params: params.toString()
}
})

View File

@@ -331,6 +331,34 @@ class DirectusService {
return response.data
}
async incrementListingViews(id) {
// Check if user already viewed this listing (session-based)
const viewedKey = `dgray_viewed_${id}`
if (sessionStorage.getItem(viewedKey)) {
return false
}
try {
// Get current views
const listing = await this.get(`/items/listings/${id}`, {
fields: ['views']
})
const currentViews = listing.data?.views || 0
// Increment views
await this.patch(`/items/listings/${id}`, {
views: currentViews + 1
})
// Mark as viewed for this session
sessionStorage.setItem(viewedKey, 'true')
return true
} catch (error) {
console.error('Failed to increment views:', error)
return false
}
}
async deleteListing(id) {
return this.delete(`/items/listings/${id}`)
}