245 lines
7.2 KiB
JavaScript
245 lines
7.2 KiB
JavaScript
import { t, i18n } from '../../i18n.js'
|
|
import { router } from '../../router.js'
|
|
import { listingsService } from '../../services/listings.js'
|
|
import { directus } from '../../services/directus.js'
|
|
import '../search-box.js'
|
|
import '../listing-card.js'
|
|
|
|
class PageSearch extends HTMLElement {
|
|
constructor() {
|
|
super()
|
|
this.results = []
|
|
this.loading = false
|
|
this.error = null
|
|
}
|
|
|
|
connectedCallback() {
|
|
this.parseUrlParams()
|
|
this.render()
|
|
this.afterRender()
|
|
this.unsubscribe = i18n.subscribe(() => {
|
|
this.render()
|
|
this.afterRender()
|
|
})
|
|
|
|
if (this.hasFilters()) {
|
|
this.performSearch()
|
|
}
|
|
}
|
|
|
|
disconnectedCallback() {
|
|
if (this.unsubscribe) this.unsubscribe()
|
|
}
|
|
|
|
parseUrlParams() {
|
|
const params = new URLSearchParams(window.location.hash.split('?')[1] || '')
|
|
this.query = params.get('q') || ''
|
|
this.category = params.get('category') || ''
|
|
this.subcategory = params.get('sub') || ''
|
|
this.country = params.get('country') || 'ch'
|
|
this.useCurrentLocation = params.get('location') === 'current'
|
|
this.radius = parseInt(params.get('radius')) || 50
|
|
this.lat = params.get('lat') ? parseFloat(params.get('lat')) : null
|
|
this.lng = params.get('lng') ? parseFloat(params.get('lng')) : null
|
|
}
|
|
|
|
hasFilters() {
|
|
return this.query || this.category || this.subcategory
|
|
}
|
|
|
|
afterRender() {
|
|
const searchBox = this.querySelector('search-box')
|
|
if (searchBox) {
|
|
searchBox.setFilters({
|
|
query: this.query,
|
|
category: this.category,
|
|
subcategory: this.subcategory,
|
|
country: this.country,
|
|
useCurrentLocation: this.useCurrentLocation,
|
|
radius: this.radius
|
|
})
|
|
|
|
searchBox.addEventListener('search', (e) => {
|
|
this.handleSearch(e.detail)
|
|
})
|
|
}
|
|
}
|
|
|
|
handleSearch(filters) {
|
|
this.query = filters.query
|
|
this.category = filters.category
|
|
this.subcategory = filters.subcategory
|
|
this.country = filters.country
|
|
this.useCurrentLocation = filters.useCurrentLocation
|
|
this.radius = filters.radius
|
|
this.lat = filters.lat
|
|
this.lng = filters.lng
|
|
|
|
this.performSearch()
|
|
}
|
|
|
|
render() {
|
|
this.innerHTML = /* html */`
|
|
<div class="search-page">
|
|
<section class="search-header">
|
|
<search-box no-navigate></search-box>
|
|
</section>
|
|
|
|
<section class="search-results" id="results">
|
|
${this.renderResults()}
|
|
</section>
|
|
</div>
|
|
`
|
|
}
|
|
|
|
async performSearch() {
|
|
this.loading = true
|
|
this.error = null
|
|
this.updateResults()
|
|
|
|
try {
|
|
const filters = {
|
|
search: this.query || undefined,
|
|
category: this.category || undefined,
|
|
limit: 50
|
|
}
|
|
|
|
const response = await listingsService.getListingsWithFilters(filters)
|
|
this.results = response.items || []
|
|
this.loading = false
|
|
this.updateResults()
|
|
} catch (err) {
|
|
console.error('Search failed:', err)
|
|
this.error = err.message
|
|
this.loading = false
|
|
this.updateResults()
|
|
}
|
|
}
|
|
|
|
updateResults() {
|
|
const resultsContainer = this.querySelector('#results')
|
|
if (resultsContainer) {
|
|
resultsContainer.innerHTML = this.renderResults()
|
|
}
|
|
}
|
|
|
|
renderResults() {
|
|
if (this.loading) {
|
|
return /* html */`
|
|
<div class="loading">
|
|
<div class="spinner"></div>
|
|
<p>${t('search.loading')}</p>
|
|
</div>
|
|
`
|
|
}
|
|
|
|
if (this.error) {
|
|
return /* html */`
|
|
<div class="empty-state">
|
|
<div class="empty-state-icon">⚠️</div>
|
|
<p>${t('common.error')}</p>
|
|
</div>
|
|
`
|
|
}
|
|
|
|
if (!this.hasFilters() && this.results.length === 0) {
|
|
return /* html */`
|
|
<div class="empty-state">
|
|
<div class="empty-state-icon">🔍</div>
|
|
<p>${t('search.enterQuery')}</p>
|
|
</div>
|
|
`
|
|
}
|
|
|
|
if (this.results.length === 0) {
|
|
return /* html */`
|
|
<div class="empty-state">
|
|
<div class="empty-state-icon">😕</div>
|
|
<p>${t('search.noResults')}</p>
|
|
</div>
|
|
`
|
|
}
|
|
|
|
return /* html */`
|
|
<p class="results-count">${t('search.resultsCount', { count: this.results.length })}</p>
|
|
<div class="listings-grid">
|
|
${this.results.map(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 || listing.location || ''
|
|
|
|
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>
|
|
`
|
|
}).join('')}
|
|
</div>
|
|
`
|
|
}
|
|
|
|
escapeHtml(text) {
|
|
const div = document.createElement('div')
|
|
div.textContent = text
|
|
return div.innerHTML
|
|
}
|
|
}
|
|
|
|
customElements.define('page-search', PageSearch)
|
|
|
|
const style = document.createElement('style')
|
|
style.textContent = /* css */`
|
|
page-search .search-page {
|
|
padding: var(--space-lg) 0;
|
|
}
|
|
|
|
page-search .search-header {
|
|
margin-bottom: var(--space-xl);
|
|
}
|
|
|
|
page-search .results-count {
|
|
color: var(--color-text-muted);
|
|
margin-bottom: var(--space-md);
|
|
}
|
|
|
|
page-search .loading {
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: var(--space-2xl);
|
|
color: var(--color-text-muted);
|
|
}
|
|
|
|
page-search .spinner {
|
|
width: 40px;
|
|
height: 40px;
|
|
border: 3px solid var(--color-border);
|
|
border-top-color: var(--color-primary);
|
|
border-radius: 50%;
|
|
animation: spin 1s linear infinite;
|
|
margin-bottom: var(--space-md);
|
|
}
|
|
|
|
@keyframes spin {
|
|
to { transform: rotate(360deg); }
|
|
}
|
|
|
|
page-search .empty-state {
|
|
text-align: center;
|
|
padding: var(--space-2xl);
|
|
color: var(--color-text-muted);
|
|
}
|
|
|
|
page-search .empty-state-icon {
|
|
font-size: 3rem;
|
|
margin-bottom: var(--space-md);
|
|
}
|
|
`
|
|
document.head.appendChild(style)
|