/** * 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 = ` `; // 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 => `
${suggestion.title}
${suggestion.type}
`).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 = `

Keine Ergebnisse gefunden

Versuche andere Suchbegriffe oder Filter

`; return; } container.innerHTML = results.map(result => `
${result.title}

${result.title}

${result.description}

€${result.price} ${result.type}
`).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 = `

Starte deine Suche

Gib einen Suchbegriff ein oder verwende die Filter

`; } 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 = `

Suchen...

`; } hideLoading() { // Loading state will be replaced by results } showError(message) { const container = document.getElementById('results-container'); container.innerHTML = `

Fehler

${message}

`; } initializeSearch() { // Initialize with empty state this.clearSearch(); } } // Advanced Search Widget initialisieren document.addEventListener('DOMContentLoaded', function() { window.kasicoAdvancedSearch = new KasicoAdvancedSearch(); });