add image placeholder; add dialog in listing
This commit is contained in:
@@ -58,9 +58,19 @@ class ListingCard extends HTMLElement {
|
|||||||
const priceDisplay = price ? formatPrice(parseFloat(price), currency) : '–';
|
const priceDisplay = price ? formatPrice(parseFloat(price), currency) : '–';
|
||||||
const favoriteLabel = this.isFavorite ? t('home.removeFavorite') : t('home.addFavorite');
|
const favoriteLabel = this.isFavorite ? t('home.removeFavorite') : t('home.addFavorite');
|
||||||
|
|
||||||
|
const placeholderSvg = /* 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>
|
||||||
|
`;
|
||||||
|
|
||||||
this.innerHTML = /* html */`
|
this.innerHTML = /* html */`
|
||||||
<a href="#/listing/${escapeHTML(id)}" class="listing-link">
|
<a href="#/listing/${escapeHTML(id)}" class="listing-link">
|
||||||
<div class="listing-image" ${image ? `style="background-image: url('${escapeHTML(image)}')"` : ''}></div>
|
<div class="listing-image" ${image ? `style="background-image: url('${escapeHTML(image)}')"` : ''}>
|
||||||
|
${!image ? placeholderSvg : ''}
|
||||||
|
</div>
|
||||||
<div class="listing-info">
|
<div class="listing-info">
|
||||||
<h3 class="listing-title">${escapeHTML(title)}</h3>
|
<h3 class="listing-title">${escapeHTML(title)}</h3>
|
||||||
<p class="listing-price">${priceDisplay}</p>
|
<p class="listing-price">${priceDisplay}</p>
|
||||||
@@ -138,6 +148,15 @@ style.textContent = /* css */`
|
|||||||
background: var(--color-bg-tertiary);
|
background: var(--color-bg-tertiary);
|
||||||
background-size: cover;
|
background-size: cover;
|
||||||
background-position: center;
|
background-position: center;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
listing-card .listing-image .placeholder-icon {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
color: var(--color-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
listing-card .listing-info {
|
listing-card .listing-info {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { t, i18n } from '../../i18n.js';
|
import { t, i18n } from '../../i18n.js';
|
||||||
|
import { mockListings } from '../../data/mock-listings.js';
|
||||||
import '../listing-card.js';
|
import '../listing-card.js';
|
||||||
import '../search-box.js';
|
import '../search-box.js';
|
||||||
|
|
||||||
@@ -21,20 +22,19 @@ class PageHome extends HTMLElement {
|
|||||||
<section class="recent-listings">
|
<section class="recent-listings">
|
||||||
<h2>${t('home.recentListings')}</h2>
|
<h2>${t('home.recentListings')}</h2>
|
||||||
<div class="listings-grid">
|
<div class="listings-grid">
|
||||||
${this.renderPlaceholderListings()}
|
${this.renderListings()}
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
renderPlaceholderListings() {
|
renderListings() {
|
||||||
const placeholders = Array(10).fill(null);
|
return mockListings.map(listing => /* html */`
|
||||||
return placeholders.map((_, i) => /* html */`
|
|
||||||
<listing-card
|
<listing-card
|
||||||
listing-id="${i + 1}"
|
listing-id="${listing.id}"
|
||||||
title="${t('home.placeholderTitle')}"
|
title="${listing.title}"
|
||||||
price="699"
|
price="${listing.price}"
|
||||||
location="${t('home.placeholderLocation')}"
|
location="${listing.location}"
|
||||||
></listing-card>
|
></listing-card>
|
||||||
`).join('');
|
`).join('');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import { t, i18n } from '../../i18n.js';
|
import { t, i18n } from '../../i18n.js';
|
||||||
|
import { getListingById } from '../../data/mock-listings.js';
|
||||||
|
|
||||||
class PageListing extends HTMLElement {
|
class PageListing extends HTMLElement {
|
||||||
constructor() {
|
constructor() {
|
||||||
@@ -21,20 +22,7 @@ class PageListing extends HTMLElement {
|
|||||||
async loadListing() {
|
async loadListing() {
|
||||||
await new Promise(resolve => setTimeout(resolve, 300));
|
await new Promise(resolve => setTimeout(resolve, 300));
|
||||||
|
|
||||||
this.listing = {
|
this.listing = getListingById(this.listingId);
|
||||||
id: this.listingId,
|
|
||||||
title: 'iPhone 13 Pro - Sehr guter Zustand',
|
|
||||||
description: 'Verkaufe mein iPhone 13 Pro in sehr gutem Zustand. Das Gerät hat keine Kratzer und funktioniert einwandfrei. Originalverpackung und Ladekabel sind dabei.',
|
|
||||||
price: 699,
|
|
||||||
location: 'Berlin, Mitte',
|
|
||||||
category: 'electronics',
|
|
||||||
createdAt: new Date().toISOString(),
|
|
||||||
seller: {
|
|
||||||
name: 'Max M.',
|
|
||||||
memberSince: '2023'
|
|
||||||
},
|
|
||||||
images: []
|
|
||||||
};
|
|
||||||
|
|
||||||
this.loading = false;
|
this.loading = false;
|
||||||
this.render();
|
this.render();
|
||||||
@@ -61,10 +49,21 @@ class PageListing extends HTMLElement {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hasImages = this.listing.images && this.listing.images.length > 0;
|
||||||
|
const placeholderSvg = /* 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>
|
||||||
|
`;
|
||||||
|
|
||||||
this.innerHTML = /* html */`
|
this.innerHTML = /* html */`
|
||||||
<article class="listing-detail">
|
<article class="listing-detail">
|
||||||
<div class="listing-gallery">
|
<div class="listing-gallery">
|
||||||
<div class="listing-image-main"></div>
|
<div class="listing-image-main">
|
||||||
|
${!hasImages ? placeholderSvg : ''}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="listing-info">
|
<div class="listing-info">
|
||||||
@@ -92,13 +91,72 @@ class PageListing extends HTMLElement {
|
|||||||
</section>
|
</section>
|
||||||
|
|
||||||
<div class="listing-actions">
|
<div class="listing-actions">
|
||||||
<button class="btn btn-primary btn-lg">
|
<button class="btn btn-primary btn-lg" id="contact-btn">
|
||||||
${t('listing.contactSeller')}
|
${t('listing.contactSeller')}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</article>
|
</article>
|
||||||
|
|
||||||
|
<!-- Contact Dialog -->
|
||||||
|
<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>
|
||||||
|
<h2>${t('listing.contactSeller')}</h2>
|
||||||
|
<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.seller.moneroAddress || '888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H'}</code>
|
||||||
|
<button class="btn btn-outline btn-copy" id="copy-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>
|
||||||
|
</dialog>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
this.setupEventListeners();
|
||||||
|
}
|
||||||
|
|
||||||
|
setupEventListeners() {
|
||||||
|
const contactBtn = this.querySelector('#contact-btn');
|
||||||
|
const dialog = this.querySelector('#contact-dialog');
|
||||||
|
const closeBtn = this.querySelector('#dialog-close');
|
||||||
|
const copyBtn = this.querySelector('#copy-btn');
|
||||||
|
|
||||||
|
contactBtn?.addEventListener('click', () => {
|
||||||
|
dialog?.showModal();
|
||||||
|
});
|
||||||
|
|
||||||
|
closeBtn?.addEventListener('click', () => {
|
||||||
|
dialog?.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
dialog?.addEventListener('click', (e) => {
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
escapeHtml(text) {
|
escapeHtml(text) {
|
||||||
@@ -134,6 +192,15 @@ style.textContent = /* css */`
|
|||||||
page-listing .listing-image-main {
|
page-listing .listing-image-main {
|
||||||
aspect-ratio: 4 / 3;
|
aspect-ratio: 4 / 3;
|
||||||
background: var(--color-bg-tertiary);
|
background: var(--color-bg-tertiary);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
page-listing .listing-image-main .placeholder-icon {
|
||||||
|
width: 80px;
|
||||||
|
height: 80px;
|
||||||
|
color: var(--color-border);
|
||||||
}
|
}
|
||||||
|
|
||||||
page-listing .listing-info header {
|
page-listing .listing-info header {
|
||||||
@@ -205,5 +272,92 @@ style.textContent = /* css */`
|
|||||||
page-listing .listing-actions .btn {
|
page-listing .listing-actions .btn {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 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-close {
|
||||||
|
position: absolute;
|
||||||
|
top: var(--space-md);
|
||||||
|
right: var(--space-md);
|
||||||
|
padding: var(--space-xs);
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
transition: color var(--transition-fast);
|
||||||
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
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-secondary);
|
||||||
|
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;
|
||||||
|
}
|
||||||
`;
|
`;
|
||||||
document.head.appendChild(style);
|
document.head.appendChild(style);
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { t, i18n } from '../../i18n.js';
|
import { t, i18n } from '../../i18n.js';
|
||||||
import { router } from '../../router.js';
|
import { router } from '../../router.js';
|
||||||
|
import { searchListings } from '../../data/mock-listings.js';
|
||||||
import '../search-box.js';
|
import '../search-box.js';
|
||||||
import '../listing-card.js';
|
import '../listing-card.js';
|
||||||
|
|
||||||
@@ -101,14 +102,7 @@ class PageSearch extends HTMLElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getMockResults() {
|
getMockResults() {
|
||||||
return [
|
return searchListings(this.query, this.category, this.subcategory);
|
||||||
{ id: 1, title: 'iPhone 13 Pro', price: 699, location: 'Berlin' },
|
|
||||||
{ id: 2, title: 'Vintage Sofa', price: 250, location: 'München' },
|
|
||||||
{ id: 3, title: 'Mountain Bike', price: 450, location: 'Hamburg' },
|
|
||||||
{ id: 4, title: 'Gaming PC', price: 1200, location: 'Köln' },
|
|
||||||
{ id: 5, title: 'Schreibtisch', price: 80, location: 'Zürich' },
|
|
||||||
{ id: 6, title: 'Winterjacke', price: 45, location: 'Wien' },
|
|
||||||
];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
updateResults() {
|
updateResults() {
|
||||||
|
|||||||
167
js/data/mock-listings.js
Normal file
167
js/data/mock-listings.js
Normal file
@@ -0,0 +1,167 @@
|
|||||||
|
export const mockListings = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
title: 'iPhone 13 Pro - Sehr guter Zustand',
|
||||||
|
description: 'Verkaufe mein iPhone 13 Pro in sehr gutem Zustand. Das Gerät hat keine Kratzer und funktioniert einwandfrei. Originalverpackung und Ladekabel sind dabei.',
|
||||||
|
price: 699,
|
||||||
|
location: 'Berlin, Mitte',
|
||||||
|
category: 'electronics',
|
||||||
|
subcategory: 'phones',
|
||||||
|
createdAt: '2026-01-27T10:00:00Z',
|
||||||
|
seller: {
|
||||||
|
name: 'Max M.',
|
||||||
|
memberSince: '2023',
|
||||||
|
moneroAddress: '888tNkZrPN6JsEgekjMnABU4TBzc2Dt29EPAvkRxbANsAnjyPbb3iQ1YBRk1UXcdRsiKc9dhwMVgN5S9cQUiyoogDavup3H'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
title: 'Vintage Ledersofa 3-Sitzer',
|
||||||
|
description: 'Wunderschönes Vintage-Ledersofa aus den 70er Jahren. Cognacfarben, leichte Patina die dem Stück Charakter verleiht. Sehr bequem.',
|
||||||
|
price: 450,
|
||||||
|
location: 'München, Schwabing',
|
||||||
|
category: 'furniture',
|
||||||
|
subcategory: 'living',
|
||||||
|
createdAt: '2026-01-26T14:30:00Z',
|
||||||
|
seller: {
|
||||||
|
name: 'Anna K.',
|
||||||
|
memberSince: '2024',
|
||||||
|
moneroAddress: '47sghzufGhJJDQEbScMCwVBimTuq6L5JiRixD8VeGbpjCTA12GwZVPWzjmpfLDJNDAWvuNDAWvuNDAWvuNDAWvuN'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
title: 'Canyon Mountainbike 29 Zoll',
|
||||||
|
description: 'Canyon Spectral AL 6.0 in Größe L. Carbon-Rahmen, Shimano XT Schaltung, frisch gewartet. Ideal für Trails und Touren.',
|
||||||
|
price: 1200,
|
||||||
|
location: 'Zürich',
|
||||||
|
category: 'sports',
|
||||||
|
subcategory: 'outdoor',
|
||||||
|
createdAt: '2026-01-25T09:15:00Z',
|
||||||
|
seller: {
|
||||||
|
name: 'Thomas B.',
|
||||||
|
memberSince: '2022',
|
||||||
|
moneroAddress: '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '4',
|
||||||
|
title: 'Gaming PC - RTX 4070, Ryzen 7',
|
||||||
|
description: 'Selbstgebauter Gaming-PC: Ryzen 7 5800X, RTX 4070, 32GB RAM, 1TB NVMe SSD. Perfekt für 1440p Gaming. RGB-Beleuchtung.',
|
||||||
|
price: 1450,
|
||||||
|
location: 'Hamburg',
|
||||||
|
category: 'electronics',
|
||||||
|
subcategory: 'gaming',
|
||||||
|
createdAt: '2026-01-24T16:45:00Z',
|
||||||
|
seller: {
|
||||||
|
name: 'Felix R.',
|
||||||
|
memberSince: '2023',
|
||||||
|
moneroAddress: '48iWMy1PH6VGBJVvHDg9mY7mJ6vBDWVHpGgXEtCGp99kT4Xk5QfN3v7nqMrqGpvU'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '5',
|
||||||
|
title: 'Ikea MALM Schreibtisch weiß',
|
||||||
|
description: 'Ikea MALM Schreibtisch in weiß, 140x65cm. Minimale Gebrauchsspuren, Kabelmanagement integriert. Selbstabholung.',
|
||||||
|
price: 80,
|
||||||
|
location: 'Wien, 1050',
|
||||||
|
category: 'furniture',
|
||||||
|
subcategory: 'office',
|
||||||
|
createdAt: '2026-01-23T11:20:00Z',
|
||||||
|
seller: {
|
||||||
|
name: 'Lisa S.',
|
||||||
|
memberSince: '2025',
|
||||||
|
moneroAddress: '45dEQp8dFKrMXKvFWJFmZKCZhH3ARYMW4MJYM9FJcPuNT5Kek9R'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '6',
|
||||||
|
title: 'Canada Goose Winterjacke M',
|
||||||
|
description: 'Original Canada Goose Expedition Parka in Schwarz, Größe M. Sehr warm, perfekt für extreme Kälte. NP 1200€.',
|
||||||
|
price: 550,
|
||||||
|
location: 'Köln',
|
||||||
|
category: 'clothing',
|
||||||
|
subcategory: 'men',
|
||||||
|
createdAt: '2026-01-22T08:00:00Z',
|
||||||
|
seller: {
|
||||||
|
name: 'Jan P.',
|
||||||
|
memberSince: '2024',
|
||||||
|
moneroAddress: '42nTNQp8dFKrMXKvFWJFmZKCZhH3ARYMW4MJYM9FJcPuNT5Kek9R'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '7',
|
||||||
|
title: 'Sony A7 III Kamera + 24-70mm',
|
||||||
|
description: 'Sony A7 III Vollformat-Kamera mit Sony 24-70mm f/2.8 GM Objektiv. 15.000 Auslösungen, einwandfreier Zustand.',
|
||||||
|
price: 2200,
|
||||||
|
location: 'Frankfurt',
|
||||||
|
category: 'electronics',
|
||||||
|
subcategory: 'tv_audio',
|
||||||
|
createdAt: '2026-01-21T13:10:00Z',
|
||||||
|
seller: {
|
||||||
|
name: 'Sarah M.',
|
||||||
|
memberSince: '2021',
|
||||||
|
moneroAddress: '46BeWrHpwXmHDpDEUmZBWZfoQpdc6HaERCNmx1pEYL2rAcuwufPN9rXHHtyUA4QVy24G3euoGWpzR7T3'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '8',
|
||||||
|
title: 'Elektrische Gitarre Fender Strat',
|
||||||
|
description: 'Fender Stratocaster Player Series in Sunburst. Ahorn-Hals, 3 Single-Coil Pickups. Inkl. Gigbag.',
|
||||||
|
price: 650,
|
||||||
|
location: 'Stuttgart',
|
||||||
|
category: 'other',
|
||||||
|
subcategory: 'art',
|
||||||
|
createdAt: '2026-01-20T17:30:00Z',
|
||||||
|
seller: {
|
||||||
|
name: 'Michael W.',
|
||||||
|
memberSince: '2022',
|
||||||
|
moneroAddress: '43gFrHpwXmHDpDEUmZBWZfoQpdc6HaERCNmx1pEYL2rAcuwufPN9rXHHtyUA4QVy24G3euoGWpzR7T3'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '9',
|
||||||
|
title: 'Weber Gasgrill Genesis II',
|
||||||
|
description: 'Weber Genesis II E-310 Gasgrill in Schwarz. 3 Brenner, Sear Station, iGrill kompatibel. Wenig benutzt.',
|
||||||
|
price: 480,
|
||||||
|
location: 'Düsseldorf',
|
||||||
|
category: 'garden',
|
||||||
|
subcategory: 'outdoor_living',
|
||||||
|
createdAt: '2026-01-19T10:45:00Z',
|
||||||
|
seller: {
|
||||||
|
name: 'Klaus H.',
|
||||||
|
memberSince: '2023',
|
||||||
|
moneroAddress: '47sghzufGhJJDQEbScMCwVBimTuq6L5JiRixD8VeGbpjCTA12GwZVPWzjmpfLDJNDAWvuNDAWvuNDAWvuNDAWvuN'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '10',
|
||||||
|
title: 'Adidas Ultra Boost 22 Gr. 43',
|
||||||
|
description: 'Adidas Ultra Boost 22 Laufschuhe in Core Black, Größe 43. Nur wenige Male getragen, wie neu.',
|
||||||
|
price: 95,
|
||||||
|
location: 'Bern',
|
||||||
|
category: 'clothing',
|
||||||
|
subcategory: 'shoes',
|
||||||
|
createdAt: '2026-01-18T15:20:00Z',
|
||||||
|
seller: {
|
||||||
|
name: 'Nina L.',
|
||||||
|
memberSince: '2024',
|
||||||
|
moneroAddress: '44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
export function getListingById(id) {
|
||||||
|
return mockListings.find(l => l.id === id) || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function searchListings(query = '', category = '', subcategory = '') {
|
||||||
|
return mockListings.filter(listing => {
|
||||||
|
const matchesQuery = !query ||
|
||||||
|
listing.title.toLowerCase().includes(query.toLowerCase()) ||
|
||||||
|
listing.description.toLowerCase().includes(query.toLowerCase());
|
||||||
|
const matchesCategory = !category || listing.category === category;
|
||||||
|
const matchesSubcategory = !subcategory || listing.subcategory === subcategory;
|
||||||
|
return matchesQuery && matchesCategory && matchesSubcategory;
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -99,7 +99,11 @@
|
|||||||
"description": "Beschreibung",
|
"description": "Beschreibung",
|
||||||
"seller": "Anbieter",
|
"seller": "Anbieter",
|
||||||
"memberSince": "Mitglied seit",
|
"memberSince": "Mitglied seit",
|
||||||
"contactSeller": "Anbieter kontaktieren"
|
"contactSeller": "Anbieter kontaktieren",
|
||||||
|
"paymentInfo": "Bezahlung erfolgt direkt über Monero (XMR).",
|
||||||
|
"moneroAddress": "Monero-Adresse des Anbieters",
|
||||||
|
"copyAddress": "Adresse kopieren",
|
||||||
|
"contactHint": "Kopiere die Adresse und sende den Betrag über dein Monero-Wallet."
|
||||||
},
|
},
|
||||||
"create": {
|
"create": {
|
||||||
"title": "Anzeige erstellen",
|
"title": "Anzeige erstellen",
|
||||||
|
|||||||
@@ -99,7 +99,11 @@
|
|||||||
"description": "Description",
|
"description": "Description",
|
||||||
"seller": "Seller",
|
"seller": "Seller",
|
||||||
"memberSince": "Member since",
|
"memberSince": "Member since",
|
||||||
"contactSeller": "Contact Seller"
|
"contactSeller": "Contact Seller",
|
||||||
|
"paymentInfo": "Payment is made directly via Monero (XMR).",
|
||||||
|
"moneroAddress": "Seller's Monero Address",
|
||||||
|
"copyAddress": "Copy address",
|
||||||
|
"contactHint": "Copy the address and send the amount using your Monero wallet."
|
||||||
},
|
},
|
||||||
"create": {
|
"create": {
|
||||||
"title": "Create Listing",
|
"title": "Create Listing",
|
||||||
|
|||||||
@@ -99,7 +99,11 @@
|
|||||||
"description": "Description",
|
"description": "Description",
|
||||||
"seller": "Vendeur",
|
"seller": "Vendeur",
|
||||||
"memberSince": "Membre depuis",
|
"memberSince": "Membre depuis",
|
||||||
"contactSeller": "Contacter le vendeur"
|
"contactSeller": "Contacter le vendeur",
|
||||||
|
"paymentInfo": "Le paiement s'effectue directement via Monero (XMR).",
|
||||||
|
"moneroAddress": "Adresse Monero du vendeur",
|
||||||
|
"copyAddress": "Copier l'adresse",
|
||||||
|
"contactHint": "Copiez l'adresse et envoyez le montant via votre portefeuille Monero."
|
||||||
},
|
},
|
||||||
"create": {
|
"create": {
|
||||||
"title": "Créer une annonce",
|
"title": "Créer une annonce",
|
||||||
|
|||||||
Reference in New Issue
Block a user