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