improve listing
This commit is contained in:
@@ -394,6 +394,14 @@ app-shell main {
|
|||||||
margin-bottom: var(--space-md);
|
margin-bottom: var(--space-md);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.empty-state p {
|
||||||
|
margin-bottom: var(--space-lg);
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-state .btn {
|
||||||
|
margin-top: var(--space-md);
|
||||||
|
}
|
||||||
|
|
||||||
/* Dropdown */
|
/* Dropdown */
|
||||||
.dropdown {
|
.dropdown {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|||||||
@@ -53,9 +53,12 @@ class PageListing extends HTMLElement {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const images = this.listing.images || []
|
const images = (this.listing.images || []).slice(0, 5)
|
||||||
const hasImages = images.length > 0
|
const hasImages = images.length > 0
|
||||||
const firstImage = hasImages ? this.getImageUrl(images[0]) : null
|
const firstImage = hasImages ? this.getImageUrl(images[0], 800) : null
|
||||||
|
this.currentImageIndex = 0
|
||||||
|
this.allImages = images
|
||||||
|
|
||||||
const placeholderSvg = /* html */`
|
const placeholderSvg = /* html */`
|
||||||
<svg class="placeholder-icon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
|
<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>
|
<rect x="3" y="3" width="18" height="18" rx="2" ry="2"></rect>
|
||||||
@@ -65,7 +68,6 @@ class PageListing extends HTMLElement {
|
|||||||
`
|
`
|
||||||
|
|
||||||
const categoryName = this.listing.category?.name || this.listing.category?.slug || ''
|
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 price = this.formatPrice(this.listing.price, this.listing.currency)
|
||||||
const createdDate = this.listing.date_created
|
const createdDate = this.listing.date_created
|
||||||
? new Date(this.listing.date_created).toLocaleDateString()
|
? new Date(this.listing.date_created).toLocaleDateString()
|
||||||
@@ -74,16 +76,16 @@ class PageListing extends HTMLElement {
|
|||||||
this.innerHTML = /* html */`
|
this.innerHTML = /* html */`
|
||||||
<article class="listing-detail">
|
<article class="listing-detail">
|
||||||
<div class="listing-gallery">
|
<div class="listing-gallery">
|
||||||
<div class="listing-image-main">
|
<div class="listing-image-main" id="main-image">
|
||||||
${firstImage
|
${firstImage
|
||||||
? `<img src="${firstImage}" alt="${this.escapeHtml(this.listing.title)}">`
|
? `<img src="${firstImage}" alt="${this.escapeHtml(this.listing.title)}" id="main-img">`
|
||||||
: placeholderSvg}
|
: placeholderSvg}
|
||||||
</div>
|
</div>
|
||||||
${images.length > 1 ? `
|
${images.length > 1 ? `
|
||||||
<div class="listing-thumbnails">
|
<div class="listing-thumbnails">
|
||||||
${images.map((img, i) => `
|
${images.map((img, i) => `
|
||||||
<button class="thumbnail ${i === 0 ? 'active' : ''}" data-index="${i}">
|
<button class="thumbnail ${i === 0 ? 'active' : ''}" data-index="${i}">
|
||||||
<img src="${this.getImageUrl(img, 100)}" alt="">
|
<img src="${this.getImageUrl(img, 150)}" alt="">
|
||||||
</button>
|
</button>
|
||||||
`).join('')}
|
`).join('')}
|
||||||
</div>
|
</div>
|
||||||
@@ -95,7 +97,6 @@ class PageListing extends HTMLElement {
|
|||||||
${categoryName ? `<span class="badge badge-primary">${this.escapeHtml(categoryName)}</span>` : ''}
|
${categoryName ? `<span class="badge badge-primary">${this.escapeHtml(categoryName)}</span>` : ''}
|
||||||
<h1>${this.escapeHtml(this.listing.title)}</h1>
|
<h1>${this.escapeHtml(this.listing.title)}</h1>
|
||||||
<p class="listing-price">${price}</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>` : ''}
|
${this.listing.condition ? `<p class="listing-condition">${this.getConditionLabel(this.listing.condition)}</p>` : ''}
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
@@ -220,6 +221,22 @@ class PageListing extends HTMLElement {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Thumbnail gallery
|
||||||
|
const thumbnails = this.querySelectorAll('.thumbnail')
|
||||||
|
const mainImg = this.querySelector('#main-img')
|
||||||
|
|
||||||
|
thumbnails.forEach(thumb => {
|
||||||
|
thumb.addEventListener('click', () => {
|
||||||
|
const index = parseInt(thumb.dataset.index)
|
||||||
|
if (mainImg && this.allImages[index]) {
|
||||||
|
mainImg.src = this.getImageUrl(this.allImages[index], 800)
|
||||||
|
thumbnails.forEach(t => t.classList.remove('active'))
|
||||||
|
thumb.classList.add('active')
|
||||||
|
this.currentImageIndex = index
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
getImageUrl(image, size = null) {
|
getImageUrl(image, size = null) {
|
||||||
@@ -297,6 +314,14 @@ style.textContent = /* css */`
|
|||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
page-listing .listing-image-main img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: contain;
|
||||||
|
background: var(--color-bg-tertiary);
|
||||||
}
|
}
|
||||||
|
|
||||||
page-listing .listing-image-main .placeholder-icon {
|
page-listing .listing-image-main .placeholder-icon {
|
||||||
@@ -305,6 +330,47 @@ style.textContent = /* css */`
|
|||||||
color: var(--color-border);
|
color: var(--color-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
page-listing .listing-thumbnails {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(5, 1fr);
|
||||||
|
gap: var(--space-xs);
|
||||||
|
padding: var(--space-xs);
|
||||||
|
background: var(--color-bg-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 480px) {
|
||||||
|
page-listing .listing-thumbnails {
|
||||||
|
grid-template-columns: repeat(5, 1fr);
|
||||||
|
gap: 4px;
|
||||||
|
padding: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
page-listing .thumbnail {
|
||||||
|
aspect-ratio: 1;
|
||||||
|
border: 2px solid transparent;
|
||||||
|
border-radius: var(--radius-md);
|
||||||
|
overflow: hidden;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: border-color var(--transition-fast), opacity var(--transition-fast);
|
||||||
|
padding: 0;
|
||||||
|
background: var(--color-bg-tertiary);
|
||||||
|
}
|
||||||
|
|
||||||
|
page-listing .thumbnail:hover {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
page-listing .thumbnail.active {
|
||||||
|
border-color: var(--color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
page-listing .thumbnail img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
|
||||||
page-listing .listing-info header {
|
page-listing .listing-info header {
|
||||||
margin-bottom: var(--space-xl);
|
margin-bottom: var(--space-xl);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -253,16 +253,19 @@ class DirectusService {
|
|||||||
async getListings(options = {}) {
|
async getListings(options = {}) {
|
||||||
const params = {
|
const params = {
|
||||||
fields: options.fields || [
|
fields: options.fields || [
|
||||||
'*',
|
'id',
|
||||||
|
'status',
|
||||||
|
'title',
|
||||||
|
'slug',
|
||||||
|
'price',
|
||||||
|
'currency',
|
||||||
|
'condition',
|
||||||
|
'date_created',
|
||||||
'images.directus_files_id.id',
|
'images.directus_files_id.id',
|
||||||
'images.directus_files_id.title',
|
|
||||||
'category.id',
|
'category.id',
|
||||||
'category.name',
|
'category.name',
|
||||||
'category.slug',
|
'category.slug',
|
||||||
'category.icon',
|
'category.icon'
|
||||||
'location.id',
|
|
||||||
'location.name',
|
|
||||||
'location.region'
|
|
||||||
],
|
],
|
||||||
filter: options.filter || { status: { _eq: 'published' } },
|
filter: options.filter || { status: { _eq: 'published' } },
|
||||||
sort: options.sort || ['-date_created'],
|
sort: options.sort || ['-date_created'],
|
||||||
@@ -284,11 +287,26 @@ class DirectusService {
|
|||||||
async getListing(id) {
|
async getListing(id) {
|
||||||
const response = await this.get(`/items/listings/${id}`, {
|
const response = await this.get(`/items/listings/${id}`, {
|
||||||
fields: [
|
fields: [
|
||||||
'*',
|
'id',
|
||||||
'images.directus_files_id.*',
|
'status',
|
||||||
'category.*',
|
'title',
|
||||||
'category.translations.*',
|
'slug',
|
||||||
'location.*'
|
'description',
|
||||||
|
'price',
|
||||||
|
'currency',
|
||||||
|
'price_mode',
|
||||||
|
'price_type',
|
||||||
|
'condition',
|
||||||
|
'shipping',
|
||||||
|
'shipping_cost',
|
||||||
|
'views',
|
||||||
|
'expires_at',
|
||||||
|
'monero_address',
|
||||||
|
'date_created',
|
||||||
|
'images.directus_files_id.id',
|
||||||
|
'category.id',
|
||||||
|
'category.name',
|
||||||
|
'category.slug'
|
||||||
]
|
]
|
||||||
})
|
})
|
||||||
return response.data
|
return response.data
|
||||||
|
|||||||
Reference in New Issue
Block a user