furry/static/js/furry-ajax.js

438 lines
14 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* ===== FURRY AJAX LIBRARY ===== */
/* Moderne AJAX-Funktionen für kasico.de */
class FurryAjax {
constructor() {
this.csrfToken = this.getCSRFToken();
this.baseURL = window.location.origin;
this.setupGlobalHandlers();
}
// CSRF Token aus Meta-Tag oder Cookie holen
getCSRFToken() {
const metaTag = document.querySelector('meta[name=csrf-token]');
if (metaTag) return metaTag.getAttribute('content');
const cookie = document.cookie.match(/csrftoken=([^;]+)/);
return cookie ? cookie[1] : '';
}
// Globale Event-Handler einrichten
setupGlobalHandlers() {
// Loading States für alle AJAX-Requests
document.addEventListener('furry-ajax-start', this.showLoading.bind(this));
document.addEventListener('furry-ajax-end', this.hideLoading.bind(this));
// Error Handler
document.addEventListener('furry-ajax-error', this.handleError.bind(this));
}
// Haupt-AJAX-Funktion
async request(url, options = {}) {
const defaultOptions = {
method: 'GET',
headers: {
'X-CSRFToken': this.csrfToken,
'Content-Type': 'application/json',
'X-Requested-With': 'XMLHttpRequest'
},
credentials: 'same-origin'
};
const config = { ...defaultOptions, ...options };
// Loading State triggern
this.triggerEvent('furry-ajax-start');
try {
const response = await fetch(url, config);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const contentType = response.headers.get('content-type');
let data;
if (contentType && contentType.includes('application/json')) {
data = await response.json();
} else {
data = await response.text();
}
this.triggerEvent('furry-ajax-end');
return { success: true, data, response };
} catch (error) {
this.triggerEvent('furry-ajax-error', { error });
return { success: false, error: error.message };
}
}
// Warenkorb-Funktionen
async addToCart(productId, quantity = 1) {
const result = await this.request(`/add-to-cart/${productId}/`, {
method: 'POST',
body: JSON.stringify({ quantity })
});
if (result.success) {
this.showSuccess('Produkt wurde zum Warenkorb hinzugefügt!');
this.updateCartCount(result.data.cart_count);
} else {
this.showError('Fehler beim Hinzufügen zum Warenkorb');
}
return result;
}
async updateCartItem(itemId, quantity) {
const result = await this.request(`/update-cart-item/${itemId}/`, {
method: 'POST',
body: JSON.stringify({ quantity })
});
if (result.success) {
this.updateCartTotal(result.data.total);
this.showSuccess('Warenkorb aktualisiert!');
} else {
this.showError('Fehler beim Aktualisieren des Warenkorbs');
}
return result;
}
async removeFromCart(itemId) {
const result = await this.request(`/remove-cart-item/${itemId}/`, {
method: 'POST'
});
if (result.success) {
this.showSuccess('Produkt aus Warenkorb entfernt!');
this.updateCartCount(result.data.cart_count);
// Element aus DOM entfernen
const cartItem = document.querySelector(`[data-cart-item="${itemId}"]`);
if (cartItem) {
cartItem.style.animation = 'fadeOut 0.3s ease';
setTimeout(() => cartItem.remove(), 300);
}
} else {
this.showError('Fehler beim Entfernen aus Warenkorb');
}
return result;
}
// Wunschliste-Funktionen
async addToWishlist(productId) {
const result = await this.request(`/add-to-wishlist/${productId}/`, {
method: 'POST'
});
if (result.success) {
this.showSuccess('Produkt zur Wunschliste hinzugefügt!');
this.updateWishlistCount(result.data.wishlist_count);
} else {
this.showError('Fehler beim Hinzufügen zur Wunschliste');
}
return result;
}
async removeFromWishlist(productId) {
const result = await this.request(`/remove-from-wishlist/${productId}/`, {
method: 'POST'
});
if (result.success) {
this.showSuccess('Produkt aus Wunschliste entfernt!');
this.updateWishlistCount(result.data.wishlist_count);
} else {
this.showError('Fehler beim Entfernen aus Wunschliste');
}
return result;
}
// Bewertungen
async submitReview(productId, rating, comment) {
const result = await this.request(`/submit-review/${productId}/`, {
method: 'POST',
body: JSON.stringify({ rating, comment })
});
if (result.success) {
this.showSuccess('Bewertung erfolgreich abgegeben!');
this.updateProductRating(productId, result.data.average_rating);
} else {
this.showError('Fehler beim Abgeben der Bewertung');
}
return result;
}
// Live-Suche
async searchProducts(query) {
const result = await this.request(`/search-products/?q=${encodeURIComponent(query)}`);
if (result.success) {
this.updateSearchResults(result.data.products);
}
return result;
}
// Filter-Funktionen
async applyFilters(filters) {
const queryString = new URLSearchParams(filters).toString();
const result = await this.request(`/filter-products/?${queryString}`);
if (result.success) {
this.updateProductGrid(result.data.products);
this.updateFilterCounts(result.data.counts);
}
return result;
}
// Loading States
showLoading(element = null) {
const target = element || document.body;
target.classList.add('loading');
// Loading Spinner hinzufügen
const spinner = document.createElement('div');
spinner.className = 'furry-loading furry-loading-lg';
spinner.id = 'furry-loading-spinner';
target.appendChild(spinner);
}
hideLoading(element = null) {
const target = element || document.body;
target.classList.remove('loading');
// Loading Spinner entfernen
const spinner = document.getElementById('furry-loading-spinner');
if (spinner) spinner.remove();
}
// Benachrichtigungen
showSuccess(message, duration = 3000) {
this.showNotification(message, 'success', duration);
}
showError(message, duration = 5000) {
this.showNotification(message, 'error', duration);
}
showWarning(message, duration = 4000) {
this.showNotification(message, 'warning', duration);
}
showInfo(message, duration = 3000) {
this.showNotification(message, 'info', duration);
}
showNotification(message, type = 'info', duration = 3000) {
const alert = document.createElement('div');
alert.className = `furry-alert furry-alert-${type}`;
alert.style.position = 'fixed';
alert.style.top = '20px';
alert.style.right = '20px';
alert.style.zIndex = '9999';
alert.style.maxWidth = '400px';
alert.style.animation = 'slideInRight 0.3s ease';
alert.innerHTML = `
<div class="furry-alert-icon">
${type === 'success' ? '✅' :
type === 'error' ? '❌' :
type === 'warning' ? '⚠️' : ''}
</div>
<div class="furry-alert-content">
<div class="furry-alert-title">${type.charAt(0).toUpperCase() + type.slice(1)}</div>
<div class="furry-alert-message">${message}</div>
</div>
<button onclick="this.parentElement.remove()" style="background: none; border: none; color: inherit; font-size: 1.2rem; cursor: pointer; margin-left: 0.5rem;">×</button>
`;
document.body.appendChild(alert);
// Auto-remove nach duration
setTimeout(() => {
if (alert.parentElement) {
alert.style.animation = 'slideOutRight 0.3s ease';
setTimeout(() => alert.remove(), 300);
}
}, duration);
}
// DOM Updates
updateCartCount(count) {
const cartBadge = document.querySelector('.cart-count');
if (cartBadge) {
cartBadge.textContent = count;
cartBadge.style.animation = 'bounce 0.5s ease';
}
}
updateWishlistCount(count) {
const wishlistBadge = document.querySelector('.wishlist-count');
if (wishlistBadge) {
wishlistBadge.textContent = count;
wishlistBadge.style.animation = 'bounce 0.5s ease';
}
}
updateCartTotal(total) {
const totalElement = document.querySelector('.cart-total');
if (totalElement) {
totalElement.textContent = `${total}`;
totalElement.style.animation = 'pulse 0.5s ease';
}
}
updateProductRating(productId, averageRating) {
const ratingElement = document.querySelector(`[data-product-rating="${productId}"]`);
if (ratingElement) {
ratingElement.innerHTML = this.generateStars(averageRating);
ratingElement.style.animation = 'fadeIn 0.5s ease';
}
}
updateSearchResults(products) {
const resultsContainer = document.querySelector('.search-results');
if (resultsContainer && products) {
resultsContainer.innerHTML = this.generateProductHTML(products);
resultsContainer.style.animation = 'fadeIn 0.3s ease';
}
}
updateProductGrid(products) {
const gridContainer = document.querySelector('.products-grid');
if (gridContainer && products) {
gridContainer.innerHTML = this.generateProductHTML(products);
gridContainer.style.animation = 'fadeIn 0.3s ease';
}
}
updateFilterCounts(counts) {
Object.keys(counts).forEach(filterType => {
const countElement = document.querySelector(`[data-filter-count="${filterType}"]`);
if (countElement) {
countElement.textContent = counts[filterType];
}
});
}
// Helper Functions
generateStars(rating) {
let stars = '';
for (let i = 1; i <= 5; i++) {
if (i <= rating) {
stars += '⭐';
} else {
stars += '☆';
}
}
return stars;
}
generateProductHTML(products) {
return products.map(product => `
<div class="furry-card furry-product-card">
${product.image ?
`<img src="${product.image}" alt="${product.name}" class="furry-card-image">` :
`<div class="furry-card-image" style="background: linear-gradient(45deg, var(--furry-primary), var(--furry-secondary)); display: flex; align-items: center; justify-content: center; color: white; font-size: 2rem;">🐾</div>`
}
<div class="furry-card-header">
<div>
<h3 class="furry-card-title">${product.name}</h3>
<p class="furry-card-subtitle">${product.fursuit_type}${product.style}</p>
</div>
${product.featured ? '<div class="furry-product-badge">Featured</div>' : ''}
</div>
<div class="furry-card-content">
<p>${product.description}</p>
<div class="furry-product-price">${product.price}€</div>
</div>
<div class="furry-card-footer">
<a href="/product/${product.id}/" class="furry-btn furry-btn-primary furry-btn-sm">👁️ Details</a>
<button onclick="furryAjax.addToCart(${product.id})" class="furry-btn furry-btn-outline furry-btn-sm">🛒 Hinzufügen</button>
</div>
</div>
`).join('');
}
// Event Handling
triggerEvent(eventName, data = {}) {
const event = new CustomEvent(eventName, { detail: data });
document.dispatchEvent(event);
}
handleError(event) {
console.error('FurryAjax Error:', event.detail.error);
this.showError('Ein Fehler ist aufgetreten. Bitte versuche es erneut.');
}
}
// Globale Instanz erstellen
const furryAjax = new FurryAjax();
// CSS für Animationen
const style = document.createElement('style');
style.textContent = `
@keyframes slideInRight {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
@keyframes slideOutRight {
from { transform: translateX(0); opacity: 1; }
to { transform: translateX(100%); opacity: 0; }
}
@keyframes fadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes fadeOut {
from { opacity: 1; }
to { opacity: 0; }
}
@keyframes bounce {
0%, 20%, 50%, 80%, 100% { transform: translateY(0); }
40% { transform: translateY(-10px); }
60% { transform: translateY(-5px); }
}
@keyframes pulse {
0% { transform: scale(1); }
50% { transform: scale(1.05); }
100% { transform: scale(1); }
}
.loading {
position: relative;
pointer-events: none;
}
.loading::after {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(255, 255, 255, 0.8);
z-index: 1000;
}
`;
document.head.appendChild(style);
// Export für Module-System
if (typeof module !== 'undefined' && module.exports) {
module.exports = FurryAjax;
}