diff --git a/public/index.html b/public/index.html
index b99dbc7..b59c9bb 100644
--- a/public/index.html
+++ b/public/index.html
@@ -100,6 +100,79 @@
box-shadow: 0 0 0 3px rgba(0, 212, 255, 0.1);
}
+ /* Location Control Layout */
+ .location-control {
+ display: flex;
+ gap: 0.75rem;
+ align-items: center;
+ }
+
+ .location-select {
+ flex: 1;
+ }
+
+ .location-btn {
+ padding: 1rem 1.5rem;
+ background: linear-gradient(135deg, #10b981, #059669);
+ border: none;
+ border-radius: 0.75rem;
+ color: white;
+ font-weight: 600;
+ font-size: 0.9rem;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ white-space: nowrap;
+ min-width: 140px;
+ }
+
+ .location-btn:hover {
+ background: linear-gradient(135deg, #059669, #047857);
+ transform: translateY(-1px);
+ box-shadow: 0 4px 12px rgba(16, 185, 129, 0.3);
+ }
+
+ .location-btn:disabled {
+ background: #374151;
+ color: #9ca3af;
+ cursor: not-allowed;
+ transform: none;
+ box-shadow: none;
+ }
+
+ .location-btn.loading {
+ background: linear-gradient(135deg, #6366f1, #4f46e5);
+ position: relative;
+ overflow: hidden;
+ }
+
+ .location-btn.loading::after {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: -100%;
+ width: 100%;
+ height: 100%;
+ background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent);
+ animation: loading-sweep 1.5s infinite;
+ }
+
+ @keyframes loading-sweep {
+ 0% { left: -100%; }
+ 100% { left: 100%; }
+ }
+
+ @media (max-width: 768px) {
+ .location-control {
+ flex-direction: column;
+ gap: 0.5rem;
+ }
+
+ .location-btn {
+ width: 100%;
+ min-width: auto;
+ }
+ }
+
/* Horizontal Time Tabs */
.time-tabs {
display: flex;
@@ -656,10 +729,15 @@
-
+
+
+
+
@@ -831,6 +909,9 @@
}
}
+ // Global variable to store locations with coordinates
+ let locationsData = [];
+
// Load locations from database
async function loadLocations() {
try {
@@ -843,6 +924,9 @@
const locations = responseData.data || responseData; // Handle both formats
const locationSelect = document.getElementById('locationSelect');
+ // Store locations globally for distance calculations
+ locationsData = locations;
+
// Clear existing options except "Alle Standorte"
locationSelect.innerHTML = '';
@@ -859,6 +943,156 @@
}
}
+ // 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);
+ }
+
// Load data from local database via MCP
async function loadData() {
try {