add secondary price display to listing-card and page-listing
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
import { t, i18n } from '../i18n.js'
|
||||
import { escapeHTML, formatPrice } from '../utils/helpers.js'
|
||||
import { escapeHTML } from '../utils/helpers.js'
|
||||
import { getXmrRates, formatPrice as formatCurrencyPrice } from '../services/currency.js'
|
||||
|
||||
let cachedRates = null
|
||||
|
||||
class ListingCard extends HTMLElement {
|
||||
static get observedAttributes() {
|
||||
@@ -9,14 +12,23 @@ class ListingCard extends HTMLElement {
|
||||
constructor() {
|
||||
super()
|
||||
this.isFavorite = false
|
||||
this.rates = null
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
async connectedCallback() {
|
||||
this.loadFavoriteState()
|
||||
await this.loadRates()
|
||||
this.render()
|
||||
this.setupEventListeners()
|
||||
}
|
||||
|
||||
async loadRates() {
|
||||
if (!cachedRates) {
|
||||
cachedRates = await getXmrRates()
|
||||
}
|
||||
this.rates = cachedRates
|
||||
}
|
||||
|
||||
attributeChangedCallback() {
|
||||
if (this.isConnected) {
|
||||
this.render()
|
||||
@@ -55,7 +67,20 @@ class ListingCard extends HTMLElement {
|
||||
const location = this.getAttribute('location') || t('home.placeholderLocation')
|
||||
const image = this.getAttribute('image')
|
||||
|
||||
const priceDisplay = price ? formatPrice(parseFloat(price), currency) : '–'
|
||||
let priceDisplay = '–'
|
||||
let secondaryPrice = null
|
||||
|
||||
if (price && this.rates) {
|
||||
const listing = { price: parseFloat(price), currency, price_mode: currency === 'XMR' ? 'xmr' : 'fiat' }
|
||||
const formatted = formatCurrencyPrice(listing, this.rates)
|
||||
priceDisplay = formatted.primary
|
||||
secondaryPrice = formatted.secondary
|
||||
} else if (price) {
|
||||
priceDisplay = currency === 'XMR'
|
||||
? `${parseFloat(price).toFixed(4)} XMR`
|
||||
: `€ ${parseFloat(price).toFixed(2)}`
|
||||
}
|
||||
|
||||
const favoriteLabel = this.isFavorite ? t('home.removeFavorite') : t('home.addFavorite')
|
||||
|
||||
const placeholderSvg = /* html */`
|
||||
@@ -73,7 +98,10 @@ class ListingCard extends HTMLElement {
|
||||
</div>
|
||||
<div class="listing-info">
|
||||
<h3 class="listing-title">${escapeHTML(title)}</h3>
|
||||
<p class="listing-price">${priceDisplay}</p>
|
||||
<div class="listing-price-wrapper">
|
||||
<p class="listing-price">${priceDisplay}</p>
|
||||
${secondaryPrice ? `<p class="listing-price-secondary">${secondaryPrice}</p>` : ''}
|
||||
</div>
|
||||
<p class="listing-location">${escapeHTML(location)}</p>
|
||||
</div>
|
||||
</a>
|
||||
@@ -172,6 +200,10 @@ style.textContent = /* css */`
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
listing-card .listing-price-wrapper {
|
||||
margin: 0 0 var(--space-xs);
|
||||
}
|
||||
|
||||
listing-card .listing-price {
|
||||
font-size: var(--font-size-sm);
|
||||
font-weight: var(--font-weight-bold);
|
||||
@@ -179,6 +211,12 @@ style.textContent = /* css */`
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
listing-card .listing-price-secondary {
|
||||
font-size: var(--font-size-xs);
|
||||
color: var(--color-text-muted);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
listing-card .listing-location {
|
||||
font-size: var(--font-size-xs);
|
||||
color: var(--color-text-muted);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { t, i18n } from '../../i18n.js'
|
||||
import { directus } from '../../services/directus.js'
|
||||
import { listingsService } from '../../services/listings.js'
|
||||
import { getXmrRates, formatPrice as formatCurrencyPrice } from '../../services/currency.js'
|
||||
import '../chat-widget.js'
|
||||
import '../location-map.js'
|
||||
import '../listing-card.js'
|
||||
@@ -12,6 +13,7 @@ class PageListing extends HTMLElement {
|
||||
this.sellerListings = []
|
||||
this.loading = true
|
||||
this.isFavorite = false
|
||||
this.rates = null
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
@@ -28,6 +30,7 @@ class PageListing extends HTMLElement {
|
||||
async loadListing() {
|
||||
try {
|
||||
this.listing = await directus.getListing(this.listingId)
|
||||
this.rates = await getXmrRates()
|
||||
this.loadFavoriteState()
|
||||
|
||||
// Increment view counter and update local state
|
||||
@@ -128,7 +131,7 @@ class PageListing extends HTMLElement {
|
||||
this.allImages = images
|
||||
|
||||
const categoryName = this.listing.category?.name || ''
|
||||
const price = this.formatPrice(this.listing.price, this.listing.currency)
|
||||
const priceInfo = this.getFormattedPrice()
|
||||
const createdDate = this.listing.date_created
|
||||
? new Date(this.listing.date_created).toLocaleDateString()
|
||||
: ''
|
||||
@@ -161,7 +164,10 @@ class PageListing extends HTMLElement {
|
||||
<header class="listing-header">
|
||||
${categoryName ? `<a href="#/search?category=${this.listing.category?.slug}" class="badge badge-primary">${this.escapeHtml(categoryName)}</a>` : ''}
|
||||
<h1>${this.escapeHtml(this.listing.title)}</h1>
|
||||
<p class="listing-price">${price}</p>
|
||||
<div class="listing-price-wrapper">
|
||||
<p class="listing-price">${priceInfo.primary}</p>
|
||||
${priceInfo.secondary ? `<p class="listing-price-secondary">${priceInfo.secondary}</p>` : ''}
|
||||
</div>
|
||||
<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>` : ''}
|
||||
@@ -438,6 +444,28 @@ class PageListing extends HTMLElement {
|
||||
}).format(price)
|
||||
}
|
||||
|
||||
getFormattedPrice() {
|
||||
const { price, currency } = this.listing
|
||||
|
||||
if (!price && price !== 0) {
|
||||
return { primary: t('listing.priceOnRequest'), secondary: null }
|
||||
}
|
||||
|
||||
if (this.rates) {
|
||||
const listing = {
|
||||
price: parseFloat(price),
|
||||
currency,
|
||||
price_mode: currency === 'XMR' ? 'xmr' : 'fiat'
|
||||
}
|
||||
return formatCurrencyPrice(listing, this.rates)
|
||||
}
|
||||
|
||||
return {
|
||||
primary: this.formatPrice(price, currency),
|
||||
secondary: null
|
||||
}
|
||||
}
|
||||
|
||||
getConditionLabel(condition) {
|
||||
const labels = {
|
||||
new: t('create.conditionNew'),
|
||||
@@ -593,13 +621,23 @@ style.textContent = /* css */`
|
||||
font-size: var(--font-size-2xl);
|
||||
}
|
||||
|
||||
page-listing .listing-header > .listing-price {
|
||||
font-size: var(--font-size-3xl);
|
||||
page-listing .listing-header .listing-price-wrapper {
|
||||
margin: var(--space-sm) 0;
|
||||
}
|
||||
|
||||
page-listing .listing-header .listing-price-wrapper .listing-price {
|
||||
font-size: var(--font-size-3xl) !important;
|
||||
font-weight: var(--font-weight-bold);
|
||||
color: var(--color-primary);
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
page-listing .listing-header .listing-price-wrapper .listing-price-secondary {
|
||||
font-size: var(--font-size-base);
|
||||
color: var(--color-text-muted);
|
||||
margin: var(--space-xs) 0 0;
|
||||
}
|
||||
|
||||
page-listing .listing-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
Reference in New Issue
Block a user