317 lines
15 KiB
HTML
317 lines
15 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="de">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>SPEEDRUN ARENA - Admin Dashboard</title>
|
|
<link rel="icon" type="image/x-icon" href="/pictures/favicon.ico">
|
|
<link rel="manifest" href="/manifest.json">
|
|
<meta name="apple-mobile-web-app-capable" content="yes">
|
|
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
|
<meta name="apple-mobile-web-app-title" content="Ninja Cross">
|
|
<link rel="apple-touch-icon" href="/pictures/favicon.ico">
|
|
<script src="https://unpkg.com/@supabase/supabase-js@2"></script>
|
|
<!-- QR Code Scanner Library -->
|
|
<script src="https://unpkg.com/jsqr@1.4.0/dist/jsQR.js"></script>
|
|
<link rel="stylesheet" href="/css/dashboard.css">
|
|
|
|
<!-- Notification Permission Script -->
|
|
<script>
|
|
// Register Service Worker for iPhone Notifications
|
|
if ('serviceWorker' in navigator) {
|
|
navigator.serviceWorker.register('/sw.js')
|
|
.then(function(registration) {
|
|
console.log('✅ Service Worker registered:', registration);
|
|
})
|
|
.catch(function(error) {
|
|
console.log('❌ Service Worker registration failed:', error);
|
|
});
|
|
}
|
|
|
|
// Request notification permission on page load
|
|
if ('Notification' in window) {
|
|
if (Notification.permission === 'default') {
|
|
Notification.requestPermission().then(function(permission) {
|
|
if (permission === 'granted') {
|
|
console.log('✅ Notification permission granted');
|
|
// Subscribe to push notifications
|
|
subscribeToPush();
|
|
} else {
|
|
console.log('❌ Notification permission denied');
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
// Subscribe to push notifications
|
|
async function subscribeToPush() {
|
|
try {
|
|
const registration = await navigator.serviceWorker.ready;
|
|
const subscription = await registration.pushManager.subscribe({
|
|
userVisibleOnly: true,
|
|
applicationServerKey: 'BEl62iUYgUivxIkv69yViEuiBIa40HI6F2B5L4h7Q8Y'
|
|
});
|
|
|
|
// Send subscription to server
|
|
await fetch('/api/v1/public/subscribe', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify(subscription)
|
|
});
|
|
|
|
console.log('✅ Push subscription successful');
|
|
} catch (error) {
|
|
console.error('❌ Push subscription failed:', error);
|
|
}
|
|
}
|
|
</script>
|
|
</head>
|
|
<body>
|
|
<div class="main-container">
|
|
<div class="nav-buttons">
|
|
<div class="user-info">
|
|
<div class="user-avatar" id="userAvatar">U</div>
|
|
<span id="userEmail">user@example.com</span>
|
|
</div>
|
|
<a href="/" class="btn btn-primary">Back to Times</a>
|
|
<button class="btn btn-logout" onclick="logout()">Logout</button>
|
|
</div>
|
|
|
|
<div class="header-section">
|
|
<h1 class="main-title">DEIN DASHBOARD</h1>
|
|
<p class="tagline">Verwalte deine Läufe in der NINJACROSS ARENA</p>
|
|
</div>
|
|
<div id="loading" class="loading">
|
|
<div class="spinner"></div>
|
|
<p>Lade dein Dashboard...</p>
|
|
</div>
|
|
|
|
<div id="dashboardContent" style="display: none;">
|
|
<div class="welcome-card">
|
|
<h2>Dein Dashboard 🥷</h2>
|
|
<p>Willkommen in Deinem Dashboard-Panel! Deine übersichtliche Übersicht aller deiner Läufe.</p>
|
|
|
|
</div>
|
|
|
|
<div class="dashboard-grid">
|
|
<div class="card">
|
|
<h3>📊 Analytics</h3>
|
|
<p>Verfolge deine Leistung und überwache wichtige Metriken. Dieser Abschnitt wird detaillierte Analysen anzeigen, sobald wir die Funktion implementieren.</p>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h3>⚡ Quick Actions</h3>
|
|
<p>Hier findest du häufige Aufgaben und Schnellzugriffe. Wir werden Buttons für das Erstellen neuer Aufzeichnungen, das Verwalten von Einstellungen und mehr hinzufügen.</p>
|
|
</div>
|
|
|
|
<div class="card" onclick="showRFIDSettings()" style="cursor: pointer;">
|
|
<h3>🏷️ RFID Verknüpfung</h3>
|
|
<p>Verknüpfe deine RFID-Karte mit deinem Account, um deine Zeiten automatisch zu tracken.</p>
|
|
<button class="btn btn-primary" style="margin-top: 1rem;" onclick="event.stopPropagation(); showRFIDSettings();">RFID verknüpfen</button>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<h3>📊 Statistiken</h3>
|
|
<p>Hier werden bald detaillierte Statistiken zu deinen Läufen angezeigt - beste Zeiten, Verbesserungen und Vergleiche mit anderen Spielern.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- User Times Section -->
|
|
<div class="times-section">
|
|
<div class="times-header">
|
|
<h2>🏃♂️ Meine Zeiten</h2>
|
|
<p>Deine persönlichen Bestzeiten an allen Standorten</p>
|
|
</div>
|
|
|
|
<!-- Loading State -->
|
|
<div id="timesLoading" class="times-loading" style="display: none;">
|
|
<div class="spinner"></div>
|
|
<p>Lade deine Zeiten...</p>
|
|
</div>
|
|
|
|
<!-- Not Linked State -->
|
|
<div id="timesNotLinked" class="times-not-linked">
|
|
<div class="not-linked-content">
|
|
<div class="not-linked-icon">🔗</div>
|
|
<h3>RFID noch nicht verknüpft</h3>
|
|
<p>Um deine persönlichen Zeiten zu sehen, musst du zuerst deine RFID-Karte mit deinem Account verknüpfen.</p>
|
|
<button class="btn btn-primary" onclick="showRFIDSettings()">
|
|
🏷️ RFID jetzt verknüpfen
|
|
</button>
|
|
<div class="link-info">
|
|
<h4>So funktioniert's:</h4>
|
|
<ol>
|
|
<li>Klicke auf "RFID jetzt verknüpfen"</li>
|
|
<li>Scanne den QR-Code auf deiner RFID-Karte</li>
|
|
<li>Fertig! Deine Zeiten werden automatisch hier angezeigt</li>
|
|
</ol>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Times Display -->
|
|
<div id="timesDisplay" style="display: none;">
|
|
<div class="times-stats">
|
|
<div class="stat-card">
|
|
<div class="stat-number" id="totalRuns">0</div>
|
|
<div class="stat-label">Gesamte Läufe</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-number" id="bestTime">--:--</div>
|
|
<div class="stat-label">Beste Zeit</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-number" id="locationsCount">0</div>
|
|
<div class="stat-label">Standorte</div>
|
|
</div>
|
|
<div class="stat-card">
|
|
<div class="stat-number" id="linkedPlayer">--</div>
|
|
<div class="stat-label">Verknüpfter Spieler</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="times-content">
|
|
<div class="times-grid" id="userTimesGrid">
|
|
<!-- Times will be populated here -->
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Achievements Section -->
|
|
<div class="achievements-section">
|
|
<div class="achievements-header">
|
|
<h2>🏆 Meine Achievements</h2>
|
|
<p>Sammele Punkte und erreiche neue Meilensteine!</p>
|
|
</div>
|
|
|
|
<!-- Achievement Stats -->
|
|
<div class="achievement-stats" id="achievementStats" style="display: none;">
|
|
<div class="stat-card achievement-stat">
|
|
<div class="stat-number" id="totalPoints">0</div>
|
|
<div class="stat-label">Gesamtpunkte</div>
|
|
</div>
|
|
<div class="stat-card achievement-stat">
|
|
<div class="stat-number" id="completedAchievements">0</div>
|
|
<div class="stat-label">Abgeschlossen</div>
|
|
</div>
|
|
<div class="stat-card achievement-stat">
|
|
<div class="stat-number" id="achievementsToday">0</div>
|
|
<div class="stat-label">Heute erreicht</div>
|
|
</div>
|
|
<div class="stat-card achievement-stat">
|
|
<div class="stat-number" id="completionPercentage">0%</div>
|
|
<div class="stat-label">Fortschritt</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Achievement Categories -->
|
|
<div class="achievement-categories" id="achievementCategories" style="display: none;">
|
|
<div class="category-tabs">
|
|
<button class="category-tab active" onclick="showAchievementCategory('all')" data-category="all">Alle</button>
|
|
<button class="category-tab" onclick="showAchievementCategory('consistency')" data-category="consistency">Konsistenz</button>
|
|
<button class="category-tab" onclick="showAchievementCategory('improvement')" data-category="improvement">Verbesserung</button>
|
|
<button class="category-tab" onclick="showAchievementCategory('seasonal')" data-category="seasonal">Saisonal</button>
|
|
<button class="category-tab" onclick="showAchievementCategory('monthly')" data-category="monthly">Monatlich</button>
|
|
</div>
|
|
|
|
<div class="achievements-grid" id="achievementsGrid">
|
|
<!-- Achievements will be populated here -->
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Achievement Loading State -->
|
|
<div id="achievementsLoading" class="achievements-loading" style="display: none;">
|
|
<div class="spinner"></div>
|
|
<p>Lade deine Achievements...</p>
|
|
</div>
|
|
|
|
<!-- Achievement Not Available State -->
|
|
<div id="achievementsNotAvailable" class="achievements-not-available" style="display: none;">
|
|
<div class="not-available-content">
|
|
<div class="not-available-icon">🏆</div>
|
|
<h3>Achievements noch nicht verfügbar</h3>
|
|
<p>Um Achievements zu sammeln, musst du zuerst deine RFID-Karte mit deinem Account verknüpfen und einige Läufe absolvieren.</p>
|
|
<button class="btn btn-primary" onclick="showRFIDSettings()">
|
|
🏷️ RFID jetzt verknüpfen
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- RFID Settings Modal -->
|
|
<div id="rfidModal" class="modal">
|
|
<div class="modal-content">
|
|
<div class="modal-header">
|
|
<h2 class="modal-title">📱 RFID QR-Code Scanner</h2>
|
|
<span class="close" onclick="closeModal('rfidModal')">×</span>
|
|
</div>
|
|
<div id="rfidMessage"></div>
|
|
|
|
<!-- QR Scanner Step -->
|
|
<div id="qrScannerStep">
|
|
<p style="color: #8892b0; margin-bottom: 1.5rem; text-align: center;">
|
|
Scanne den QR-Code auf deiner RFID-Karte, um sie mit deinem Account zu verknüpfen.
|
|
</p>
|
|
|
|
<!-- Camera Preview -->
|
|
<div id="cameraContainer" style="display: none;">
|
|
<video id="qrVideo" style="width: 100%; max-width: 400px; border-radius: 0.75rem; margin: 0 auto; display: block;"></video>
|
|
<canvas id="qrCanvas" style="display: none;"></canvas>
|
|
</div>
|
|
|
|
<!-- Scanner Controls -->
|
|
<div style="text-align: center; margin: 1.5rem 0;">
|
|
<button class="btn btn-primary" onclick="startQRScanner()" id="startScanBtn">
|
|
📷 Kamera starten
|
|
</button>
|
|
<button class="btn btn-secondary" onclick="stopQRScanner()" id="stopScanBtn" style="display: none;">
|
|
🛑 Scanner stoppen
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Manual Input Fallback -->
|
|
<div style="border-top: 1px solid #334155; padding-top: 1.5rem; margin-top: 1.5rem;">
|
|
<p style="color: #8892b0; text-align: center; margin-bottom: 1rem; font-size: 0.9rem;">
|
|
Kamera funktioniert nicht? RFID UID manuell eingeben:
|
|
</p>
|
|
<div class="form-group">
|
|
<input type="text" id="manualRfidInput" class="form-input" placeholder="z.B. aaaaaa, FFFFFF oder FF:FF:FF:FF" style="text-align: center; font-family: monospace;">
|
|
</div>
|
|
<button class="btn btn-secondary" onclick="linkManualRfid()" style="width: 100%;">
|
|
Manuell verknüpfen
|
|
</button>
|
|
</div>
|
|
|
|
<!-- Scanning Status -->
|
|
<div id="scanningStatus" style="display: none; text-align: center; color: #00d4ff; margin-top: 1rem;">
|
|
<div class="spinner" style="width: 20px; height: 20px; margin: 0 auto 0.5rem;"></div>
|
|
Suche nach QR-Code...
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- Footer -->
|
|
<footer class="footer">
|
|
<div class="footer-content">
|
|
<div class="footer-links">
|
|
<a href="/impressum.html" class="footer-link">Impressum</a>
|
|
<a href="/datenschutz.html" class="footer-link">Datenschutz</a>
|
|
<button id="cookie-settings-footer" class="footer-link cookie-settings-btn">Cookie-Einstellungen</button>
|
|
</div>
|
|
<div class="footer-text">
|
|
<p>© 2024 NinjaCross. Alle Rechte vorbehalten.</p>
|
|
</div>
|
|
</div>
|
|
</footer>
|
|
|
|
<script src="/js/cookie-consent.js"></script>
|
|
<script src="/js/dashboard.js?v=1.1"></script>
|
|
</body>
|
|
</html>
|