feat: add verifiable listings (proof of possession) with verification widget, badge on cards/detail, i18n (7 langs), fix edit prefill for location/monero, prevent edit/delete on pending listings

This commit is contained in:
2026-02-11 08:14:44 +01:00
parent f5cfab6a2a
commit 53673b4650
20 changed files with 754 additions and 34 deletions

View File

@@ -6,7 +6,7 @@ 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']
return ['listing-id', 'title', 'price', 'currency', 'location', 'image', 'owner-id', 'payment-status', 'status', 'priority', 'verified']
}
constructor() {
@@ -103,8 +103,9 @@ class ListingCard extends HTMLElement {
const paymentStatus = this.getAttribute('payment-status')
const status = this.getAttribute('status')
const isDeleted = status === 'deleted'
const isPending = status === 'draft' && paymentStatus !== 'paid'
const ownerBadge = (this.isOwner && !isDeleted) ? /* html */`
const ownerBadge = (this.isOwner && !isDeleted && !isPending) ? /* html */`
<a href="#/edit/${escapeHTML(id)}" class="owner-badge" title="${t('listing.edit')}">
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
<path d="M11 4H4a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7"></path>
@@ -141,13 +142,16 @@ class ListingCard extends HTMLElement {
${paymentBadge}
</div>
<div class="listing-info">
<h3 class="listing-title">${escapeHTML(title)}</h3>
<div class="listing-price-wrapper">
<p class="listing-price">${priceDisplay}</p>
${secondaryPrice ? `<p class="listing-price-secondary">${secondaryPrice}</p>` : ''}
</div>
<h3 class="listing-title">${escapeHTML(title)}</h3>
<div class="listing-price-wrapper">
<p class="listing-price">${priceDisplay}</p>
${secondaryPrice ? `<p class="listing-price-secondary">${secondaryPrice}</p>` : ''}
</div>
<div class="listing-meta-row">
${this.getAttribute('verified') === 'true' ? `<span class="listing-verified-badge">${t('verification.badge')}</span>` : ''}
<p class="listing-location">${escapeHTML(location)}</p>
</div>
</div>
</${linkTag}>
${!isDeleted ? /* html */`
<button
@@ -331,6 +335,19 @@ style.textContent = /* css */`
text-overflow: ellipsis;
}
listing-card .listing-meta-row {
display: flex;
align-items: center;
gap: var(--space-xs);
}
listing-card .listing-verified-badge {
font-size: var(--font-size-xs);
font-weight: var(--font-weight-medium);
color: var(--color-success);
white-space: nowrap;
}
listing-card .listing-location {
font-size: var(--font-size-xs);
color: var(--color-text-muted);