// 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); // Initialize Socket.IO connection const socket = io(); // Global variable to store locations with coordinates let locationsData = []; // WebSocket Event Handlers socket.on('connect', () => { console.log('🔌 WebSocket verbunden'); }); socket.on('disconnect', () => { console.log('🔌 WebSocket getrennt'); }); socket.on('newTime', (data) => { console.log('🏁 Neue Zeit empfangen:', data); showNotification(data); // Reload data to show the new time loadData(); }); // Notification Functions function showNotification(timeData) { const notificationBubble = document.getElementById('notificationBubble'); const notificationTitle = document.getElementById('notificationTitle'); const notificationSubtitle = document.getElementById('notificationSubtitle'); // Format the time data const playerName = timeData.player_name || 'Unbekannter Spieler'; const locationName = timeData.location_name || 'Unbekannter Standort'; const timeString = timeData.recorded_time || '--:--'; // Update notification content notificationTitle.textContent = `🏁 Neue Zeit von ${playerName}!`; notificationSubtitle.textContent = `${timeString} • ${locationName}`; // Show notification notificationBubble.classList.remove('hide'); notificationBubble.classList.add('show'); // Auto-hide after 5 seconds setTimeout(() => { hideNotification(); }, 5000); } function hideNotification() { const notificationBubble = document.getElementById('notificationBubble'); notificationBubble.classList.remove('show'); notificationBubble.classList.add('hide'); // Remove hide class after animation setTimeout(() => { notificationBubble.classList.remove('hide'); }, 300); } // Check authentication status async function checkAuth() { try { const { data: { session } } = await supabase.auth.getSession(); if (session) { // User is logged in, show dashboard button document.getElementById('adminLoginBtn').style.display = 'none'; document.getElementById('dashboardBtn').style.display = 'inline-block'; document.getElementById('logoutBtn').style.display = 'inline-block'; } else { // User is not logged in, show admin login button document.getElementById('adminLoginBtn').style.display = 'inline-block'; document.getElementById('dashboardBtn').style.display = 'none'; document.getElementById('logoutBtn').style.display = 'none'; } } catch (error) { console.error('Error checking auth:', error); // Fallback: show login button if auth check fails document.getElementById('adminLoginBtn').style.display = 'inline-block'; document.getElementById('dashboardBtn').style.display = 'none'; document.getElementById('logoutBtn').style.display = 'none'; } } // Logout function async function logout() { try { const { error } = await supabase.auth.signOut(); if (error) { console.error('Error logging out:', error); } else { window.location.reload(); } } catch (error) { console.error('Error during logout:', error); window.location.reload(); } } // Load locations from database async function loadLocations() { try { const response = await fetch('/api/v1/public/locations'); if (!response.ok) { throw new Error('Failed to fetch locations'); } const responseData = await response.json(); const locations = responseData.data || responseData; // Handle both formats const locationSelect = document.getElementById('locationSelect'); // Store locations globally for distance calculations locationsData = locations; // Clear existing options and set default placeholder locationSelect.innerHTML = ''; // Add locations from database locations.forEach(location => { const option = document.createElement('option'); option.value = location.name; option.textContent = `📍 ${location.name}`; locationSelect.appendChild(option); }); } catch (error) { console.error('Error loading locations:', error); } } // Calculate distance between two points using Haversine formula function calculateDistance(lat1, lon1, lat2, lon2) { const R = 6371; // Earth's radius in kilometers const dLat = toRadians(lat2 - lat1); const dLon = toRadians(lon2 - lon1); const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(toRadians(lat1)) * Math.cos(toRadians(lat2)) * Math.sin(dLon / 2) * Math.sin(dLon / 2); const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); const distance = R * c; // Distance in kilometers return distance; } function toRadians(degrees) { return degrees * (Math.PI / 180); } // Find nearest location based on user's current position async function findNearestLocation() { const btn = document.getElementById('findLocationBtn'); const locationSelect = document.getElementById('locationSelect'); // Check if geolocation is supported if (!navigator.geolocation) { showLocationError('Geolocation wird von diesem Browser nicht unterstützt.'); return; } // Update button state to loading btn.disabled = true; btn.classList.add('loading'); btn.textContent = '🔍 Suche...'; try { // Get user's current position const position = await new Promise((resolve, reject) => { navigator.geolocation.getCurrentPosition( resolve, reject, { enableHighAccuracy: true, timeout: 10000, maximumAge: 300000 // 5 minutes } ); }); const userLat = position.coords.latitude; const userLon = position.coords.longitude; // Calculate distances to all locations const locationsWithDistance = locationsData.map(location => ({ ...location, distance: calculateDistance( userLat, userLon, parseFloat(location.latitude), parseFloat(location.longitude) ) })); // Find the nearest location const nearestLocation = locationsWithDistance.reduce((nearest, current) => { return current.distance < nearest.distance ? current : nearest; }); // Select the nearest location in the dropdown locationSelect.value = nearestLocation.name; // Trigger change event to update the leaderboard locationSelect.dispatchEvent(new Event('change')); // Show success notification showLocationSuccess(nearestLocation.name, nearestLocation.distance); } catch (error) { console.error('Error getting location:', error); let errorMessage = 'Standort konnte nicht ermittelt werden.'; if (error.code) { switch(error.code) { case error.PERMISSION_DENIED: errorMessage = 'Standortzugriff wurde verweigert. Bitte erlaube den Standortzugriff in den Browser-Einstellungen.'; break; case error.POSITION_UNAVAILABLE: errorMessage = 'Standortinformationen sind nicht verfügbar.'; break; case error.TIMEOUT: errorMessage = 'Zeitüberschreitung beim Abrufen des Standorts.'; break; } } showLocationError(errorMessage); } finally { // Reset button state btn.disabled = false; btn.classList.remove('loading'); btn.textContent = '📍 Mein Standort'; } } // Show success notification for location finding function showLocationSuccess(locationName, distance) { const notificationBubble = document.getElementById('notificationBubble'); const notificationTitle = document.getElementById('notificationTitle'); const notificationSubtitle = document.getElementById('notificationSubtitle'); // Update notification content notificationTitle.textContent = `📍 Standort gefunden!`; notificationSubtitle.textContent = `${locationName} (${distance.toFixed(1)} km entfernt)`; // Show notification notificationBubble.classList.remove('hide'); notificationBubble.classList.add('show'); // Auto-hide after 4 seconds setTimeout(() => { hideNotification(); }, 4000); } // Show error notification for location finding function showLocationError(message) { const notificationBubble = document.getElementById('notificationBubble'); const notificationTitle = document.getElementById('notificationTitle'); const notificationSubtitle = document.getElementById('notificationSubtitle'); // Change notification style to error notificationBubble.style.background = 'linear-gradient(135deg, #dc3545, #c82333)'; // Update notification content notificationTitle.textContent = '❌ Fehler'; notificationSubtitle.textContent = message; // Show notification notificationBubble.classList.remove('hide'); notificationBubble.classList.add('show'); // Auto-hide after 6 seconds setTimeout(() => { hideNotification(); // Reset notification style notificationBubble.style.background = 'linear-gradient(135deg, #00d4ff, #0891b2)'; }, 6000); } // Show prompt when no location is selected function showLocationSelectionPrompt() { const rankingList = document.getElementById('rankingList'); rankingList.innerHTML = `