/** * Kasico Auction Widget - Real-time Biet-System * WebSocket-basierte Live-Auktionen mit Furry-Design */ class KasicoAuctionWidget { constructor(auctionId) { this.auctionId = auctionId; this.websocket = null; this.currentBid = null; this.timeRemaining = null; this.isConnected = false; this.bidHistory = []; this.watchers = []; this.init(); } init() { // Auction Widget HTML erstellen this.createWidget(); // Event Listeners this.bindEvents(); // WebSocket-Verbindung this.connectWebSocket(); // Initial data laden this.loadAuctionData(); // Timer starten this.startTimer(); } createWidget() { const widgetHTML = `
`; // Widget in die Seite einfügen const container = document.getElementById('auction-container') || document.body; container.insertAdjacentHTML('beforeend', widgetHTML); // CSS hinzufügen this.addStyles(); } addStyles() { const styles = ` .auction-widget { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); border-radius: 20px; padding: 25px; color: white; font-family: 'Quicksand', sans-serif; box-shadow: 0 10px 40px rgba(0, 0, 0, 0.1); margin: 20px 0; } .auction-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); } .auction-title h2 { margin: 0 0 5px 0; font-size: 24px; font-weight: 700; } .auction-status { display: flex; align-items: center; gap: 8px; font-size: 14px; } .status-indicator { width: 10px; height: 10px; border-radius: 50%; background: #4CAF50; animation: pulse 2s infinite; } .status-indicator.ended { background: #f44336; animation: none; } @keyframes pulse { 0% { opacity: 1; } 50% { opacity: 0.5; } 100% { opacity: 1; } } .auction-timer { display: flex; align-items: center; gap: 10px; background: rgba(255, 255, 255, 0.1); padding: 12px 20px; border-radius: 25px; font-weight: 600; } .timer-text { font-size: 18px; font-family: 'Courier New', monospace; } .current-bid-section { background: rgba(255, 255, 255, 0.1); border-radius: 15px; padding: 25px; margin-bottom: 25px; } .current-bid-display { text-align: center; margin-bottom: 20px; } .bid-label { font-size: 14px; opacity: 0.8; margin-bottom: 5px; } .bid-amount { font-size: 36px; font-weight: 700; margin-bottom: 10px; color: #FFD700; } .bid-info { display: flex; justify-content: center; gap: 20px; font-size: 14px; opacity: 0.8; } .bid-form { display: flex; gap: 15px; align-items: center; margin-bottom: 15px; } .bid-input-group { position: relative; flex: 1; } .currency-symbol { position: absolute; left: 15px; top: 50%; transform: translateY(-50%); font-weight: 600; color: #333; } .bid-input { width: 100%; padding: 15px 15px 15px 35px; border: none; border-radius: 25px; font-size: 16px; font-weight: 600; background: white; color: #333; outline: none; transition: all 0.3s ease; } .bid-input:focus { box-shadow: 0 0 0 3px rgba(255, 215, 0, 0.3); } .bid-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; } .bid-button:hover { transform: translateY(-2px); box-shadow: 0 5px 15px rgba(255, 215, 0, 0.4); } .bid-button:disabled { opacity: 0.5; cursor: not-allowed; transform: none; } .reserve-info { display: flex; align-items: center; gap: 8px; font-size: 14px; opacity: 0.8; justify-content: center; } .bid-history-section { background: rgba(255, 255, 255, 0.1); border-radius: 15px; padding: 20px; margin-bottom: 25px; } .section-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; } .section-header h3 { margin: 0; font-size: 18px; display: flex; align-items: center; gap: 8px; } .bid-count { font-size: 14px; opacity: 0.8; } .bid-history { max-height: 200px; overflow-y: auto; } .bid-item { display: flex; justify-content: space-between; align-items: center; padding: 10px 0; border-bottom: 1px solid rgba(255, 255, 255, 0.1); } .bid-item:last-child { border-bottom: none; } .bid-user { font-weight: 600; } .bid-amount-small { color: #FFD700; font-weight: 600; } .bid-time-small { font-size: 12px; opacity: 0.7; } .empty-history { text-align: center; padding: 30px; opacity: 0.7; } .empty-history i { font-size: 24px; margin-bottom: 10px; } .auction-info-section { background: rgba(255, 255, 255, 0.1); border-radius: 15px; padding: 20px; margin-bottom: 25px; } .info-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 20px; } .info-item { display: flex; align-items: center; gap: 12px; } .info-item i { font-size: 20px; color: #FFD700; } .info-content { display: flex; flex-direction: column; } .info-label { font-size: 12px; opacity: 0.8; } .info-value { font-size: 18px; font-weight: 600; } .watch-section { text-align: center; } .watch-button { background: rgba(255, 255, 255, 0.2); color: white; border: 2px solid rgba(255, 255, 255, 0.3); border-radius: 25px; padding: 12px 25px; font-size: 16px; font-weight: 600; cursor: pointer; transition: all 0.3s ease; display: inline-flex; align-items: center; gap: 8px; } .watch-button:hover { background: rgba(255, 255, 255, 0.3); border-color: rgba(255, 255, 255, 0.5); } .watch-button.watching { background: #e91e63; border-color: #e91e63; } .watch-button.watching:hover { background: #c2185b; border-color: #c2185b; } /* Responsive Design */ @media (max-width: 768px) { .auction-widget { padding: 20px; } .auction-header { flex-direction: column; gap: 15px; text-align: center; } .bid-form { flex-direction: column; } .info-grid { grid-template-columns: 1fr; gap: 15px; } } `; const styleSheet = document.createElement('style'); styleSheet.textContent = styles; document.head.appendChild(styleSheet); } bindEvents() { // Bid Button document.getElementById('place-bid-btn').addEventListener('click', () => { this.placeBid(); }); // Bid Input document.getElementById('bid-amount').addEventListener('keypress', (e) => { if (e.key === 'Enter') { this.placeBid(); } }); // Watch Button document.getElementById('watch-button').addEventListener('click', () => { this.toggleWatch(); }); } connectWebSocket() { const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; const wsUrl = `${protocol}//${window.location.host}/ws/auction/${this.auctionId}/`; this.websocket = new WebSocket(wsUrl); this.websocket.onopen = () => { console.log('Auction WebSocket connected'); this.isConnected = true; this.updateConnectionStatus(true); }; this.websocket.onmessage = (event) => { const data = JSON.parse(event.data); this.handleWebSocketMessage(data); }; this.websocket.onclose = () => { console.log('Auction WebSocket disconnected'); this.isConnected = false; this.updateConnectionStatus(false); // Reconnect nach 5 Sekunden setTimeout(() => { if (!this.isConnected) { this.connectWebSocket(); } }, 5000); }; this.websocket.onerror = (error) => { console.error('Auction WebSocket error:', error); }; } handleWebSocketMessage(data) { switch (data.type) { case 'bid_placed': this.handleNewBid(data.bid); break; case 'auction_updated': this.updateAuctionData(data.auction); break; case 'time_update': this.updateTimer(data.time_remaining); break; case 'auction_ended': this.handleAuctionEnded(data.winner); break; } } async loadAuctionData() { try { const response = await fetch(`/auction/api/${this.auctionId}/`); const data = await response.json(); this.updateAuctionData(data); } catch (error) { console.error('Error loading auction data:', error); } } updateAuctionData(data) { // Basic info document.getElementById('auction-title').textContent = data.title; document.getElementById('current-bid-amount').textContent = data.current_bid ? `€${parseFloat(data.current_bid).toFixed(2)}` : `€${parseFloat(data.starting_bid).toFixed(2)}`; // Status const statusElement = document.getElementById('auction-status'); const statusText = statusElement.querySelector('.status-text'); const statusIndicator = statusElement.querySelector('.status-indicator'); statusText.textContent = data.is_active ? 'Aktiv' : 'Beendet'; statusIndicator.className = `status-indicator ${data.is_active ? '' : 'ended'}`; // Reserve price if (data.reserve_price) { document.getElementById('reserve-price').textContent = parseFloat(data.reserve_price).toFixed(2); document.getElementById('reserve-info').style.display = 'flex'; } // Stats document.getElementById('total-bidders').textContent = data.total_bidders; document.getElementById('bid-count').textContent = `${data.total_bids} Gebote`; // Store current data this.currentBid = data.current_bid; this.timeRemaining = data.time_remaining; // Update timer this.updateTimer(data.time_remaining); // Load bid history this.loadBidHistory(); } async loadBidHistory() { try { const response = await fetch(`/auction/api/${this.auctionId}/bids/`); const data = await response.json(); this.updateBidHistory(data.bids); } catch (error) { console.error('Error loading bid history:', error); } } updateBidHistory(bids) { const historyContainer = document.getElementById('bid-history'); if (bids.length === 0) { historyContainer.innerHTML = `Noch keine Gebote