827 lines
28 KiB
JavaScript
827 lines
28 KiB
JavaScript
import { t, i18n } from '../../i18n.js'
|
|
import { directus } from '../../services/directus.js'
|
|
import { listingsService } from '../../services/listings.js'
|
|
import '../chat-widget.js'
|
|
import '../location-map.js'
|
|
import '../listing-card.js'
|
|
|
|
class PageListing extends HTMLElement {
|
|
constructor() {
|
|
super()
|
|
this.listing = null
|
|
this.sellerListings = []
|
|
this.loading = true
|
|
this.isFavorite = false
|
|
}
|
|
|
|
connectedCallback() {
|
|
this.listingId = this.dataset.id
|
|
this.render()
|
|
this.loadListing()
|
|
this.unsubscribe = i18n.subscribe(() => this.render())
|
|
}
|
|
|
|
disconnectedCallback() {
|
|
if (this.unsubscribe) this.unsubscribe()
|
|
}
|
|
|
|
async loadListing() {
|
|
try {
|
|
this.listing = await directus.getListing(this.listingId)
|
|
this.loadFavoriteState()
|
|
|
|
// Load other listings from same seller
|
|
if (this.listing?.user_created) {
|
|
await this.loadSellerListings()
|
|
}
|
|
} catch (e) {
|
|
console.error('Failed to load listing:', e)
|
|
this.listing = null
|
|
}
|
|
|
|
this.loading = false
|
|
this.render()
|
|
this.setupEventListeners()
|
|
}
|
|
|
|
async loadSellerListings() {
|
|
try {
|
|
const response = await directus.getListings({
|
|
filter: {
|
|
status: { _eq: 'published' },
|
|
user_created: { _eq: this.listing.user_created },
|
|
id: { _neq: this.listing.id }
|
|
},
|
|
limit: 4
|
|
})
|
|
this.sellerListings = response.items || []
|
|
} catch (e) {
|
|
console.error('Failed to load seller listings:', e)
|
|
this.sellerListings = []
|
|
}
|
|
}
|
|
|
|
loadFavoriteState() {
|
|
const favorites = JSON.parse(localStorage.getItem('favorites') || '[]')
|
|
this.isFavorite = favorites.includes(this.listingId)
|
|
}
|
|
|
|
toggleFavorite() {
|
|
let favorites = JSON.parse(localStorage.getItem('favorites') || '[]')
|
|
|
|
if (this.isFavorite) {
|
|
favorites = favorites.filter(f => f !== this.listingId)
|
|
} else {
|
|
favorites.push(this.listingId)
|
|
}
|
|
|
|
localStorage.setItem('favorites', JSON.stringify(favorites))
|
|
this.isFavorite = !this.isFavorite
|
|
|
|
const btn = this.querySelector('#favorite-btn')
|
|
btn?.classList.toggle('active', this.isFavorite)
|
|
btn.innerHTML = this.getFavoriteIcon()
|
|
}
|
|
|
|
async copyShareLink() {
|
|
const url = window.location.href
|
|
try {
|
|
await navigator.clipboard.writeText(url)
|
|
const btn = this.querySelector('#share-btn')
|
|
btn?.classList.add('copied')
|
|
setTimeout(() => btn?.classList.remove('copied'), 2000)
|
|
} catch (e) {
|
|
console.error('Failed to copy:', e)
|
|
}
|
|
}
|
|
|
|
render() {
|
|
if (this.loading) {
|
|
this.innerHTML = /* html */`
|
|
<div class="loading">
|
|
<div class="spinner"></div>
|
|
</div>
|
|
`
|
|
return
|
|
}
|
|
|
|
if (!this.listing) {
|
|
this.innerHTML = /* html */`
|
|
<div class="empty-state">
|
|
<div class="empty-state-icon">😕</div>
|
|
<p>${t('listing.notFound')}</p>
|
|
<a href="#/" class="btn btn-primary">${t('listing.backHome')}</a>
|
|
</div>
|
|
`
|
|
return
|
|
}
|
|
|
|
const images = (this.listing.images || []).slice(0, 5)
|
|
const hasImages = images.length > 0
|
|
const firstImage = hasImages ? this.getImageUrl(images[0], 800) : null
|
|
this.allImages = images
|
|
|
|
const categoryName = this.listing.category?.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">
|
|
<!-- Gallery - Full Width -->
|
|
<div class="listing-gallery">
|
|
<div class="listing-image-main" id="main-image">
|
|
${firstImage
|
|
? `<img src="${firstImage}" alt="${this.escapeHtml(this.listing.title)}" id="main-img">`
|
|
: this.getPlaceholderSvg()}
|
|
</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, 150)}" alt="">
|
|
</button>
|
|
`).join('')}
|
|
</div>
|
|
` : ''}
|
|
</div>
|
|
|
|
<!-- Content Row: Info + Actions Sidebar -->
|
|
<div class="listing-content">
|
|
<!-- Main Info -->
|
|
<div class="listing-info">
|
|
<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-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>` : ''}
|
|
<span class="meta-item meta-date">${t('listing.postedOn')} ${createdDate}</span>
|
|
</div>
|
|
</header>
|
|
|
|
<section class="listing-description">
|
|
<h2>${t('listing.description')}</h2>
|
|
<div class="description-text">${this.formatDescription(this.listing.description)}</div>
|
|
</section>
|
|
|
|
${this.listing.location ? `
|
|
<section class="listing-location-section">
|
|
<h2>${t('listing.location')}</h2>
|
|
<location-map
|
|
name="${this.escapeHtml(this.listing.location.name || '')}"
|
|
postal-code="${this.escapeHtml(this.listing.location.postal_code || '')}"
|
|
country="${this.escapeHtml(this.listing.location.country || '')}"
|
|
></location-map>
|
|
</section>
|
|
` : ''}
|
|
</div>
|
|
|
|
<!-- Actions Sidebar -->
|
|
<aside class="listing-sidebar">
|
|
<div class="sidebar-card">
|
|
<button class="btn btn-primary btn-lg sidebar-btn" id="contact-btn">
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<path d="M21 15a2 2 0 0 1-2 2H7l-4 4V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2z"></path>
|
|
</svg>
|
|
${t('listing.contactSeller')}
|
|
</button>
|
|
|
|
<div class="sidebar-actions">
|
|
<button class="action-btn ${this.isFavorite ? 'active' : ''}" id="favorite-btn" title="${t('home.addFavorite')}">
|
|
${this.getFavoriteIcon()}
|
|
<span>${this.isFavorite ? t('home.removeFavorite') : t('home.addFavorite')}</span>
|
|
</button>
|
|
|
|
<button class="action-btn" id="share-btn" title="${t('listing.share') || 'Teilen'}">
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<circle cx="18" cy="5" r="3"></circle>
|
|
<circle cx="6" cy="12" r="3"></circle>
|
|
<circle cx="18" cy="19" r="3"></circle>
|
|
<line x1="8.59" y1="13.51" x2="15.42" y2="17.49"></line>
|
|
<line x1="15.41" y1="6.51" x2="8.59" y2="10.49"></line>
|
|
</svg>
|
|
<span>${t('listing.share') || 'Teilen'}</span>
|
|
</button>
|
|
|
|
<button class="action-btn" id="report-btn" title="${t('listing.report') || 'Melden'}">
|
|
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<path d="M4 15s1-1 4-1 5 2 8 2 4-1 4-1V3s-1 1-4 1-5-2-8-2-4 1-4 1z"></path>
|
|
<line x1="4" y1="22" x2="4" y2="15"></line>
|
|
</svg>
|
|
<span>${t('listing.report') || 'Melden'}</span>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Seller Card -->
|
|
<div class="sidebar-card seller-card">
|
|
<div class="seller-header">
|
|
<div class="seller-avatar">?</div>
|
|
<div class="seller-info">
|
|
<strong>${t('listing.anonymousSeller')}</strong>
|
|
<span>${t('listing.memberSince')} 2024</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</aside>
|
|
</div>
|
|
|
|
<!-- Seller's Other Listings -->
|
|
${this.sellerListings.length > 0 ? `
|
|
<section class="seller-listings">
|
|
<h2>${t('listing.moreFromSeller') || 'Weitere Anzeigen des Anbieters'}</h2>
|
|
<div class="listings-grid">
|
|
${this.sellerListings.map(listing => this.renderListingCard(listing)).join('')}
|
|
</div>
|
|
</section>
|
|
` : ''}
|
|
</article>
|
|
|
|
${this.renderContactDialog()}
|
|
`
|
|
}
|
|
|
|
renderListingCard(listing) {
|
|
const imageId = listing.images?.[0]?.directus_files_id?.id || listing.images?.[0]?.directus_files_id
|
|
const imageUrl = imageId ? directus.getThumbnailUrl(imageId, 300) : ''
|
|
const locationName = listing.location?.name || ''
|
|
|
|
return /* html */`
|
|
<listing-card
|
|
listing-id="${listing.id}"
|
|
title="${this.escapeHtml(listing.title || '')}"
|
|
price="${listing.price || ''}"
|
|
currency="${listing.currency || 'EUR'}"
|
|
location="${this.escapeHtml(locationName)}"
|
|
image="${imageUrl}"
|
|
></listing-card>
|
|
`
|
|
}
|
|
|
|
renderContactDialog() {
|
|
return /* html */`
|
|
<dialog class="contact-dialog" id="contact-dialog">
|
|
<button class="dialog-close" id="dialog-close" aria-label="Close">
|
|
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
</svg>
|
|
</button>
|
|
|
|
<div class="dialog-tabs">
|
|
<button class="tab-btn active" data-tab="chat">${t('chat.title')}</button>
|
|
<button class="tab-btn" data-tab="payment">${t('listing.moneroAddress')}</button>
|
|
</div>
|
|
|
|
<div class="tab-content" id="tab-chat">
|
|
<chat-widget
|
|
listing-id="${this.listing?.id || ''}"
|
|
recipient-id="${this.listing?.user_created || ''}"
|
|
recipient-key=""
|
|
recipient-name="${t('listing.anonymousSeller')}"
|
|
></chat-widget>
|
|
</div>
|
|
|
|
<div class="tab-content hidden" id="tab-payment">
|
|
<p class="dialog-subtitle">${t('listing.paymentInfo')}</p>
|
|
|
|
<div class="monero-section">
|
|
<label>${t('listing.moneroAddress')}</label>
|
|
<div class="monero-address">
|
|
<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-addr-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>
|
|
|
|
<p class="dialog-hint">${t('listing.contactHint')}</p>
|
|
</div>
|
|
</dialog>
|
|
`
|
|
}
|
|
|
|
getFavoriteIcon() {
|
|
return this.isFavorite
|
|
? `<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor" stroke="currentColor" stroke-width="2">
|
|
<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path>
|
|
</svg>`
|
|
: `<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
<path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"></path>
|
|
</svg>`
|
|
}
|
|
|
|
getPlaceholderSvg() {
|
|
return /* 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>
|
|
<circle cx="8.5" cy="8.5" r="1.5"></circle>
|
|
<polyline points="21 15 16 10 5 21"></polyline>
|
|
</svg>
|
|
`
|
|
}
|
|
|
|
setupEventListeners() {
|
|
// Contact dialog
|
|
const contactBtn = this.querySelector('#contact-btn')
|
|
const dialog = this.querySelector('#contact-dialog')
|
|
const closeBtn = this.querySelector('#dialog-close')
|
|
|
|
contactBtn?.addEventListener('click', () => dialog?.showModal())
|
|
closeBtn?.addEventListener('click', () => dialog?.close())
|
|
dialog?.addEventListener('click', (e) => {
|
|
if (e.target === dialog) dialog.close()
|
|
})
|
|
|
|
// Dialog tabs
|
|
this.querySelectorAll('.tab-btn').forEach(btn => {
|
|
btn.addEventListener('click', () => {
|
|
const tab = btn.dataset.tab
|
|
this.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'))
|
|
btn.classList.add('active')
|
|
this.querySelectorAll('.tab-content').forEach(content => {
|
|
content.classList.toggle('hidden', content.id !== `tab-${tab}`)
|
|
})
|
|
})
|
|
})
|
|
|
|
// Copy Monero address
|
|
this.querySelector('#copy-addr-btn')?.addEventListener('click', async () => {
|
|
const addr = this.querySelector('#monero-addr')?.textContent
|
|
if (addr) {
|
|
await navigator.clipboard.writeText(addr)
|
|
const btn = this.querySelector('#copy-addr-btn')
|
|
btn?.classList.add('copied')
|
|
setTimeout(() => btn?.classList.remove('copied'), 2000)
|
|
}
|
|
})
|
|
|
|
// Favorite button
|
|
this.querySelector('#favorite-btn')?.addEventListener('click', () => this.toggleFavorite())
|
|
|
|
// Share button
|
|
this.querySelector('#share-btn')?.addEventListener('click', () => this.copyShareLink())
|
|
|
|
// Thumbnail gallery
|
|
this.querySelectorAll('.thumbnail').forEach(thumb => {
|
|
thumb.addEventListener('click', () => {
|
|
const index = parseInt(thumb.dataset.index)
|
|
const mainImg = this.querySelector('#main-img')
|
|
if (mainImg && this.allImages[index]) {
|
|
mainImg.src = this.getImageUrl(this.allImages[index], 800)
|
|
this.querySelectorAll('.thumbnail').forEach(t => t.classList.remove('active'))
|
|
thumb.classList.add('active')
|
|
}
|
|
})
|
|
})
|
|
}
|
|
|
|
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 && price !== 0) return t('listing.priceOnRequest')
|
|
|
|
if (currency === 'XMR') {
|
|
return `${parseFloat(price).toFixed(4)} XMR`
|
|
}
|
|
|
|
return new Intl.NumberFormat('de-CH', {
|
|
style: 'currency',
|
|
currency: currency
|
|
}).format(price)
|
|
}
|
|
|
|
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
|
|
}
|
|
|
|
formatDescription(text) {
|
|
if (!text) return ''
|
|
return text
|
|
.replace(/&/g, '&')
|
|
.replace(/</g, '<')
|
|
.replace(/>/g, '>')
|
|
.replace(/\n/g, '<br>')
|
|
}
|
|
|
|
escapeHtml(text) {
|
|
if (!text) return ''
|
|
const div = document.createElement('div')
|
|
div.textContent = text
|
|
return div.innerHTML
|
|
}
|
|
}
|
|
|
|
customElements.define('page-listing', PageListing)
|
|
|
|
const style = document.createElement('style')
|
|
style.textContent = /* css */`
|
|
page-listing .listing-detail {
|
|
padding: var(--space-lg) 0;
|
|
}
|
|
|
|
/* Gallery - Full Width */
|
|
page-listing .listing-gallery {
|
|
background: var(--color-bg-secondary);
|
|
border-radius: var(--radius-lg);
|
|
overflow: hidden;
|
|
margin-bottom: var(--space-xl);
|
|
}
|
|
|
|
page-listing .listing-image-main {
|
|
aspect-ratio: 16/9;
|
|
max-height: 500px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
overflow: hidden;
|
|
background: var(--color-bg-tertiary);
|
|
}
|
|
|
|
page-listing .listing-image-main img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: contain;
|
|
}
|
|
|
|
page-listing .listing-image-main .placeholder-icon {
|
|
width: 80px;
|
|
height: 80px;
|
|
color: var(--color-border);
|
|
}
|
|
|
|
page-listing .listing-thumbnails {
|
|
display: flex;
|
|
gap: var(--space-xs);
|
|
padding: var(--space-sm);
|
|
overflow-x: auto;
|
|
}
|
|
|
|
page-listing .thumbnail {
|
|
flex-shrink: 0;
|
|
width: 80px;
|
|
height: 80px;
|
|
border: 2px solid transparent;
|
|
border-radius: var(--radius-md);
|
|
overflow: hidden;
|
|
cursor: pointer;
|
|
padding: 0;
|
|
background: var(--color-bg-tertiary);
|
|
}
|
|
|
|
page-listing .thumbnail.active {
|
|
border-color: var(--color-primary);
|
|
}
|
|
|
|
page-listing .thumbnail img {
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
|
|
/* Content Row */
|
|
page-listing .listing-content {
|
|
display: grid;
|
|
grid-template-columns: 1fr 320px;
|
|
gap: var(--space-xl);
|
|
align-items: start;
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
page-listing .listing-content {
|
|
grid-template-columns: 1fr;
|
|
}
|
|
|
|
page-listing .listing-sidebar {
|
|
position: static;
|
|
order: -1;
|
|
}
|
|
|
|
page-listing .sidebar-card {
|
|
padding: var(--space-md);
|
|
}
|
|
}
|
|
|
|
/* Main Info */
|
|
page-listing .listing-info {
|
|
min-width: 0;
|
|
}
|
|
|
|
page-listing .listing-header {
|
|
margin-bottom: var(--space-xl);
|
|
}
|
|
|
|
page-listing .listing-header h1 {
|
|
margin: var(--space-sm) 0;
|
|
font-size: var(--font-size-2xl);
|
|
}
|
|
|
|
page-listing .listing-price {
|
|
font-size: var(--font-size-3xl);
|
|
font-weight: var(--font-weight-bold);
|
|
color: var(--color-primary);
|
|
margin: 0;
|
|
}
|
|
|
|
page-listing .listing-meta {
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
gap: var(--space-sm);
|
|
margin-top: var(--space-md);
|
|
}
|
|
|
|
page-listing .meta-item {
|
|
font-size: var(--font-size-sm);
|
|
color: var(--color-text-secondary);
|
|
background: var(--color-bg-secondary);
|
|
padding: var(--space-xs) var(--space-sm);
|
|
border-radius: var(--radius-sm);
|
|
}
|
|
|
|
page-listing .meta-date {
|
|
margin-left: auto;
|
|
}
|
|
|
|
page-listing .listing-description,
|
|
page-listing .listing-location-section {
|
|
margin-bottom: var(--space-xl);
|
|
}
|
|
|
|
page-listing .listing-description h2,
|
|
page-listing .listing-location-section h2 {
|
|
font-size: var(--font-size-lg);
|
|
margin-bottom: var(--space-md);
|
|
}
|
|
|
|
page-listing .description-text {
|
|
line-height: 1.6;
|
|
color: var(--color-text-secondary);
|
|
}
|
|
|
|
/* Sidebar */
|
|
page-listing .listing-sidebar {
|
|
position: sticky;
|
|
top: var(--space-lg);
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-md);
|
|
}
|
|
|
|
@media (max-width: 768px) {
|
|
page-listing .listing-sidebar {
|
|
position: static;
|
|
}
|
|
}
|
|
|
|
page-listing .sidebar-card {
|
|
background: var(--color-bg-secondary);
|
|
border: 1px solid var(--color-border);
|
|
border-radius: var(--radius-lg);
|
|
padding: var(--space-lg);
|
|
}
|
|
|
|
page-listing .sidebar-btn {
|
|
width: 100%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: var(--space-sm);
|
|
}
|
|
|
|
page-listing .sidebar-actions {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-xs);
|
|
margin-top: var(--space-md);
|
|
padding-top: var(--space-md);
|
|
border-top: 1px solid var(--color-border);
|
|
}
|
|
|
|
page-listing .action-btn {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-sm);
|
|
padding: var(--space-sm) var(--space-md);
|
|
background: transparent;
|
|
border: none;
|
|
border-radius: var(--radius-md);
|
|
color: var(--color-text-secondary);
|
|
cursor: pointer;
|
|
transition: all var(--transition-fast);
|
|
font-size: var(--font-size-sm);
|
|
}
|
|
|
|
page-listing .action-btn:hover {
|
|
background: var(--color-bg-tertiary);
|
|
color: var(--color-text);
|
|
}
|
|
|
|
page-listing .action-btn.active {
|
|
color: var(--color-accent);
|
|
background: var(--color-bg-tertiary);
|
|
}
|
|
|
|
page-listing .action-btn.copied {
|
|
color: var(--color-success);
|
|
}
|
|
|
|
/* Seller Card */
|
|
page-listing .seller-header {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: var(--space-md);
|
|
}
|
|
|
|
page-listing .seller-avatar {
|
|
width: 48px;
|
|
height: 48px;
|
|
border-radius: 50%;
|
|
background: var(--color-primary);
|
|
color: white;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
font-weight: var(--font-weight-bold);
|
|
font-size: var(--font-size-lg);
|
|
}
|
|
|
|
page-listing .seller-info {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
page-listing .seller-info span {
|
|
font-size: var(--font-size-sm);
|
|
color: var(--color-text-muted);
|
|
}
|
|
|
|
/* Seller Listings */
|
|
page-listing .seller-listings {
|
|
margin-top: var(--space-3xl);
|
|
padding-top: var(--space-xl);
|
|
border-top: 1px solid var(--color-border);
|
|
}
|
|
|
|
page-listing .seller-listings h2 {
|
|
margin-bottom: var(--space-lg);
|
|
}
|
|
|
|
/* Dialog */
|
|
page-listing .contact-dialog {
|
|
background: var(--color-bg);
|
|
border: 1px solid var(--color-border);
|
|
border-radius: var(--radius-lg);
|
|
padding: var(--space-xl);
|
|
max-width: 500px;
|
|
width: calc(100% - 2 * var(--space-md));
|
|
position: fixed;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
margin: 0;
|
|
}
|
|
|
|
page-listing .contact-dialog::backdrop {
|
|
background: var(--color-overlay);
|
|
}
|
|
|
|
page-listing .dialog-tabs {
|
|
display: flex;
|
|
gap: var(--space-sm);
|
|
margin-bottom: var(--space-lg);
|
|
border-bottom: 1px solid var(--color-border);
|
|
padding-bottom: var(--space-sm);
|
|
}
|
|
|
|
page-listing .tab-btn {
|
|
padding: var(--space-sm) var(--space-md);
|
|
font-size: var(--font-size-sm);
|
|
font-weight: var(--font-weight-medium);
|
|
color: var(--color-text-muted);
|
|
border-radius: var(--radius-md);
|
|
transition: all var(--transition-fast);
|
|
background: transparent;
|
|
border: none;
|
|
cursor: pointer;
|
|
}
|
|
|
|
page-listing .tab-btn:hover {
|
|
color: var(--color-text);
|
|
background: var(--color-bg-secondary);
|
|
}
|
|
|
|
page-listing .tab-btn.active {
|
|
color: var(--color-primary);
|
|
background: var(--color-primary-light);
|
|
}
|
|
|
|
page-listing .tab-content.hidden {
|
|
display: none;
|
|
}
|
|
|
|
page-listing .dialog-close {
|
|
position: absolute;
|
|
top: var(--space-md);
|
|
right: var(--space-md);
|
|
padding: var(--space-xs);
|
|
color: var(--color-text-muted);
|
|
background: transparent;
|
|
border: none;
|
|
cursor: pointer;
|
|
}
|
|
|
|
page-listing .dialog-close:hover {
|
|
color: var(--color-text);
|
|
}
|
|
|
|
page-listing .dialog-subtitle {
|
|
color: var(--color-text-secondary);
|
|
margin-bottom: var(--space-lg);
|
|
}
|
|
|
|
page-listing .monero-section {
|
|
margin-bottom: var(--space-lg);
|
|
}
|
|
|
|
page-listing .monero-section label {
|
|
display: block;
|
|
font-size: var(--font-size-sm);
|
|
font-weight: var(--font-weight-medium);
|
|
margin-bottom: var(--space-sm);
|
|
color: var(--color-text-secondary);
|
|
}
|
|
|
|
page-listing .monero-address {
|
|
display: flex;
|
|
gap: var(--space-sm);
|
|
align-items: stretch;
|
|
}
|
|
|
|
page-listing .monero-address code {
|
|
flex: 1;
|
|
padding: var(--space-sm) var(--space-md);
|
|
background: var(--color-bg-tertiary);
|
|
border: 1px solid var(--color-border);
|
|
border-radius: var(--radius-md);
|
|
font-size: var(--font-size-xs);
|
|
word-break: break-all;
|
|
line-height: 1.4;
|
|
}
|
|
|
|
page-listing .btn-copy {
|
|
padding: var(--space-sm);
|
|
flex-shrink: 0;
|
|
}
|
|
|
|
page-listing .btn-copy.copied {
|
|
background: var(--color-success);
|
|
border-color: var(--color-success);
|
|
color: white;
|
|
}
|
|
|
|
page-listing .dialog-hint {
|
|
font-size: var(--font-size-sm);
|
|
color: var(--color-text-muted);
|
|
text-align: center;
|
|
}
|
|
|
|
/* Loading & Empty States */
|
|
page-listing .loading {
|
|
display: flex;
|
|
justify-content: center;
|
|
padding: var(--space-3xl);
|
|
}
|
|
|
|
page-listing .empty-state {
|
|
text-align: center;
|
|
padding: var(--space-3xl);
|
|
}
|
|
|
|
page-listing .empty-state-icon {
|
|
font-size: 3rem;
|
|
margin-bottom: var(--space-md);
|
|
}
|
|
`
|
|
document.head.appendChild(style)
|