@@ -171,9 +291,9 @@ class PageListing extends HTMLElement {
-
${this.listing.monero_address || t('listing.noMoneroAddress')}
- ${this.listing.monero_address ? `
-
`
-
- this.setupEventListeners()
+ }
+
+ getFavoriteIcon() {
+ return this.isFavorite
+ ? `
`
+ : `
`
+ }
+
+ getPlaceholderSvg() {
+ return /* html */`
+
+ `
}
setupEventListeners() {
+ // Contact dialog
const contactBtn = this.querySelector('#contact-btn')
const dialog = this.querySelector('#contact-dialog')
const closeBtn = this.querySelector('#dialog-close')
- const copyBtn = this.querySelector('#copy-btn')
- const tabBtns = this.querySelectorAll('.tab-btn')
-
- contactBtn?.addEventListener('click', () => {
- dialog?.showModal()
- })
-
- closeBtn?.addEventListener('click', () => {
- dialog?.close()
- })
+ contactBtn?.addEventListener('click', () => dialog?.showModal())
+ closeBtn?.addEventListener('click', () => dialog?.close())
dialog?.addEventListener('click', (e) => {
- if (e.target === dialog) {
- dialog.close()
- }
+ if (e.target === dialog) dialog.close()
})
-
- copyBtn?.addEventListener('click', async () => {
- const addr = this.querySelector('#monero-addr')?.textContent
- if (addr) {
- await navigator.clipboard.writeText(addr)
- copyBtn.classList.add('copied')
- setTimeout(() => copyBtn.classList.remove('copied'), 2000)
- }
- })
-
- tabBtns.forEach(btn => {
+
+ // Dialog tabs
+ this.querySelectorAll('.tab-btn').forEach(btn => {
btn.addEventListener('click', () => {
const tab = btn.dataset.tab
-
- tabBtns.forEach(b => b.classList.remove('active'))
+ 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
- const thumbnails = this.querySelectorAll('.thumbnail')
- const mainImg = this.querySelector('#main-img')
-
- thumbnails.forEach(thumb => {
+ 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)
- thumbnails.forEach(t => t.classList.remove('active'))
+ this.querySelectorAll('.thumbnail').forEach(t => t.classList.remove('active'))
thumb.classList.add('active')
- this.currentImageIndex = index
}
})
})
@@ -260,23 +393,18 @@ class PageListing extends HTMLElement {
}
formatPrice(price, currency = 'EUR') {
- if (price === null || price === undefined) return t('listing.priceOnRequest')
+ if (!price && price !== 0) return t('listing.priceOnRequest')
if (currency === 'XMR') {
return `${parseFloat(price).toFixed(4)} XMR`
}
- return new Intl.NumberFormat('de-DE', {
+ return new Intl.NumberFormat('de-CH', {
style: 'currency',
currency: currency
}).format(price)
}
- formatDescription(desc) {
- if (!desc) return ''
- return this.escapeHtml(desc).replace(/\n/g, '
')
- }
-
getConditionLabel(condition) {
const labels = {
new: t('create.conditionNew'),
@@ -288,6 +416,15 @@ class PageListing extends HTMLElement {
return labels[condition] || condition
}
+ formatDescription(text) {
+ if (!text) return ''
+ return text
+ .replace(/&/g, '&')
+ .replace(//g, '>')
+ .replace(/\n/g, '
')
+ }
+
escapeHtml(text) {
if (!text) return ''
const div = document.createElement('div')
@@ -301,39 +438,31 @@ customElements.define('page-listing', PageListing)
const style = document.createElement('style')
style.textContent = /* css */`
page-listing .listing-detail {
- display: grid;
- grid-template-columns: 1fr 400px;
- gap: var(--space-xl);
padding: var(--space-lg) 0;
}
-
- @media (max-width: 900px) {
- page-listing .listing-detail {
- grid-template-columns: 1fr;
- }
- }
-
+
+ /* Gallery - Full Width */
page-listing .listing-gallery {
background: var(--color-bg-secondary);
border-radius: var(--radius-lg);
overflow: hidden;
- align-self: start;
+ margin-bottom: var(--space-xl);
}
page-listing .listing-image-main {
- aspect-ratio: 4 / 3;
- background: var(--color-bg-tertiary);
+ 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;
- background: var(--color-bg-tertiary);
}
page-listing .listing-image-main .placeholder-icon {
@@ -343,36 +472,24 @@ style.textContent = /* css */`
}
page-listing .listing-thumbnails {
- display: grid;
- grid-template-columns: repeat(5, 1fr);
+ display: flex;
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;
- }
+ padding: var(--space-sm);
+ overflow-x: auto;
}
page-listing .thumbnail {
- aspect-ratio: 1;
+ flex-shrink: 0;
+ width: 80px;
+ height: 80px;
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);
}
@@ -382,46 +499,147 @@ style.textContent = /* css */`
height: 100%;
object-fit: cover;
}
-
- page-listing .listing-info header {
+
+ /* Content Row */
+ page-listing .listing-content {
+ display: grid;
+ grid-template-columns: 1fr 320px;
+ gap: var(--space-xl);
+ align-items: start;
+ }
+
+ @media (max-width: 900px) {
+ page-listing .listing-content {
+ grid-template-columns: 1fr;
+ }
+
+ page-listing .listing-sidebar {
+ position: static;
+ order: -1;
+ }
+ }
+
+ /* Main Info */
+ page-listing .listing-info {
+ min-width: 0;
+ }
+
+ page-listing .listing-header {
margin-bottom: var(--space-xl);
}
- page-listing .listing-info h1 {
+ 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-2xl);
+ font-size: var(--font-size-3xl);
font-weight: var(--font-weight-bold);
color: var(--color-primary);
+ margin: 0;
}
-
- page-listing .listing-location {
+
+ 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);
- margin-top: var(--space-sm);
+ 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-seller,
page-listing .listing-location-section {
margin-bottom: var(--space-xl);
}
page-listing .listing-description h2,
- page-listing .listing-seller h2,
page-listing .listing-location-section h2 {
font-size: var(--font-size-lg);
margin-bottom: var(--space-md);
}
-
- page-listing .seller-card {
+
+ 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);
+ }
+
+ 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);
+ }
+
+ page-listing .action-btn.copied {
+ color: var(--color-success);
+ }
+
+ /* Seller Card */
+ page-listing .seller-header {
display: flex;
align-items: center;
gap: var(--space-md);
- padding: var(--space-md);
- background: var(--color-bg-secondary);
- border-radius: var(--radius-md);
}
page-listing .seller-avatar {
@@ -446,15 +664,18 @@ style.textContent = /* css */`
font-size: var(--font-size-sm);
color: var(--color-text-muted);
}
-
- page-listing .listing-actions {
- margin-top: var(--space-xl);
+
+ /* 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 .listing-actions .btn {
- width: 100%;
+
+ page-listing .seller-listings h2 {
+ margin-bottom: var(--space-lg);
}
-
+
/* Dialog */
page-listing .contact-dialog {
background: var(--color-bg);
@@ -489,6 +710,9 @@ style.textContent = /* css */`
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 {
@@ -511,17 +735,15 @@ style.textContent = /* css */`
right: var(--space-md);
padding: var(--space-xs);
color: var(--color-text-muted);
- transition: color var(--transition-fast);
+ background: transparent;
+ border: none;
+ cursor: pointer;
}
page-listing .dialog-close:hover {
color: var(--color-text);
}
- page-listing .contact-dialog h2 {
- margin-bottom: var(--space-sm);
- }
-
page-listing .dialog-subtitle {
color: var(--color-text-secondary);
margin-bottom: var(--space-lg);
@@ -554,13 +776,10 @@ style.textContent = /* css */`
font-size: var(--font-size-xs);
word-break: break-all;
line-height: 1.4;
- color: var(--color-text);
}
page-listing .btn-copy {
padding: var(--space-sm);
- border-color: var(--color-border);
- color: var(--color-text);
flex-shrink: 0;
}
@@ -575,5 +794,22 @@ style.textContent = /* css */`
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)
diff --git a/locales/de.json b/locales/de.json
index 485b1bd..08d6438 100644
--- a/locales/de.json
+++ b/locales/de.json
@@ -115,7 +115,10 @@
"copyAddress": "Adresse kopieren",
"contactHint": "Kopiere die Adresse und sende den Betrag über dein Monero-Wallet.",
"priceOnRequest": "Preis auf Anfrage",
- "shippingAvailable": "Versand verfügbar"
+ "shippingAvailable": "Versand verfügbar",
+ "share": "Teilen",
+ "report": "Melden",
+ "moreFromSeller": "Weitere Anzeigen des Anbieters"
},
"chat": {
"title": "Nachricht senden",
diff --git a/locales/en.json b/locales/en.json
index 4b22a7e..a21ec97 100644
--- a/locales/en.json
+++ b/locales/en.json
@@ -115,7 +115,10 @@
"copyAddress": "Copy address",
"contactHint": "Copy the address and send the amount using your Monero wallet.",
"priceOnRequest": "Price on request",
- "shippingAvailable": "Shipping available"
+ "shippingAvailable": "Shipping available",
+ "share": "Share",
+ "report": "Report",
+ "moreFromSeller": "More from this seller"
},
"chat": {
"title": "Send Message",
diff --git a/locales/fr.json b/locales/fr.json
index 1385529..71b8565 100644
--- a/locales/fr.json
+++ b/locales/fr.json
@@ -115,7 +115,10 @@
"copyAddress": "Copier l'adresse",
"contactHint": "Copiez l'adresse et envoyez le montant via votre portefeuille Monero.",
"priceOnRequest": "Prix sur demande",
- "shippingAvailable": "Livraison disponible"
+ "shippingAvailable": "Livraison disponible",
+ "share": "Partager",
+ "report": "Signaler",
+ "moreFromSeller": "Autres annonces du vendeur"
},
"chat": {
"title": "Envoyer un message",