implement directus listings

This commit is contained in:
2026-02-01 10:08:04 +01:00
parent 515e43541b
commit fbadcf2efc
6 changed files with 128 additions and 38 deletions

View File

@@ -1,11 +1,20 @@
import { t, i18n } from '../../i18n.js'
import { mockListings } from '../../data/mock-listings.js'
import { listingsService } from '../../services/listings.js'
import { directus } from '../../services/directus.js'
import '../listing-card.js'
import '../search-box.js'
class PageHome extends HTMLElement {
constructor() {
super()
this.listings = []
this.loading = true
this.error = null
}
connectedCallback() {
this.render()
this.loadListings()
this.unsubscribe = i18n.subscribe(() => this.render())
}
@@ -13,6 +22,24 @@ class PageHome extends HTMLElement {
if (this.unsubscribe) this.unsubscribe()
}
async loadListings() {
try {
this.loading = true
this.error = null
this.render()
const response = await listingsService.getRecentListings(12)
this.listings = response.items || []
this.loading = false
this.render()
} catch (err) {
console.error('Failed to load listings:', err)
this.error = err.message
this.loading = false
this.render()
}
}
render() {
this.innerHTML = /* html */`
<section class="search-section">
@@ -29,14 +56,34 @@ class PageHome extends HTMLElement {
}
renderListings() {
return mockListings.map(listing => /* html */`
<listing-card
listing-id="${listing.id}"
title="${listing.title}"
price="${listing.price}"
location="${listing.location}"
></listing-card>
`).join('')
if (this.loading) {
return /* html */`<p class="loading-text">${t('common.loading') || 'Laden...'}</p>`
}
if (this.error) {
return /* html */`<p class="error-text">${t('common.error') || 'Fehler beim Laden'}</p>`
}
if (this.listings.length === 0) {
return /* html */`<p class="empty-text">${t('home.noListings') || 'Keine Anzeigen gefunden'}</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 || ''
return /* html */`
<listing-card
listing-id="${listing.id}"
title="${listing.title || ''}"
price="${listing.price || ''}"
currency="${listing.currency || 'EUR'}"
location="${locationName}"
image="${imageUrl}"
></listing-card>
`
}).join('')
}
}

View File

@@ -1,6 +1,7 @@
import { t, i18n } from '../../i18n.js'
import { router } from '../../router.js'
import { searchListings } from '../../data/mock-listings.js'
import { listingsService } from '../../services/listings.js'
import { directus } from '../../services/directus.js'
import '../search-box.js'
import '../listing-card.js'
@@ -9,6 +10,7 @@ class PageSearch extends HTMLElement {
super()
this.results = []
this.loading = false
this.error = null
}
connectedCallback() {
@@ -92,17 +94,26 @@ class PageSearch extends HTMLElement {
async performSearch() {
this.loading = true
this.error = null
this.updateResults()
await new Promise(resolve => setTimeout(resolve, 500))
this.results = this.getMockResults()
this.loading = false
this.updateResults()
}
getMockResults() {
return searchListings(this.query, this.category, this.subcategory)
try {
const filters = {
search: this.query || undefined,
category: this.category || undefined,
limit: 50
}
const response = await listingsService.getListingsWithFilters(filters)
this.results = response.items || []
this.loading = false
this.updateResults()
} catch (err) {
console.error('Search failed:', err)
this.error = err.message
this.loading = false
this.updateResults()
}
}
updateResults() {
@@ -122,6 +133,15 @@ class PageSearch extends HTMLElement {
`
}
if (this.error) {
return /* html */`
<div class="empty-state">
<div class="empty-state-icon">⚠️</div>
<p>${t('common.error')}</p>
</div>
`
}
if (!this.hasFilters() && this.results.length === 0) {
return /* html */`
<div class="empty-state">
@@ -143,14 +163,22 @@ class PageSearch extends HTMLElement {
return /* html */`
<p class="results-count">${t('search.resultsCount', { count: this.results.length })}</p>
<div class="listings-grid">
${this.results.map(item => /* html */`
<listing-card
listing-id="${item.id}"
title="${this.escapeHtml(item.title)}"
price="${item.price}"
location="${this.escapeHtml(item.location)}"
></listing-card>
`).join('')}
${this.results.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 || ''
return /* html */`
<listing-card
listing-id="${listing.id}"
title="${this.escapeHtml(listing.title || '')}"
price="${listing.price || ''}"
currency="${listing.currency || 'EUR'}"
location="${this.escapeHtml(locationName)}"
image="${imageUrl}"
></listing-card>
`
}).join('')}
</div>
`
}

View File

@@ -4,7 +4,7 @@
*/
import { directus } from './directus.js'
import { currencyService } from './currency.js'
import currency from './currency.js'
class ListingsService {
constructor() {
@@ -34,12 +34,9 @@ class ListingsService {
const listing = await directus.getListing(id)
if (!listing) return null
if (listing.price && listing.price_mode === 'xmr') {
const fiatPrice = await currencyService.xmrToFiat(listing.price, targetCurrency)
listing.price_converted = fiatPrice
listing.price_converted_currency = targetCurrency
} else if (listing.price && listing.currency === 'XMR') {
const fiatPrice = await currencyService.xmrToFiat(listing.price, targetCurrency)
if (listing.price && (listing.price_mode === 'xmr' || listing.currency === 'XMR')) {
const rates = await currency.getXmrRates()
const fiatPrice = currency.convertFromXmr(listing.price, targetCurrency, rates)
listing.price_converted = fiatPrice
listing.price_converted_currency = targetCurrency
}
@@ -51,7 +48,7 @@ class ListingsService {
const directusFilter = { status: { _eq: 'published' } }
if (filters.category) {
directusFilter.category = { _eq: filters.category }
directusFilter.category = { slug: { _eq: filters.category } }
}
if (filters.location) {

View File

@@ -23,7 +23,12 @@
"placeholderTitle": "Beispielanzeige",
"placeholderLocation": "Standort",
"addFavorite": "Zu Favoriten hinzufügen",
"removeFavorite": "Aus Favoriten entfernen"
"removeFavorite": "Aus Favoriten entfernen",
"noListings": "Keine Anzeigen gefunden"
},
"common": {
"loading": "Laden...",
"error": "Fehler beim Laden"
},
"categories": {
"electronics": "Elektronik",
@@ -139,6 +144,7 @@
"shippingAvailable": "Versand möglich",
"location": "Standort",
"locationPlaceholder": "Stadt, PLZ oder Adresse",
"locationHint": "Wähle den Standort für deine Anzeige",
"description": "Beschreibung",
"descriptionPlaceholder": "Beschreibe deinen Artikel ausführlich...",
"images": "Bilder",

View File

@@ -23,7 +23,12 @@
"placeholderTitle": "Sample Listing",
"placeholderLocation": "Location",
"addFavorite": "Add to favorites",
"removeFavorite": "Remove from favorites"
"removeFavorite": "Remove from favorites",
"noListings": "No listings found"
},
"common": {
"loading": "Loading...",
"error": "Error loading"
},
"categories": {
"electronics": "Electronics",
@@ -139,6 +144,7 @@
"shippingAvailable": "Shipping available",
"location": "Location",
"locationPlaceholder": "City, ZIP or address",
"locationHint": "Choose the location for your listing",
"description": "Description",
"descriptionPlaceholder": "Describe your item in detail...",
"images": "Images",

View File

@@ -23,7 +23,12 @@
"placeholderTitle": "Exemple d'annonce",
"placeholderLocation": "Emplacement",
"addFavorite": "Ajouter aux favoris",
"removeFavorite": "Retirer des favoris"
"removeFavorite": "Retirer des favoris",
"noListings": "Aucune annonce trouvée"
},
"common": {
"loading": "Chargement...",
"error": "Erreur de chargement"
},
"categories": {
"electronics": "Électronique",
@@ -139,6 +144,7 @@
"shippingAvailable": "Livraison disponible",
"location": "Emplacement",
"locationPlaceholder": "Ville, code postal ou adresse",
"locationHint": "Choisissez l'emplacement de votre annonce",
"description": "Description",
"descriptionPlaceholder": "Décrivez votre article en détail...",
"images": "Images",