import { t, i18n } from '../i18n.js' import { escapeHTML } from '../utils/helpers.js' import { getXmrRates, formatPrice as formatCurrencyPrice } from '../services/currency.js' import { auth } from '../services/auth.js' import { favoritesService } from '../services/favorites.js' class ListingCard extends HTMLElement { static get observedAttributes() { return ['listing-id', 'title', 'price', 'currency', 'location', 'image', 'owner-id', 'payment-status', 'status', 'priority'] } constructor() { super() this.isFavorite = false this.rates = null this.isOwner = false this.handleCurrencyChange = this.handleCurrencyChange.bind(this) } async connectedCallback() { this.loadFavoriteState() await this.loadRates() await this.checkOwnership() this.render() this.setupEventListeners() window.addEventListener('currency-changed', this.handleCurrencyChange) } disconnectedCallback() { window.removeEventListener('currency-changed', this.handleCurrencyChange) } handleCurrencyChange() { this.render() this.setupEventListeners() } async checkOwnership() { const ownerId = this.getAttribute('owner-id') if (!ownerId || !auth.isLoggedIn()) { this.isOwner = false return } try { const user = await auth.getUser() this.isOwner = user?.id === ownerId } catch { this.isOwner = false } } async loadRates() { this.rates = await getXmrRates() } attributeChangedCallback() { if (this.isConnected) { this.render() this.setupEventListeners() } } loadFavoriteState() { const id = this.getAttribute('listing-id') if (id) { this.isFavorite = favoritesService.isFavorite(id) } } render() { const id = this.getAttribute('listing-id') || '' const title = this.getAttribute('title') || t('home.placeholderTitle') const price = this.getAttribute('price') const currency = this.getAttribute('currency') || 'EUR' const location = this.getAttribute('location') || t('home.placeholderLocation') const image = this.getAttribute('image') 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 */` ` const paymentStatus = this.getAttribute('payment-status') const status = this.getAttribute('status') const isDeleted = status === 'deleted' const ownerBadge = (this.isOwner && !isDeleted) ? /* html */` ` : '' let paymentBadge = '' if (status === 'deleted') { paymentBadge = /* html */`${t('myListings.status.deleted')}` } else if (status === 'archived') { paymentBadge = /* html */`${t('myListings.status.expired')}` } else if (paymentStatus === 'processing' || paymentStatus === 'pending') { paymentBadge = /* html */`${t('myListings.status.processing')}` } else if (paymentStatus === 'expired') { paymentBadge = /* html */`${t('myListings.status.expired')}` } else if (paymentStatus === 'paid' && status === 'draft') { paymentBadge = /* html */`${t('myListings.status.unpublished')}` } else if (paymentStatus === 'paid') { paymentBadge = /* html */`${t('myListings.status.published')}` } const linkTag = isDeleted ? 'div' : 'a' const linkAttr = isDeleted ? '' : `href="#/listing/${escapeHTML(id)}"` this.innerHTML = /* html */` ${ownerBadge} <${linkTag} ${linkAttr} class="listing-link">
${image ? `${escapeHTML(title)}` : placeholderSvg} ${paymentBadge}

${escapeHTML(title)}

${priceDisplay}

${secondaryPrice ? `

${secondaryPrice}

` : ''}

${escapeHTML(location)}

${!isDeleted ? /* html */` ` : ''} ` } setupEventListeners() { const btn = this.querySelector('.favorite-btn') btn?.addEventListener('click', (e) => { e.preventDefault() e.stopPropagation() this.toggleFavorite() }) } toggleFavorite() { const id = this.getAttribute('listing-id') if (!id) return this.isFavorite = !this.isFavorite favoritesService.toggle(id) const btn = this.querySelector('.favorite-btn') btn?.classList.toggle('active', this.isFavorite) btn?.setAttribute('aria-pressed', this.isFavorite) btn?.setAttribute('aria-label', this.isFavorite ? t('home.removeFavorite') : t('home.addFavorite')) btn?.classList.add('animate__animated', 'animate__heartBeat') btn?.addEventListener('animationend', () => { btn?.classList.remove('animate__animated', 'animate__heartBeat') }, { once: true }) this.dispatchEvent(new CustomEvent('favorite-toggle', { bubbles: true, detail: { id: this.getAttribute('listing-id'), isFavorite: this.isFavorite } })) } } customElements.define('listing-card', ListingCard) const style = document.createElement('style') style.textContent = /* css */` listing-card { display: block; position: relative; background: var(--color-bg-secondary); border: 1px solid var(--color-border); border-radius: var(--radius-md); overflow: hidden; transition: box-shadow var(--transition-fast); min-width: 0; } listing-card:hover { box-shadow: var(--shadow-md); } listing-card .listing-link { display: block; text-decoration: none; color: inherit; } listing-card .listing-image { aspect-ratio: 1; background: var(--color-bg-tertiary); display: flex; align-items: center; justify-content: center; overflow: hidden; position: relative; } listing-card .payment-badge { position: absolute; bottom: 0; left: 0; right: 0; padding: var(--space-xs) var(--space-sm); font-size: var(--font-size-xs); font-weight: var(--font-weight-medium); text-align: center; } listing-card .payment-processing { background: rgba(230, 167, 0, 0.9); color: #fff; display: flex; align-items: center; justify-content: center; gap: 6px; } listing-card .payment-published { background: var(--color-accent); color: var(--color-accent-text, #fff); } listing-card .payment-expired { background: rgba(180, 60, 60, 0.85); color: #fff; } listing-card .payment-unpublished { background: rgba(120, 120, 120, 0.85); color: #fff; } listing-card .pulse-dot { width: 6px; height: 6px; border-radius: var(--radius-full); background: #fff; animation: card-pulse 1.5s ease-in-out infinite; } @keyframes card-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.3; } } listing-card .listing-image img { width: 100%; height: 100%; object-fit: cover; transition: transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94); } listing-card:hover .listing-image img { transform: scale(1.08); } listing-card .listing-image .placeholder-icon { width: 48px; height: 48px; color: var(--color-border); } listing-card .listing-info { padding: var(--space-sm); } listing-card .listing-title { font-size: var(--font-size-sm); font-weight: var(--font-weight-medium); margin: 0 0 var(--space-xs); white-space: nowrap; overflow: hidden; 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); color: var(--color-primary); margin: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } listing-card .listing-price-secondary { font-size: var(--font-size-xs); color: var(--color-text-muted); margin: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } listing-card .listing-location { font-size: var(--font-size-xs); color: var(--color-text-muted); margin: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } listing-card .favorite-btn { position: absolute; top: var(--space-sm); right: var(--space-sm); width: 36px; height: 36px; display: flex; align-items: center; justify-content: center; background: var(--color-bg); border: none; border-radius: var(--radius-full); cursor: pointer; box-shadow: var(--shadow-sm); transition: all var(--transition-fast); z-index: 1; } listing-card .favorite-btn:hover { transform: scale(1.1); } listing-card .favorite-btn .heart-icon { color: var(--color-text-muted); transition: all var(--transition-fast); } listing-card .favorite-btn.active .heart-icon { fill: var(--color-accent); stroke: var(--color-accent); color: var(--color-accent); } listing-card .favorite-btn:hover .heart-icon { color: var(--color-accent); } listing-card .owner-badge { position: absolute; top: var(--space-sm); left: var(--space-sm); width: 36px; height: 36px; display: flex; align-items: center; justify-content: center; background: var(--color-bg); border: none; border-radius: var(--radius-full); box-shadow: var(--shadow-sm); z-index: 2; color: var(--color-text-muted); transition: all var(--transition-fast); text-decoration: none; } listing-card .owner-badge:hover { background: var(--color-primary); color: white; transform: scale(1.1); } ` document.head.appendChild(style)