import { t, i18n } from '../i18n.js'; import { escapeHTML, formatPrice } from '../utils/helpers.js'; class ListingCard extends HTMLElement { static get observedAttributes() { return ['listing-id', 'title', 'price', 'currency', 'location', 'image']; } constructor() { super(); this.isFavorite = false; } connectedCallback() { this.loadFavoriteState(); this.render(); this.setupEventListeners(); } attributeChangedCallback() { if (this.isConnected) { this.render(); this.setupEventListeners(); } } loadFavoriteState() { const id = this.getAttribute('listing-id'); if (id) { const favorites = JSON.parse(localStorage.getItem('favorites') || '[]'); this.isFavorite = favorites.includes(id); } } saveFavoriteState() { const id = this.getAttribute('listing-id'); if (!id) return; let favorites = JSON.parse(localStorage.getItem('favorites') || '[]'); if (this.isFavorite) { if (!favorites.includes(id)) favorites.push(id); } else { favorites = favorites.filter(f => f !== id); } localStorage.setItem('favorites', JSON.stringify(favorites)); } 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'); const priceDisplay = price ? formatPrice(parseFloat(price), currency) : '–'; const favoriteLabel = this.isFavorite ? t('home.removeFavorite') : t('home.addFavorite'); this.innerHTML = /* html */ `

${escapeHTML(title)}

${priceDisplay}

${escapeHTML(location)}

`; } setupEventListeners() { const btn = this.querySelector('.favorite-btn'); btn?.addEventListener('click', (e) => { e.preventDefault(); e.stopPropagation(); this.toggleFavorite(); }); } toggleFavorite() { this.isFavorite = !this.isFavorite; this.saveFavoriteState(); 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); } 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); background-size: cover; background-position: center; } 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 { font-size: var(--font-size-sm); font-weight: var(--font-weight-bold); color: var(--color-primary); margin: 0; } listing-card .listing-location { font-size: var(--font-size-xs); color: var(--color-text-muted); margin: 0; } 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-error); stroke: var(--color-error); color: var(--color-error); } listing-card .favorite-btn:hover .heart-icon { color: var(--color-error); } `; document.head.appendChild(style);