improve page create and page listing

This commit is contained in:
2026-01-31 17:11:05 +01:00
parent b9462b040d
commit c1144139b5
6 changed files with 130 additions and 40 deletions

View File

@@ -1,5 +1,5 @@
import { t, i18n } from '../../i18n.js'
import { getListingById } from '../../data/mock-listings.js'
import { directus } from '../../services/directus.js'
import '../chat-widget.js'
class PageListing extends HTMLElement {
@@ -21,9 +21,12 @@ class PageListing extends HTMLElement {
}
async loadListing() {
await new Promise(resolve => setTimeout(resolve, 300))
this.listing = getListingById(this.listingId)
try {
this.listing = await directus.getListing(this.listingId)
} catch (e) {
console.error('Failed to load listing:', e)
this.listing = null
}
this.loading = false
this.render()
@@ -50,7 +53,9 @@ class PageListing extends HTMLElement {
return
}
const hasImages = this.listing.images && this.listing.images.length > 0
const images = this.listing.images || []
const hasImages = images.length > 0
const firstImage = hasImages ? this.getImageUrl(images[0]) : null
const placeholderSvg = /* html */`
<svg class="placeholder-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
@@ -59,38 +64,63 @@ class PageListing extends HTMLElement {
</svg>
`
const categoryName = this.listing.category?.name || this.listing.category?.slug || ''
const locationName = this.listing.location?.name || ''
const price = this.formatPrice(this.listing.price, this.listing.currency)
const createdDate = this.listing.date_created
? new Date(this.listing.date_created).toLocaleDateString()
: ''
this.innerHTML = /* html */`
<article class="listing-detail">
<div class="listing-gallery">
<div class="listing-image-main">
${!hasImages ? placeholderSvg : ''}
${firstImage
? `<img src="${firstImage}" alt="${this.escapeHtml(this.listing.title)}">`
: placeholderSvg}
</div>
${images.length > 1 ? `
<div class="listing-thumbnails">
${images.map((img, i) => `
<button class="thumbnail ${i === 0 ? 'active' : ''}" data-index="${i}">
<img src="${this.getImageUrl(img, 100)}" alt="">
</button>
`).join('')}
</div>
` : ''}
</div>
<div class="listing-info">
<header>
<span class="badge badge-primary">${t(`categories.${this.listing.category}`)}</span>
${categoryName ? `<span class="badge badge-primary">${this.escapeHtml(categoryName)}</span>` : ''}
<h1>${this.escapeHtml(this.listing.title)}</h1>
<p class="listing-price">${this.listing.price}</p>
<p class="listing-location">📍 ${this.escapeHtml(this.listing.location)}</p>
<p class="listing-price">${price}</p>
${locationName ? `<p class="listing-location">📍 ${this.escapeHtml(locationName)}</p>` : ''}
${this.listing.condition ? `<p class="listing-condition">${this.getConditionLabel(this.listing.condition)}</p>` : ''}
</header>
<section class="listing-description">
<h2 data-i18n="listing.description">${t('listing.description')}</h2>
<p>${this.escapeHtml(this.listing.description)}</p>
<div class="description-text">${this.formatDescription(this.listing.description)}</div>
</section>
<section class="listing-seller">
<h2 data-i18n="listing.seller">${t('listing.seller')}</h2>
<div class="seller-card">
<div class="seller-avatar">${this.listing.seller.name.charAt(0)}</div>
<div class="seller-avatar">?</div>
<div class="seller-info">
<strong>${this.escapeHtml(this.listing.seller.name)}</strong>
<span>${t('listing.memberSince')} ${this.listing.seller.memberSince}</span>
<strong>${t('listing.anonymousSeller')}</strong>
${createdDate ? `<span>${t('listing.postedOn')} ${createdDate}</span>` : ''}
</div>
</div>
</section>
${this.listing.shipping ? `
<section class="listing-shipping">
<span class="shipping-badge">📦 ${t('listing.shippingAvailable')}</span>
</section>
` : ''}
<div class="listing-actions">
<button class="btn btn-primary btn-lg" id="contact-btn">
${t('listing.contactSeller')}
@@ -116,9 +146,9 @@ class PageListing extends HTMLElement {
<div class="tab-content" id="tab-chat">
<chat-widget
listing-id="${this.listing.id}"
recipient-id="${this.listing.seller.name}"
recipient-key="${this.listing.seller.publicKey || 'demo-key-' + this.listing.id}"
recipient-name="${this.escapeHtml(this.listing.seller.name)}"
recipient-id="${this.listing.user_created?.id || ''}"
recipient-key=""
recipient-name="${t('listing.anonymousSeller')}"
></chat-widget>
</div>
@@ -128,13 +158,15 @@ class PageListing extends HTMLElement {
<div class="monero-section">
<label>${t('listing.moneroAddress')}</label>
<div class="monero-address">
<code id="monero-addr">${this.listing.seller.moneroAddress || '888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H'}</code>
<button class="btn btn-outline btn-copy" id="copy-btn" title="${t('listing.copyAddress')}">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
</svg>
</button>
<code id="monero-addr">${this.listing.monero_address || t('listing.noMoneroAddress')}</code>
${this.listing.monero_address ? `
<button class="btn btn-outline btn-copy" id="copy-btn" title="${t('listing.copyAddress')}">
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
</svg>
</button>
` : ''}
</div>
</div>
@@ -190,7 +222,45 @@ class PageListing extends HTMLElement {
})
}
getImageUrl(image, size = null) {
const fileId = image?.directus_files_id?.id || image?.directus_files_id || image
if (!fileId) return null
return size
? directus.getThumbnailUrl(fileId, size)
: directus.getFileUrl(fileId)
}
formatPrice(price, currency = 'EUR') {
if (price === null || price === undefined) return t('listing.priceOnRequest')
if (currency === 'XMR') {
return `${parseFloat(price).toFixed(4)} XMR`
}
return new Intl.NumberFormat('de-DE', {
style: 'currency',
currency: currency
}).format(price)
}
formatDescription(desc) {
if (!desc) return ''
return this.escapeHtml(desc).replace(/\n/g, '<br>')
}
getConditionLabel(condition) {
const labels = {
new: t('create.conditionNew'),
like_new: t('create.conditionLikeNew'),
good: t('create.conditionGood'),
fair: t('create.conditionFair'),
poor: t('create.conditionPoor')
}
return labels[condition] || condition
}
escapeHtml(text) {
if (!text) return ''
const div = document.createElement('div')
div.textContent = text
return div.innerHTML