/** * Recommendation Widget für Frontend Integration * ML-basierte Empfehlungen mit Furry-Design */ class RecommendationWidget { constructor(options = {}) { this.options = { container: options.container || '#recommendations', apiEndpoint: options.apiEndpoint || '/recommendations/api/recommendations/', behaviorEndpoint: options.behaviorEndpoint || '/recommendations/api/track-behavior/', clickEndpoint: options.clickEndpoint || '/recommendations/api/track-recommendation-click/', purchaseEndpoint: options.purchaseEndpoint || '/recommendations/api/track-recommendation-purchase/', maxRecommendations: options.maxRecommendations || 6, autoRefresh: options.autoRefresh || true, refreshInterval: options.refreshInterval || 300000, // 5 minutes showLoading: options.showLoading || true, furryTheme: options.furryTheme || true, ...options }; this.isAuthenticated = false; this.currentRecommendations = []; this.init(); } init() { this.checkAuthentication(); this.setupEventListeners(); this.loadRecommendations(); if (this.options.autoRefresh) { this.startAutoRefresh(); } } checkAuthentication() { // Check if user is authenticated (CSRF token presence) const csrfToken = this.getCookie('csrftoken'); this.isAuthenticated = !!csrfToken; } getCookie(name) { let cookieValue = null; if (document.cookie && document.cookie !== '') { const cookies = document.cookie.split(';'); for (let i = 0; i < cookies.length; i++) { const cookie = cookies[i].trim(); if (cookie.substring(0, name.length + 1) === (name + '=')) { cookieValue = decodeURIComponent(cookie.substring(name.length + 1)); break; } } } return cookieValue; } setupEventListeners() { // Track product views document.addEventListener('click', (e) => { if (e.target.closest('.product-card, .product-link')) { const productId = e.target.closest('[data-product-id]')?.dataset.productId; if (productId) { this.trackBehavior('view', productId); } } }); // Track cart additions document.addEventListener('click', (e) => { if (e.target.closest('.add-to-cart')) { const productId = e.target.closest('[data-product-id]')?.dataset.productId; if (productId) { this.trackBehavior('cart_add', productId); } } }); // Track wishlist additions document.addEventListener('click', (e) => { if (e.target.closest('.add-to-wishlist')) { const productId = e.target.closest('[data-product-id]')?.dataset.productId; if (productId) { this.trackBehavior('wishlist_add', productId); } } }); } async loadRecommendations() { if (!this.isAuthenticated) { this.loadPopularProducts(); return; } try { if (this.options.showLoading) { this.showLoading(); } const response = await fetch(this.options.apiEndpoint, { method: 'GET', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': this.getCookie('csrftoken') } }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); this.currentRecommendations = data.recommendations || []; this.renderRecommendations(); } catch (error) { console.error('Error loading recommendations:', error); this.loadPopularProducts(); } } async loadPopularProducts() { try { const response = await fetch('/recommendations/api/popular-products/'); if (response.ok) { const data = await response.json(); this.currentRecommendations = data.popular_products || []; this.renderRecommendations(); } } catch (error) { console.error('Error loading popular products:', error); } } renderRecommendations() { const container = document.querySelector(this.options.container); if (!container) return; if (this.currentRecommendations.length === 0) { container.innerHTML = this.renderEmptyState(); return; } const recommendationsHtml = this.currentRecommendations .slice(0, this.options.maxRecommendations) .map(rec => this.renderRecommendationCard(rec)) .join(''); container.innerHTML = `

${this.options.furryTheme ? '🦊 ' : ''}Empfohlen für dich

Basierend auf deinen Präferenzen

${recommendationsHtml}
`; // Add click tracking this.setupRecommendationTracking(); } renderRecommendationCard(recommendation) { const product = recommendation.product; const confidencePercent = Math.round(recommendation.confidence_score * 100); return `
${confidencePercent}% Match
${product.name}

${product.name}

€${product.price}

${recommendation.reason}

`; } renderEmptyState() { return `
${this.options.furryTheme ? '🦊' : '📦'}

Noch keine Empfehlungen

Interagiere mit Produkten, um personalisierte Empfehlungen zu erhalten!

`; } setupRecommendationTracking() { const cards = document.querySelectorAll('.recommendation-card'); cards.forEach(card => { card.addEventListener('click', (e) => { if (!e.target.closest('.btn')) { const productId = card.dataset.productId; const recommendationType = card.dataset.recommendationType; this.trackRecommendationClick(productId, recommendationType); } }); }); } async trackBehavior(behaviorType, productId, metadata = {}) { if (!this.isAuthenticated) return; try { await fetch(this.options.behaviorEndpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': this.getCookie('csrftoken') }, body: JSON.stringify({ behavior_type: behaviorType, product_id: productId, metadata: metadata }) }); } catch (error) { console.error('Error tracking behavior:', error); } } async trackRecommendationClick(productId, recommendationType) { if (!this.isAuthenticated) return; try { await fetch(this.options.clickEndpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': this.getCookie('csrftoken') }, body: JSON.stringify({ product_id: productId, recommendation_type: recommendationType }) }); } catch (error) { console.error('Error tracking recommendation click:', error); } } async trackRecommendationPurchase(productId, recommendationType) { if (!this.isAuthenticated) return; try { await fetch(this.options.purchaseEndpoint, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-CSRFToken': this.getCookie('csrftoken') }, body: JSON.stringify({ product_id: productId, recommendation_type: recommendationType }) }); } catch (error) { console.error('Error tracking recommendation purchase:', error); } } showLoading() { const container = document.querySelector(this.options.container); if (container) { container.innerHTML = `

Lade Empfehlungen...

`; } } startAutoRefresh() { setInterval(() => { this.loadRecommendations(); }, this.options.refreshInterval); } // Public methods refresh() { this.loadRecommendations(); } updateOptions(newOptions) { this.options = { ...this.options, ...newOptions }; } } // Similar Products Widget class SimilarProductsWidget { constructor(options = {}) { this.options = { container: options.container || '#similar-products', apiEndpoint: options.apiEndpoint || '/recommendations/api/similar-products/', maxProducts: options.maxProducts || 4, ...options }; this.init(); } init() { this.loadSimilarProducts(); } async loadSimilarProducts(productId) { if (!productId) return; try { const response = await fetch(`${this.options.apiEndpoint}${productId}/`); if (response.ok) { const data = await response.json(); this.renderSimilarProducts(data.similar_products); } } catch (error) { console.error('Error loading similar products:', error); } } renderSimilarProducts(products) { const container = document.querySelector(this.options.container); if (!container || !products) return; const productsHtml = products .slice(0, this.options.maxProducts) .map(product => this.renderProductCard(product)) .join(''); container.innerHTML = `

Ähnliche Produkte

${productsHtml}
`; } renderProductCard(product) { return `
${Math.round(product.similarity_score * 100)}% ähnlich
${product.product.name}

${product.product.name}

€${product.product.price}

${product.reason}

`; } } // Initialize widgets when DOM is loaded document.addEventListener('DOMContentLoaded', function() { // Initialize recommendation widget if (document.querySelector('#recommendations')) { window.recommendationWidget = new RecommendationWidget({ container: '#recommendations', maxRecommendations: 6, furryTheme: true }); } // Initialize similar products widget if (document.querySelector('#similar-products')) { window.similarProductsWidget = new SimilarProductsWidget({ container: '#similar-products', maxProducts: 4 }); // Load similar products for current product const productId = document.querySelector('[data-product-id]')?.dataset.productId; if (productId) { window.similarProductsWidget.loadSimilarProducts(productId); } } }); // Export for global access window.RecommendationWidget = RecommendationWidget; window.SimilarProductsWidget = SimilarProductsWidget;