// Supabase configuration const SUPABASE_URL = 'https://lfxlplnypzvjrhftaoog.supabase.co'; const SUPABASE_ANON_KEY = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImxmeGxwbG55cHp2anJoZnRhb29nIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NDkyMTQ3NzIsImV4cCI6MjA2NDc5MDc3Mn0.XR4preBqWAQ1rT4PFbpkmRdz57BTwIusBI89fIxDHM8'; // Initialize Supabase client const supabase = window.supabase.createClient(SUPABASE_URL, SUPABASE_ANON_KEY); // Global variables let currentUser = null; // Check authentication and load dashboard async function initDashboard() { try { // Get current session const { data: { session }, error } = await supabase.auth.getSession(); if (error) { console.error('Error checking authentication:', error); // Temporarily show dashboard for testing currentUser = { id: '9966cffd-2088-423c-b852-0ca7996cda97', email: 'admin@speedrun-arena.com' }; displayUserInfo({ email: 'admin@speedrun-arena.com' }); showDashboard(); // Check times section checkLinkStatusAndLoadTimes(); return; } if (!session) { // No session, redirect to login window.location.href = '/login'; return; } // User is authenticated, show dashboard if (session.user) { console.log('User data:', session.user); currentUser = session.user; displayUserInfo(session.user); } else { // Fallback if no user data currentUser = { id: '9966cffd-2088-423c-b852-0ca7996cda97', email: 'admin@speedrun-arena.com' }; displayUserInfo({ email: 'admin@speedrun-arena.com' }); } showDashboard(); // Load times section checkLinkStatusAndLoadTimes(); } catch (error) { console.error('An unexpected error occurred:', error); // window.location.href = '/login'; } } // Display user information function displayUserInfo(user) { const userEmail = document.getElementById('userEmail'); const userAvatar = document.getElementById('userAvatar'); userEmail.textContent = user.email; userAvatar.textContent = user.email.charAt(0).toUpperCase(); } // Show dashboard content function showDashboard() { document.getElementById('loading').style.display = 'none'; document.getElementById('dashboardContent').style.display = 'block'; } // Logout function async function logout() { try { const { error } = await supabase.auth.signOut(); if (error) { console.error('Error logging out:', error); } else { window.location.href = '/'; } } catch (error) { console.error('Error during logout:', error); } } // Listen for auth state changes supabase.auth.onAuthStateChange((event, session) => { if (event === 'SIGNED_OUT' || !session) { window.location.href = '/login'; } }); // Initialize dashboard when page loads initDashboard(); // Modal functions function openModal(modalId) { document.getElementById(modalId).style.display = 'block'; } function closeModal(modalId) { document.getElementById(modalId).style.display = 'none'; // Reset modal state if (modalId === 'rfidModal') { stopQRScanner(); document.getElementById('manualRfidInput').value = ''; } } // Close modal when clicking outside window.onclick = function(event) { if (event.target.classList.contains('modal')) { closeModal(event.target.id); } } // QR Scanner variables let qrStream = null; let qrScanning = false; // Show RFID Settings async function showRFIDSettings() { openModal('rfidModal'); // Reset scanner state stopQRScanner(); } // Check link status and load times async function checkLinkStatusAndLoadTimes() { if (!currentUser) { showTimesNotLinked(); return; } try { // Check if user has a linked player const response = await fetch(`/api/v1/public/user-player/${currentUser.id}?t=${Date.now()}`); if (response.ok) { const result = await response.json(); // User is linked, load times await loadUserTimesSection(result.data); } else { // User is not linked showTimesNotLinked(); } } catch (error) { console.error('Error checking link status:', error); showTimesNotLinked(); } } // Start QR Scanner async function startQRScanner() { try { // Request camera access qrStream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: 'environment', // Use back camera if available width: { ideal: 1280 }, height: { ideal: 720 } } }); const video = document.getElementById('qrVideo'); const canvas = document.getElementById('qrCanvas'); const context = canvas.getContext('2d'); video.srcObject = qrStream; video.play(); // Show camera container and update buttons document.getElementById('cameraContainer').style.display = 'block'; document.getElementById('startScanBtn').style.display = 'none'; document.getElementById('stopScanBtn').style.display = 'inline-block'; document.getElementById('scanningStatus').style.display = 'block'; qrScanning = true; // Start scanning loop video.addEventListener('loadedmetadata', () => { canvas.width = video.videoWidth; canvas.height = video.videoHeight; scanQRCode(); }); } catch (error) { console.error('Error accessing camera:', error); showMessage('rfidMessage', 'Kamera-Zugriff fehlgeschlagen. Bitte verwende die manuelle Eingabe.', 'error'); } } // Stop QR Scanner function stopQRScanner() { qrScanning = false; if (qrStream) { qrStream.getTracks().forEach(track => track.stop()); qrStream = null; } // Reset UI document.getElementById('cameraContainer').style.display = 'none'; document.getElementById('startScanBtn').style.display = 'inline-block'; document.getElementById('stopScanBtn').style.display = 'none'; document.getElementById('scanningStatus').style.display = 'none'; } // Scan QR Code from video stream function scanQRCode() { if (!qrScanning) return; const video = document.getElementById('qrVideo'); const canvas = document.getElementById('qrCanvas'); const context = canvas.getContext('2d'); if (video.readyState === video.HAVE_ENOUGH_DATA) { canvas.width = video.videoWidth; canvas.height = video.videoHeight; context.drawImage(video, 0, 0, canvas.width, canvas.height); const imageData = context.getImageData(0, 0, canvas.width, canvas.height); const code = jsQR(imageData.data, imageData.width, imageData.height); if (code) { console.log('QR Code detected:', code.data); handleQRCodeDetected(code.data); return; } } // Continue scanning if (qrScanning) { requestAnimationFrame(scanQRCode); } } // Format RFID UID to match database format function formatRfidUid(rawUid) { // Remove any existing formatting (spaces, colons, etc.) let cleanUid = rawUid.replace(/[^a-fA-F0-9]/g, '').toUpperCase(); // Handle different UID lengths if (cleanUid.length === 6) { // Pad 6-digit UID to 8 digits by adding leading zeros cleanUid = '00' + cleanUid; } else if (cleanUid.length === 8) { // Already correct length } else if (cleanUid.length < 6) { // Pad shorter UIDs to 8 digits cleanUid = cleanUid.padStart(8, '0'); } else { throw new Error(`Ungültige RFID UID Länge: ${cleanUid.length} Zeichen (unterstützt: 6-8)`); } // Format as XX:XX:XX:XX return cleanUid.match(/.{2}/g).join(':'); } // Handle detected QR code async function handleQRCodeDetected(qrData) { stopQRScanner(); try { // Extract and format RFID UID from QR code const rawUid = qrData.trim(); if (!rawUid) { showMessage('rfidMessage', 'QR-Code enthält keine gültige RFID UID', 'error'); return; } // Format the UID to match database format (XX:XX:XX:XX) const formattedUid = formatRfidUid(rawUid); showMessage('rfidMessage', `QR-Code erkannt: ${rawUid} → ${formattedUid}`, 'info'); // Link the user using the formatted RFID UID await linkUserByRfidUid(formattedUid); } catch (error) { console.error('Error formatting RFID UID:', error); showMessage('rfidMessage', `Fehler beim Formatieren der RFID UID: ${error.message}`, 'error'); } } // Manual RFID linking async function linkManualRfid() { const rawUid = document.getElementById('manualRfidInput').value.trim(); if (!rawUid) { showMessage('rfidMessage', 'Bitte gib eine RFID UID ein', 'error'); return; } try { // Format the UID to match database format const formattedUid = formatRfidUid(rawUid); showMessage('rfidMessage', `Formatiert: ${rawUid} → ${formattedUid}`, 'info'); await linkUserByRfidUid(formattedUid); } catch (error) { console.error('Error formatting manual RFID UID:', error); showMessage('rfidMessage', `Fehler beim Formatieren: ${error.message}`, 'error'); } } // Link user by RFID UID (core function) async function linkUserByRfidUid(rfidUid) { if (!currentUser) { showMessage('rfidMessage', 'Benutzer nicht authentifiziert', 'error'); return; } try { // First, find the player with this RFID UID const response = await fetch('/api/v1/public/link-by-rfid', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ rfiduid: rfidUid, supabase_user_id: currentUser.id }) }); const result = await response.json(); if (response.ok) { showMessage('rfidMessage', `✅ RFID erfolgreich verknüpft!\nSpieler: ${result.data.firstname} ${result.data.lastname}`, 'success'); setTimeout(() => { closeModal('rfidModal'); // Reload times section after successful linking checkLinkStatusAndLoadTimes(); }, 2000); } else { showMessage('rfidMessage', result.message || 'Fehler beim Verknüpfen', 'error'); } } catch (error) { console.error('Error linking RFID:', error); showMessage('rfidMessage', 'Fehler beim Verknüpfen der RFID', 'error'); } } // Show not linked state function showTimesNotLinked() { document.getElementById('timesLoading').style.display = 'none'; document.getElementById('timesNotLinked').style.display = 'block'; document.getElementById('timesDisplay').style.display = 'none'; } // Show loading state function showTimesLoading() { document.getElementById('timesLoading').style.display = 'block'; document.getElementById('timesNotLinked').style.display = 'none'; document.getElementById('timesDisplay').style.display = 'none'; } // Load user times for the section async function loadUserTimesSection(playerData) { showTimesLoading(); try { const response = await fetch(`/api/v1/public/user-times/${currentUser.id}?t=${Date.now()}`); const result = await response.json(); if (!response.ok) { throw new Error(result.message || 'Failed to load times'); } const times = result.data || result; // Update stats updateTimesStats(times, playerData); // Display times displayUserTimes(times); // Show the times display document.getElementById('timesLoading').style.display = 'none'; document.getElementById('timesNotLinked').style.display = 'none'; document.getElementById('timesDisplay').style.display = 'block'; // Initialize achievements for this player initializeAchievements(playerData.id); } catch (error) { console.error('Error loading user times:', error); showTimesNotLinked(); } } // Update stats cards function updateTimesStats(times, playerData) { // Total runs document.getElementById('totalRuns').textContent = times.length; // Best time if (times.length > 0) { const bestTimeValue = times.reduce((best, current) => { const currentSeconds = convertTimeToSeconds(current.recorded_time); const bestSeconds = convertTimeToSeconds(best.recorded_time); return currentSeconds < bestSeconds ? current : best; }); document.getElementById('bestTime').textContent = formatTime(bestTimeValue.recorded_time); } else { document.getElementById('bestTime').textContent = '--:--'; } // Unique locations count const uniqueLocations = [...new Set(times.map(time => time.location_name))]; document.getElementById('locationsCount').textContent = uniqueLocations.length; // Linked player name document.getElementById('linkedPlayer').textContent = `${playerData.firstname} ${playerData.lastname}`; } // Display user times in grid function displayUserTimes(times) { const timesGrid = document.getElementById('userTimesGrid'); if (times.length === 0) { timesGrid.innerHTML = `

Noch keine Zeiten aufgezeichnet

Deine ersten Läufe werden hier angezeigt, sobald du sie abgeschlossen hast!

`; return; } // Group times by location const timesByLocation = times.reduce((acc, time) => { if (!acc[time.location_name]) { acc[time.location_name] = []; } acc[time.location_name].push(time); return acc; }, {}); // Generate cards for each location const cards = Object.entries(timesByLocation).map(([locationName, locationTimes], index) => { // Sort times by performance (best first) const sortedTimes = locationTimes.sort((a, b) => { return convertTimeToSeconds(a.recorded_time) - convertTimeToSeconds(b.recorded_time); }); // Get best time for this location const bestTime = sortedTimes[0]; // Generate all runs for expanded view const allRunsHtml = sortedTimes.map((run, runIndex) => { let rankBadge = ''; let rankClass = ''; if (runIndex === 0) { rankBadge = '🥇 Beste'; rankClass = 'best'; } else if (runIndex === 1) { rankBadge = '🥈 2.'; rankClass = 'second'; } else if (runIndex === 2) { rankBadge = '🥉 3.'; rankClass = 'third'; } else { rankBadge = `${runIndex + 1}.`; rankClass = ''; } return `
${formatTime(run.recorded_time)}
${new Date(run.created_at).toLocaleDateString('de-DE')}
${new Date(run.created_at).toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' })}
${rankBadge}
`; }).join(''); return `
${locationName}
${formatTime(bestTime.recorded_time)}
${new Date(bestTime.created_at).toLocaleDateString('de-DE')} ${locationTimes.length} Läufe
Alle Läufe an diesem Standort:
${allRunsHtml}
`; }).join(''); timesGrid.innerHTML = cards; } // Toggle time card expansion function toggleTimeCard(cardElement) { const isExpanded = cardElement.classList.contains('expanded'); // Close all other cards first document.querySelectorAll('.user-time-card.expanded').forEach(card => { if (card !== cardElement) { card.classList.remove('expanded'); } }); // Toggle current card if (isExpanded) { cardElement.classList.remove('expanded'); } else { cardElement.classList.add('expanded'); } } // Helper function to convert time to seconds for comparison function convertTimeToSeconds(timeValue) { if (typeof timeValue === 'string') { // Handle HH:MM:SS format const parts = timeValue.split(':'); if (parts.length === 3) { return parseInt(parts[0]) * 3600 + parseInt(parts[1]) * 60 + parseFloat(parts[2]); } // Handle MM:SS format if (parts.length === 2) { return parseInt(parts[0]) * 60 + parseFloat(parts[1]); } } return parseFloat(timeValue) || 0; } // Format time interval to readable format function formatTime(interval) { // Postgres interval format: {"hours":0,"minutes":1,"seconds":23.45} if (typeof interval === 'object') { const { hours = 0, minutes = 0, seconds = 0 } = interval; const totalSeconds = hours * 3600 + minutes * 60 + seconds; return formatSeconds(totalSeconds); } // Fallback for string format if (typeof interval === 'string') { // Parse format like "00:01:23.45" const parts = interval.split(':'); if (parts.length === 3) { const hours = parseInt(parts[0]); const minutes = parseInt(parts[1]); const seconds = parseFloat(parts[2]); const totalSeconds = hours * 3600 + minutes * 60 + seconds; return formatSeconds(totalSeconds); } } return interval; } function formatSeconds(totalSeconds) { const minutes = Math.floor(totalSeconds / 60); const seconds = (totalSeconds % 60).toFixed(2); if (minutes > 0) { return `${minutes}:${seconds.padStart(5, '0')}`; } else { return `${seconds}s`; } } // Show message in modal function showMessage(containerId, message, type) { const container = document.getElementById(containerId); container.innerHTML = `
${message}
`; } // Initialize when DOM is loaded // ==================== ACHIEVEMENT FUNCTIONS ==================== // Global variables for achievements let currentPlayerId = null; let allAchievements = []; let playerAchievements = []; let currentAchievementCategory = 'all'; // Load achievements for the current player async function loadPlayerAchievements() { if (!currentPlayerId) { showAchievementsNotAvailable(); return; } try { // Show loading state document.getElementById('achievementsLoading').style.display = 'block'; document.getElementById('achievementStats').style.display = 'none'; document.getElementById('achievementCategories').style.display = 'none'; document.getElementById('achievementsNotAvailable').style.display = 'none'; // Load player achievements (includes all achievements with player status) const response = await fetch(`/api/achievements/player/${currentPlayerId}?t=${Date.now()}`); if (!response.ok) { throw new Error('Failed to load player achievements'); } const result = await response.json(); window.allAchievements = result.data; playerAchievements = result.data.filter(achievement => achievement.is_completed); // Load achievement statistics await loadAchievementStats(); // Show achievements displayAchievementStats(); displayAchievements(); // Hide loading state document.getElementById('achievementsLoading').style.display = 'none'; document.getElementById('achievementStats').style.display = 'flex'; document.getElementById('achievementCategories').style.display = 'block'; } catch (error) { console.error('Error loading achievements:', error); document.getElementById('achievementsLoading').style.display = 'none'; showAchievementsNotAvailable(); } } // Load achievement statistics async function loadAchievementStats() { try { const response = await fetch(`/api/achievements/player/${currentPlayerId}/stats?t=${Date.now()}`); if (response.ok) { const result = await response.json(); window.achievementStats = result.data; } } catch (error) { console.error('Error loading achievement stats:', error); } } // Display achievement statistics function displayAchievementStats() { if (!window.achievementStats) return; const stats = window.achievementStats; document.getElementById('totalPoints').textContent = stats.total_points; document.getElementById('completedAchievements').textContent = `${stats.completed_achievements}/${stats.total_achievements}`; document.getElementById('achievementsToday').textContent = stats.achievements_today; document.getElementById('completionPercentage').textContent = `${stats.completion_percentage}%`; } // Display achievements in grid function displayAchievements() { const achievementsGrid = document.getElementById('achievementsGrid'); if (!window.allAchievements || window.allAchievements.length === 0) { achievementsGrid.innerHTML = `
🏆

Noch keine Achievements

Starte deine ersten Läufe, um Achievements zu sammeln!

`; return; } // Filter achievements by category let filteredAchievements = window.allAchievements; if (currentAchievementCategory !== 'all') { filteredAchievements = window.allAchievements.filter(achievement => achievement.category === currentAchievementCategory ); } // Generate achievement cards const achievementCards = filteredAchievements.map(achievement => { const isCompleted = achievement.is_completed; const progress = achievement.progress || 0; const earnedAt = achievement.earned_at; // Debug logging if (achievement.name === 'Tageskönig') { console.log('Tageskönig Debug:', { isCompleted, progress, earnedAt }); } let progressText = ''; if (isCompleted) { progressText = earnedAt ? `Erreicht am ${new Date(earnedAt).toLocaleDateString('de-DE')}` : 'Abgeschlossen'; } else if (progress > 0) { // Show progress for incomplete achievements const conditionValue = getAchievementConditionValue(achievement.name); if (conditionValue) { progressText = `${progress}/${conditionValue}`; } } return `
${achievement.icon}

${achievement.name}

${achievement.description}

+${achievement.points} Punkte ${progressText ? `${progressText}` : ''}
${isCompleted ? '✅' : '⏳'}
`; }).join(''); achievementsGrid.innerHTML = achievementCards; } // Get achievement condition value for progress display function getAchievementConditionValue(achievementName) { const conditionMap = { 'Erste Schritte': 1, 'Durchhalter': 3, 'Fleißig': 5, 'Besessen': 10, 'Regelmäßig': 5, 'Stammgast': 10, 'Treue': 20, 'Veteran': 50, 'Fortschritt': 5, 'Durchbruch': 10, 'Transformation': 15, 'Perfektionist': 20 }; return conditionMap[achievementName] || null; } // Show achievement category function showAchievementCategory(category) { currentAchievementCategory = category; // Update active tab document.querySelectorAll('.category-tab').forEach(tab => { tab.classList.remove('active'); }); document.querySelector(`[data-category="${category}"]`).classList.add('active'); // Display filtered achievements displayAchievements(); } // Show achievement details (placeholder for future modal) function showAchievementDetails(achievementId) { const achievement = playerAchievements.find(a => a.id === achievementId); if (achievement) { console.log('Achievement details:', achievement); // TODO: Implement achievement details modal } } // Show achievements not available state function showAchievementsNotAvailable() { document.getElementById('achievementsLoading').style.display = 'none'; document.getElementById('achievementStats').style.display = 'none'; document.getElementById('achievementCategories').style.display = 'none'; document.getElementById('achievementsNotAvailable').style.display = 'block'; } // Check achievements for current player async function checkPlayerAchievements() { if (!currentPlayerId) return; try { const response = await fetch(`/api/achievements/check/${currentPlayerId}?t=${Date.now()}`, { method: 'POST' }); if (response.ok) { const result = await response.json(); if (result.data.count > 0) { // Show notification for new achievements showAchievementNotification(result.data.new_achievements); // Reload achievements await loadPlayerAchievements(); } } } catch (error) { console.error('Error checking achievements:', error); } } // Show achievement notification function showAchievementNotification(newAchievements) { // Create notification element const notification = document.createElement('div'); notification.className = 'achievement-notification'; notification.innerHTML = `
🏆

Neue Achievements erreicht!

Du hast ${newAchievements.length} neue Achievement${newAchievements.length > 1 ? 's' : ''} erhalten!

`; // Add to page document.body.appendChild(notification); // Auto-remove after 5 seconds setTimeout(() => { if (notification.parentElement) { notification.remove(); } }, 5000); } // Initialize achievements when player is loaded function initializeAchievements(playerId) { currentPlayerId = playerId; loadPlayerAchievements(); } // Web Notification Functions function showWebNotification(title, message, icon = '🏆') { if ('Notification' in window && Notification.permission === 'granted') { const notification = new Notification(title, { body: message, icon: '/pictures/icon-192.png', badge: '/pictures/icon-192.png', tag: 'ninjacross-achievement', requireInteraction: true }); // Auto-close after 10 seconds setTimeout(() => { notification.close(); }, 10000); // Handle click notification.onclick = function() { window.focus(); notification.close(); }; } } // Check for best time achievements and show notifications async function checkBestTimeNotifications() { try { const response = await fetch('/api/v1/public/best-times'); const result = await response.json(); if (result.success && result.data) { const { daily, weekly, monthly } = result.data; // Check if current player has best times if (currentPlayerId) { if (daily && daily.player_id === currentPlayerId) { showWebNotification( '🏆 Tageskönig!', `Glückwunsch! Du hast die beste Zeit des Tages mit ${daily.best_time} erreicht!`, '👑' ); } if (weekly && weekly.player_id === currentPlayerId) { showWebNotification( '🏆 Wochenchampion!', `Fantastisch! Du bist der Wochenchampion mit ${weekly.best_time}!`, '🏆' ); } if (monthly && monthly.player_id === currentPlayerId) { showWebNotification( '🏆 Monatsmeister!', `Unglaublich! Du bist der Monatsmeister mit ${monthly.best_time}!`, '🥇' ); } } } } catch (error) { console.error('Error checking best time notifications:', error); } } // Check for new achievements and show notifications async function checkAchievementNotifications() { try { if (!currentPlayerId) return; const response = await fetch(`/api/achievements/player/${currentPlayerId}?t=${Date.now()}`); const result = await response.json(); if (result.success && result.data) { const newAchievements = result.data.filter(achievement => { // Check if achievement was earned in the last 5 minutes const earnedAt = new Date(achievement.earned_at); const fiveMinutesAgo = new Date(Date.now() - 5 * 60 * 1000); return earnedAt > fiveMinutesAgo; }); if (newAchievements.length > 0) { newAchievements.forEach(achievement => { showWebNotification( `🏆 ${achievement.name}`, achievement.description, achievement.icon || '🏆' ); }); } } } catch (error) { console.error('Error checking achievement notifications:', error); } } // Periodic check for notifications (every 30 seconds) setInterval(() => { checkBestTimeNotifications(); checkAchievementNotifications(); }, 30000); // Settings Functions function showSettings() { const modal = document.getElementById('settingsModal'); if (modal) { modal.style.display = 'block'; loadSettings(); } } async function loadSettings() { try { if (!currentPlayerId) { console.error('No player ID available'); return; } // Load current player settings const response = await fetch(`/api/v1/public/user-player/${currentUser.id}`); const result = await response.json(); if (result.success && result.data) { const showInLeaderboard = result.data.show_in_leaderboard || false; document.getElementById('showInLeaderboard').checked = showInLeaderboard; } } catch (error) { console.error('Error loading settings:', error); } } function updateLeaderboardSetting() { const checkbox = document.getElementById('showInLeaderboard'); console.log('Leaderboard setting changed:', checkbox.checked); } async function saveSettings() { try { if (!currentPlayerId) { console.error('No player ID available'); return; } const showInLeaderboard = document.getElementById('showInLeaderboard').checked; // Update player settings const response = await fetch(`/api/v1/private/update-player-settings`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${localStorage.getItem('apiKey')}` }, body: JSON.stringify({ player_id: currentPlayerId, show_in_leaderboard: showInLeaderboard }) }); const result = await response.json(); if (result.success) { showNotification('Einstellungen erfolgreich gespeichert!', 'success'); closeModal('settingsModal'); } else { showNotification('Fehler beim Speichern der Einstellungen: ' + result.message, 'error'); } } catch (error) { console.error('Error saving settings:', error); showNotification('Fehler beim Speichern der Einstellungen', 'error'); } } function showNotification(message, type = 'info') { // Create notification element const notification = document.createElement('div'); notification.className = `notification notification-${type}`; notification.style.cssText = ` position: fixed; top: 20px; right: 20px; background: ${type === 'success' ? '#10b981' : type === 'error' ? '#ef4444' : '#3b82f6'}; color: white; padding: 1rem 1.5rem; border-radius: 0.5rem; box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); z-index: 10000; max-width: 300px; font-weight: 500; `; notification.textContent = message; document.body.appendChild(notification); // Remove notification after 3 seconds setTimeout(() => { if (notification.parentNode) { notification.parentNode.removeChild(notification); } }, 3000); } document.addEventListener('DOMContentLoaded', function() { // Add cookie settings button functionality const cookieSettingsBtn = document.getElementById('cookie-settings-footer'); if (cookieSettingsBtn) { cookieSettingsBtn.addEventListener('click', function() { if (window.cookieConsent) { window.cookieConsent.resetConsent(); } }); } });