929 lines
31 KiB
JavaScript
929 lines
31 KiB
JavaScript
/**
|
|
* Kasico Advanced Search Widget - Elasticsearch Integration
|
|
* Fuzzy Search, Faceted Search, Auto-Complete mit Furry-Design
|
|
*/
|
|
|
|
class KasicoAdvancedSearch {
|
|
constructor() {
|
|
this.currentQuery = '';
|
|
this.filters = {};
|
|
this.suggestions = [];
|
|
this.isSearching = false;
|
|
this.searchTimeout = null;
|
|
|
|
this.init();
|
|
}
|
|
|
|
init() {
|
|
// Search Widget HTML erstellen
|
|
this.createWidget();
|
|
|
|
// Event Listeners
|
|
this.bindEvents();
|
|
|
|
// Initialize search
|
|
this.initializeSearch();
|
|
}
|
|
|
|
createWidget() {
|
|
const widgetHTML = `
|
|
<div id="kasico-advanced-search" class="advanced-search-widget">
|
|
<!-- Search Header -->
|
|
<div class="search-header">
|
|
<div class="search-title">
|
|
<h2><i class="fas fa-search"></i> Erweiterte Suche</h2>
|
|
<p>Finde dein perfektes Fursuit mit unserer intelligenten Suche</p>
|
|
</div>
|
|
<div class="search-stats" id="search-stats">
|
|
<span class="results-count">0 Ergebnisse</span>
|
|
<span class="search-time">0ms</span>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Search Input -->
|
|
<div class="search-input-section">
|
|
<div class="search-input-wrapper">
|
|
<div class="search-input-group">
|
|
<i class="fas fa-search search-icon"></i>
|
|
<input type="text"
|
|
id="search-input"
|
|
class="search-input"
|
|
placeholder="Suche nach Fursuits, Auktionen, Kategorien..."
|
|
autocomplete="off">
|
|
<button class="search-clear" id="search-clear" style="display: none;">
|
|
<i class="fas fa-times"></i>
|
|
</button>
|
|
</div>
|
|
<button class="search-button" id="search-button">
|
|
<i class="fas fa-search"></i>
|
|
Suchen
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Search Suggestions -->
|
|
<div class="search-suggestions" id="search-suggestions" style="display: none;">
|
|
<div class="suggestions-header">
|
|
<i class="fas fa-lightbulb"></i>
|
|
<span>Vorschläge</span>
|
|
</div>
|
|
<div class="suggestions-list" id="suggestions-list">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Search Filters -->
|
|
<div class="search-filters">
|
|
<div class="filter-section">
|
|
<h4><i class="fas fa-filter"></i> Filter</h4>
|
|
|
|
<div class="filter-group">
|
|
<label>Kategorie</label>
|
|
<select id="category-filter" class="filter-select">
|
|
<option value="">Alle Kategorien</option>
|
|
<option value="fullsuit">Fullsuit</option>
|
|
<option value="partial">Partial</option>
|
|
<option value="head">Head Only</option>
|
|
<option value="handpaws">Handpaws</option>
|
|
<option value="feetpaws">Feetpaws</option>
|
|
<option value="tail">Tail</option>
|
|
<option value="custom">Custom</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="filter-group">
|
|
<label>Fursuit Typ</label>
|
|
<select id="fursuit-type-filter" class="filter-select">
|
|
<option value="">Alle Typen</option>
|
|
<option value="fullsuit">Fullsuit</option>
|
|
<option value="partial">Partial</option>
|
|
<option value="head">Head Only</option>
|
|
</select>
|
|
</div>
|
|
|
|
<div class="filter-group">
|
|
<label>Preisbereich</label>
|
|
<div class="price-range">
|
|
<input type="number"
|
|
id="price-min"
|
|
class="price-input"
|
|
placeholder="Min">
|
|
<span>-</span>
|
|
<input type="number"
|
|
id="price-max"
|
|
class="price-input"
|
|
placeholder="Max">
|
|
</div>
|
|
</div>
|
|
|
|
<div class="filter-group">
|
|
<label class="checkbox-label">
|
|
<input type="checkbox" id="featured-filter">
|
|
<span class="checkmark"></span>
|
|
Nur Featured Produkte
|
|
</label>
|
|
</div>
|
|
|
|
<div class="filter-group">
|
|
<label>Sortierung</label>
|
|
<select id="sort-filter" class="filter-select">
|
|
<option value="-created_at">Neueste zuerst</option>
|
|
<option value="price">Preis: Niedrig zu Hoch</option>
|
|
<option value="-price">Preis: Hoch zu Niedrig</option>
|
|
<option value="title">Name: A-Z</option>
|
|
<option value="-title">Name: Z-A</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="filter-actions">
|
|
<button class="filter-clear" id="filter-clear">
|
|
<i class="fas fa-undo"></i>
|
|
Filter zurücksetzen
|
|
</button>
|
|
<button class="filter-apply" id="filter-apply">
|
|
<i class="fas fa-check"></i>
|
|
Filter anwenden
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Search Results -->
|
|
<div class="search-results" id="search-results">
|
|
<div class="results-header">
|
|
<div class="results-info">
|
|
<span class="results-count">0 Ergebnisse gefunden</span>
|
|
<span class="search-query" id="search-query"></span>
|
|
</div>
|
|
<div class="results-view">
|
|
<button class="view-toggle active" data-view="grid">
|
|
<i class="fas fa-th"></i>
|
|
</button>
|
|
<button class="view-toggle" data-view="list">
|
|
<i class="fas fa-list"></i>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="results-container" id="results-container">
|
|
<div class="empty-state">
|
|
<i class="fas fa-search"></i>
|
|
<h3>Starte deine Suche</h3>
|
|
<p>Gib einen Suchbegriff ein oder verwende die Filter</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="results-pagination" id="results-pagination" style="display: none;">
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Search Facets -->
|
|
<div class="search-facets" id="search-facets" style="display: none;">
|
|
<div class="facets-header">
|
|
<h4><i class="fas fa-tags"></i> Kategorien</h4>
|
|
</div>
|
|
<div class="facets-content" id="facets-content">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
// Widget in die Seite einfügen
|
|
const container = document.getElementById('search-container') || document.body;
|
|
container.insertAdjacentHTML('beforeend', widgetHTML);
|
|
|
|
// CSS hinzufügen
|
|
this.addStyles();
|
|
}
|
|
|
|
addStyles() {
|
|
const styles = `
|
|
.advanced-search-widget {
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
border-radius: 20px;
|
|
padding: 30px;
|
|
color: white;
|
|
font-family: 'Quicksand', sans-serif;
|
|
box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1);
|
|
margin: 20px 0;
|
|
}
|
|
|
|
.search-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 25px;
|
|
padding-bottom: 20px;
|
|
border-bottom: 2px solid rgba(255, 255, 255, 0.2);
|
|
}
|
|
|
|
.search-title h2 {
|
|
margin: 0 0 5px 0;
|
|
font-size: 28px;
|
|
font-weight: 700;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
}
|
|
|
|
.search-title p {
|
|
margin: 0;
|
|
opacity: 0.9;
|
|
font-size: 16px;
|
|
}
|
|
|
|
.search-stats {
|
|
display: flex;
|
|
gap: 20px;
|
|
font-size: 14px;
|
|
opacity: 0.8;
|
|
}
|
|
|
|
.search-input-section {
|
|
margin-bottom: 25px;
|
|
position: relative;
|
|
}
|
|
|
|
.search-input-wrapper {
|
|
display: flex;
|
|
gap: 15px;
|
|
align-items: center;
|
|
}
|
|
|
|
.search-input-group {
|
|
position: relative;
|
|
flex: 1;
|
|
}
|
|
|
|
.search-icon {
|
|
position: absolute;
|
|
left: 15px;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
color: #666;
|
|
font-size: 18px;
|
|
}
|
|
|
|
.search-input {
|
|
width: 100%;
|
|
padding: 15px 15px 15px 50px;
|
|
border: none;
|
|
border-radius: 25px;
|
|
font-size: 16px;
|
|
background: white;
|
|
color: #333;
|
|
outline: none;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.search-input:focus {
|
|
box-shadow: 0 0 0 3px rgba(255, 255, 255, 0.3);
|
|
}
|
|
|
|
.search-clear {
|
|
position: absolute;
|
|
right: 15px;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
background: none;
|
|
border: none;
|
|
color: #666;
|
|
cursor: pointer;
|
|
font-size: 16px;
|
|
transition: color 0.3s ease;
|
|
}
|
|
|
|
.search-clear:hover {
|
|
color: #333;
|
|
}
|
|
|
|
.search-button {
|
|
background: linear-gradient(135deg, #FFD700, #FFA500);
|
|
color: #333;
|
|
border: none;
|
|
border-radius: 25px;
|
|
padding: 15px 25px;
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
|
|
.search-button:hover {
|
|
transform: translateY(-2px);
|
|
box-shadow: 0 5px 15px rgba(255, 215, 0, 0.4);
|
|
}
|
|
|
|
.search-suggestions {
|
|
position: absolute;
|
|
top: 100%;
|
|
left: 0;
|
|
right: 0;
|
|
background: white;
|
|
border-radius: 15px;
|
|
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
|
|
z-index: 1000;
|
|
margin-top: 10px;
|
|
max-height: 300px;
|
|
overflow-y: auto;
|
|
}
|
|
|
|
.suggestions-header {
|
|
padding: 15px 20px;
|
|
background: #f8f9fa;
|
|
border-radius: 15px 15px 0 0;
|
|
font-weight: 600;
|
|
color: #333;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
|
|
.suggestions-list {
|
|
padding: 0;
|
|
}
|
|
|
|
.suggestion-item {
|
|
padding: 12px 20px;
|
|
cursor: pointer;
|
|
transition: background-color 0.3s ease;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
color: #333;
|
|
}
|
|
|
|
.suggestion-item:hover {
|
|
background: #f8f9fa;
|
|
}
|
|
|
|
.suggestion-item.selected {
|
|
background: #e3f2fd;
|
|
}
|
|
|
|
.suggestion-icon {
|
|
width: 20px;
|
|
text-align: center;
|
|
color: #666;
|
|
}
|
|
|
|
.suggestion-text {
|
|
flex: 1;
|
|
}
|
|
|
|
.suggestion-type {
|
|
font-size: 12px;
|
|
opacity: 0.7;
|
|
padding: 2px 8px;
|
|
border-radius: 10px;
|
|
background: #f0f0f0;
|
|
}
|
|
|
|
.search-filters {
|
|
background: rgba(255, 255, 255, 0.1);
|
|
border-radius: 15px;
|
|
padding: 25px;
|
|
margin-bottom: 25px;
|
|
}
|
|
|
|
.filter-section h4 {
|
|
margin: 0 0 20px 0;
|
|
font-size: 18px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
|
|
.filter-group {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.filter-group label {
|
|
display: block;
|
|
margin-bottom: 8px;
|
|
font-weight: 600;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.filter-select {
|
|
width: 100%;
|
|
padding: 12px 15px;
|
|
border: none;
|
|
border-radius: 10px;
|
|
background: white;
|
|
color: #333;
|
|
font-size: 14px;
|
|
outline: none;
|
|
}
|
|
|
|
.price-range {
|
|
display: flex;
|
|
gap: 10px;
|
|
align-items: center;
|
|
}
|
|
|
|
.price-input {
|
|
flex: 1;
|
|
padding: 12px 15px;
|
|
border: none;
|
|
border-radius: 10px;
|
|
background: white;
|
|
color: #333;
|
|
font-size: 14px;
|
|
outline: none;
|
|
}
|
|
|
|
.checkbox-label {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 10px;
|
|
cursor: pointer;
|
|
font-size: 14px;
|
|
}
|
|
|
|
.checkbox-label input[type="checkbox"] {
|
|
display: none;
|
|
}
|
|
|
|
.checkmark {
|
|
width: 20px;
|
|
height: 20px;
|
|
border: 2px solid white;
|
|
border-radius: 5px;
|
|
position: relative;
|
|
transition: all 0.3s ease;
|
|
}
|
|
|
|
.checkbox-label input[type="checkbox"]:checked + .checkmark {
|
|
background: #FFD700;
|
|
border-color: #FFD700;
|
|
}
|
|
|
|
.checkbox-label input[type="checkbox"]:checked + .checkmark::after {
|
|
content: '✓';
|
|
position: absolute;
|
|
top: 50%;
|
|
left: 50%;
|
|
transform: translate(-50%, -50%);
|
|
color: #333;
|
|
font-weight: bold;
|
|
}
|
|
|
|
.filter-actions {
|
|
display: flex;
|
|
gap: 15px;
|
|
margin-top: 20px;
|
|
}
|
|
|
|
.filter-clear, .filter-apply {
|
|
flex: 1;
|
|
padding: 12px 20px;
|
|
border: none;
|
|
border-radius: 10px;
|
|
font-size: 14px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
gap: 8px;
|
|
}
|
|
|
|
.filter-clear {
|
|
background: rgba(255, 255, 255, 0.2);
|
|
color: white;
|
|
}
|
|
|
|
.filter-apply {
|
|
background: linear-gradient(135deg, #FFD700, #FFA500);
|
|
color: #333;
|
|
}
|
|
|
|
.filter-clear:hover, .filter-apply:hover {
|
|
transform: translateY(-1px);
|
|
}
|
|
|
|
.search-results {
|
|
background: rgba(255, 255, 255, 0.1);
|
|
border-radius: 15px;
|
|
padding: 25px;
|
|
}
|
|
|
|
.results-header {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 20px;
|
|
padding-bottom: 15px;
|
|
border-bottom: 1px solid rgba(255, 255, 255, 0.2);
|
|
}
|
|
|
|
.results-info {
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 5px;
|
|
}
|
|
|
|
.results-count {
|
|
font-weight: 600;
|
|
font-size: 16px;
|
|
}
|
|
|
|
.search-query {
|
|
font-size: 14px;
|
|
opacity: 0.8;
|
|
}
|
|
|
|
.results-view {
|
|
display: flex;
|
|
gap: 5px;
|
|
}
|
|
|
|
.view-toggle {
|
|
background: rgba(255, 255, 255, 0.2);
|
|
border: none;
|
|
color: white;
|
|
width: 40px;
|
|
height: 40px;
|
|
border-radius: 10px;
|
|
cursor: pointer;
|
|
transition: all 0.3s ease;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
}
|
|
|
|
.view-toggle.active {
|
|
background: #FFD700;
|
|
color: #333;
|
|
}
|
|
|
|
.results-container {
|
|
min-height: 200px;
|
|
}
|
|
|
|
.empty-state {
|
|
text-align: center;
|
|
padding: 40px;
|
|
opacity: 0.7;
|
|
}
|
|
|
|
.empty-state i {
|
|
font-size: 48px;
|
|
margin-bottom: 15px;
|
|
}
|
|
|
|
.empty-state h3 {
|
|
margin: 0 0 10px 0;
|
|
font-size: 20px;
|
|
}
|
|
|
|
.empty-state p {
|
|
margin: 0;
|
|
font-size: 16px;
|
|
}
|
|
|
|
.search-facets {
|
|
background: rgba(255, 255, 255, 0.1);
|
|
border-radius: 15px;
|
|
padding: 20px;
|
|
margin-top: 20px;
|
|
}
|
|
|
|
.facets-header h4 {
|
|
margin: 0 0 15px 0;
|
|
font-size: 16px;
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 8px;
|
|
}
|
|
|
|
.facet-item {
|
|
display: flex;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
padding: 8px 0;
|
|
cursor: pointer;
|
|
transition: opacity 0.3s ease;
|
|
}
|
|
|
|
.facet-item:hover {
|
|
opacity: 0.8;
|
|
}
|
|
|
|
.facet-name {
|
|
font-size: 14px;
|
|
}
|
|
|
|
.facet-count {
|
|
background: rgba(255, 255, 255, 0.2);
|
|
padding: 2px 8px;
|
|
border-radius: 10px;
|
|
font-size: 12px;
|
|
}
|
|
|
|
/* Responsive Design */
|
|
@media (max-width: 768px) {
|
|
.advanced-search-widget {
|
|
padding: 20px;
|
|
}
|
|
|
|
.search-header {
|
|
flex-direction: column;
|
|
gap: 15px;
|
|
text-align: center;
|
|
}
|
|
|
|
.search-input-wrapper {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.filter-actions {
|
|
flex-direction: column;
|
|
}
|
|
|
|
.results-header {
|
|
flex-direction: column;
|
|
gap: 15px;
|
|
}
|
|
}
|
|
`;
|
|
|
|
const styleSheet = document.createElement('style');
|
|
styleSheet.textContent = styles;
|
|
document.head.appendChild(styleSheet);
|
|
}
|
|
|
|
bindEvents() {
|
|
// Search input
|
|
const searchInput = document.getElementById('search-input');
|
|
searchInput.addEventListener('input', (e) => {
|
|
this.handleSearchInput(e.target.value);
|
|
});
|
|
|
|
searchInput.addEventListener('keypress', (e) => {
|
|
if (e.key === 'Enter') {
|
|
this.performSearch();
|
|
}
|
|
});
|
|
|
|
// Search button
|
|
document.getElementById('search-button').addEventListener('click', () => {
|
|
this.performSearch();
|
|
});
|
|
|
|
// Clear button
|
|
document.getElementById('search-clear').addEventListener('click', () => {
|
|
this.clearSearch();
|
|
});
|
|
|
|
// Filter events
|
|
document.getElementById('filter-apply').addEventListener('click', () => {
|
|
this.applyFilters();
|
|
});
|
|
|
|
document.getElementById('filter-clear').addEventListener('click', () => {
|
|
this.clearFilters();
|
|
});
|
|
|
|
// View toggles
|
|
document.querySelectorAll('.view-toggle').forEach(button => {
|
|
button.addEventListener('click', (e) => {
|
|
this.toggleView(e.target.closest('.view-toggle').dataset.view);
|
|
});
|
|
});
|
|
}
|
|
|
|
handleSearchInput(query) {
|
|
this.currentQuery = query;
|
|
|
|
// Show/hide clear button
|
|
const clearButton = document.getElementById('search-clear');
|
|
clearButton.style.display = query ? 'block' : 'none';
|
|
|
|
// Clear previous timeout
|
|
if (this.searchTimeout) {
|
|
clearTimeout(this.searchTimeout);
|
|
}
|
|
|
|
// Debounce search suggestions
|
|
if (query.length >= 2) {
|
|
this.searchTimeout = setTimeout(() => {
|
|
this.getSuggestions(query);
|
|
}, 300);
|
|
} else {
|
|
this.hideSuggestions();
|
|
}
|
|
}
|
|
|
|
async getSuggestions(query) {
|
|
try {
|
|
const response = await fetch(`/search/suggestions/?q=${encodeURIComponent(query)}`);
|
|
const data = await response.json();
|
|
|
|
this.displaySuggestions(data.suggestions);
|
|
} catch (error) {
|
|
console.error('Error fetching suggestions:', error);
|
|
}
|
|
}
|
|
|
|
displaySuggestions(suggestions) {
|
|
const suggestionsContainer = document.getElementById('search-suggestions');
|
|
const suggestionsList = document.getElementById('suggestions-list');
|
|
|
|
if (suggestions.length === 0) {
|
|
suggestionsContainer.style.display = 'none';
|
|
return;
|
|
}
|
|
|
|
suggestionsList.innerHTML = suggestions.map(suggestion => `
|
|
<div class="suggestion-item" data-query="${suggestion.title}">
|
|
<div class="suggestion-icon">
|
|
<i class="fas fa-${suggestion.type === 'product' ? 'tshirt' : 'gavel'}"></i>
|
|
</div>
|
|
<div class="suggestion-text">${suggestion.title}</div>
|
|
<div class="suggestion-type">${suggestion.type}</div>
|
|
</div>
|
|
`).join('');
|
|
|
|
// Add click events
|
|
suggestionsList.querySelectorAll('.suggestion-item').forEach(item => {
|
|
item.addEventListener('click', () => {
|
|
const query = item.dataset.query;
|
|
document.getElementById('search-input').value = query;
|
|
this.currentQuery = query;
|
|
this.hideSuggestions();
|
|
this.performSearch();
|
|
});
|
|
});
|
|
|
|
suggestionsContainer.style.display = 'block';
|
|
}
|
|
|
|
hideSuggestions() {
|
|
document.getElementById('search-suggestions').style.display = 'none';
|
|
}
|
|
|
|
async performSearch() {
|
|
if (!this.currentQuery.trim()) {
|
|
return;
|
|
}
|
|
|
|
this.isSearching = true;
|
|
this.showLoading();
|
|
|
|
try {
|
|
const startTime = performance.now();
|
|
|
|
const response = await fetch('/search/api/', {
|
|
method: 'GET',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
params: {
|
|
q: this.currentQuery,
|
|
filters: JSON.stringify(this.filters),
|
|
}
|
|
});
|
|
|
|
const data = await response.json();
|
|
const endTime = performance.now();
|
|
|
|
this.displayResults(data.results, endTime - startTime);
|
|
this.updateSearchStats(data.total, endTime - startTime);
|
|
|
|
} catch (error) {
|
|
console.error('Search error:', error);
|
|
this.showError('Fehler bei der Suche');
|
|
} finally {
|
|
this.isSearching = false;
|
|
this.hideLoading();
|
|
}
|
|
}
|
|
|
|
displayResults(results, searchTime) {
|
|
const container = document.getElementById('results-container');
|
|
|
|
if (results.length === 0) {
|
|
container.innerHTML = `
|
|
<div class="empty-state">
|
|
<i class="fas fa-search"></i>
|
|
<h3>Keine Ergebnisse gefunden</h3>
|
|
<p>Versuche andere Suchbegriffe oder Filter</p>
|
|
</div>
|
|
`;
|
|
return;
|
|
}
|
|
|
|
container.innerHTML = results.map(result => `
|
|
<div class="result-item">
|
|
<div class="result-image">
|
|
<img src="${result.image_url || '/static/images/placeholder.jpg'}" alt="${result.title}">
|
|
</div>
|
|
<div class="result-content">
|
|
<h4>${result.title}</h4>
|
|
<p>${result.description}</p>
|
|
<div class="result-meta">
|
|
<span class="result-price">€${result.price}</span>
|
|
<span class="result-type">${result.type}</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
`).join('');
|
|
}
|
|
|
|
updateSearchStats(total, searchTime) {
|
|
document.getElementById('search-query').textContent = `für "${this.currentQuery}"`;
|
|
document.querySelector('.results-count').textContent = `${total} Ergebnisse`;
|
|
document.querySelector('.search-time').textContent = `${Math.round(searchTime)}ms`;
|
|
}
|
|
|
|
applyFilters() {
|
|
this.filters = {
|
|
category: document.getElementById('category-filter').value,
|
|
fursuit_type: document.getElementById('fursuit-type-filter').value,
|
|
price_min: document.getElementById('price-min').value,
|
|
price_max: document.getElementById('price-max').value,
|
|
featured: document.getElementById('featured-filter').checked,
|
|
sort_by: document.getElementById('sort-filter').value,
|
|
};
|
|
|
|
this.performSearch();
|
|
}
|
|
|
|
clearFilters() {
|
|
document.getElementById('category-filter').value = '';
|
|
document.getElementById('fursuit-type-filter').value = '';
|
|
document.getElementById('price-min').value = '';
|
|
document.getElementById('price-max').value = '';
|
|
document.getElementById('featured-filter').checked = false;
|
|
document.getElementById('sort-filter').value = '-created_at';
|
|
|
|
this.filters = {};
|
|
}
|
|
|
|
clearSearch() {
|
|
document.getElementById('search-input').value = '';
|
|
this.currentQuery = '';
|
|
document.getElementById('search-clear').style.display = 'none';
|
|
this.hideSuggestions();
|
|
|
|
// Reset results
|
|
document.getElementById('results-container').innerHTML = `
|
|
<div class="empty-state">
|
|
<i class="fas fa-search"></i>
|
|
<h3>Starte deine Suche</h3>
|
|
<p>Gib einen Suchbegriff ein oder verwende die Filter</p>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
toggleView(view) {
|
|
document.querySelectorAll('.view-toggle').forEach(btn => {
|
|
btn.classList.remove('active');
|
|
});
|
|
|
|
event.target.closest('.view-toggle').classList.add('active');
|
|
|
|
const container = document.getElementById('results-container');
|
|
container.className = `results-container view-${view}`;
|
|
}
|
|
|
|
showLoading() {
|
|
const container = document.getElementById('results-container');
|
|
container.innerHTML = `
|
|
<div class="loading-state">
|
|
<i class="fas fa-spinner fa-spin"></i>
|
|
<p>Suchen...</p>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
hideLoading() {
|
|
// Loading state will be replaced by results
|
|
}
|
|
|
|
showError(message) {
|
|
const container = document.getElementById('results-container');
|
|
container.innerHTML = `
|
|
<div class="error-state">
|
|
<i class="fas fa-exclamation-triangle"></i>
|
|
<h3>Fehler</h3>
|
|
<p>${message}</p>
|
|
</div>
|
|
`;
|
|
}
|
|
|
|
initializeSearch() {
|
|
// Initialize with empty state
|
|
this.clearSearch();
|
|
}
|
|
}
|
|
|
|
// Advanced Search Widget initialisieren
|
|
document.addEventListener('DOMContentLoaded', function() {
|
|
window.kasicoAdvancedSearch = new KasicoAdvancedSearch();
|
|
});
|