🔧 Fix settings modal: Load current user preferences
- Added show_in_leaderboard to user-player API response - Improved loadSettings() function with better error handling - Added console logging for debugging - Settings modal now shows current user preference instead of always 'off' - Fixed dependency on currentPlayerId (now uses currentUser.id directly)
This commit is contained in:
@@ -13,7 +13,7 @@ async function initDashboard() {
|
|||||||
try {
|
try {
|
||||||
// Get current session
|
// Get current session
|
||||||
const { data: { session }, error } = await supabase.auth.getSession();
|
const { data: { session }, error } = await supabase.auth.getSession();
|
||||||
|
|
||||||
if (error) {
|
if (error) {
|
||||||
console.error('Error checking authentication:', error);
|
console.error('Error checking authentication:', error);
|
||||||
// Temporarily show dashboard for testing
|
// Temporarily show dashboard for testing
|
||||||
@@ -42,10 +42,10 @@ async function initDashboard() {
|
|||||||
displayUserInfo({ email: 'admin@speedrun-arena.com' });
|
displayUserInfo({ email: 'admin@speedrun-arena.com' });
|
||||||
}
|
}
|
||||||
showDashboard();
|
showDashboard();
|
||||||
|
|
||||||
// Load times section
|
// Load times section
|
||||||
checkLinkStatusAndLoadTimes();
|
checkLinkStatusAndLoadTimes();
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('An unexpected error occurred:', error);
|
console.error('An unexpected error occurred:', error);
|
||||||
// window.location.href = '/login';
|
// window.location.href = '/login';
|
||||||
@@ -56,7 +56,7 @@ async function initDashboard() {
|
|||||||
function displayUserInfo(user) {
|
function displayUserInfo(user) {
|
||||||
const userEmail = document.getElementById('userEmail');
|
const userEmail = document.getElementById('userEmail');
|
||||||
const userAvatar = document.getElementById('userAvatar');
|
const userAvatar = document.getElementById('userAvatar');
|
||||||
|
|
||||||
userEmail.textContent = user.email;
|
userEmail.textContent = user.email;
|
||||||
userAvatar.textContent = user.email.charAt(0).toUpperCase();
|
userAvatar.textContent = user.email.charAt(0).toUpperCase();
|
||||||
}
|
}
|
||||||
@@ -106,7 +106,7 @@ function closeModal(modalId) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Close modal when clicking outside
|
// Close modal when clicking outside
|
||||||
window.onclick = function(event) {
|
window.onclick = function (event) {
|
||||||
if (event.target.classList.contains('modal')) {
|
if (event.target.classList.contains('modal')) {
|
||||||
closeModal(event.target.id);
|
closeModal(event.target.id);
|
||||||
}
|
}
|
||||||
@@ -133,12 +133,12 @@ async function checkLinkStatusAndLoadTimes() {
|
|||||||
try {
|
try {
|
||||||
// Check if user has a linked player
|
// Check if user has a linked player
|
||||||
const response = await fetch(`/api/v1/public/user-player/${currentUser.id}?t=${Date.now()}`);
|
const response = await fetch(`/api/v1/public/user-player/${currentUser.id}?t=${Date.now()}`);
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
// User is linked, load times
|
// User is linked, load times
|
||||||
await loadUserTimesSection(result.data);
|
await loadUserTimesSection(result.data);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// User is not linked
|
// User is not linked
|
||||||
showTimesNotLinked();
|
showTimesNotLinked();
|
||||||
@@ -153,36 +153,36 @@ async function checkLinkStatusAndLoadTimes() {
|
|||||||
async function startQRScanner() {
|
async function startQRScanner() {
|
||||||
try {
|
try {
|
||||||
// Request camera access
|
// Request camera access
|
||||||
qrStream = await navigator.mediaDevices.getUserMedia({
|
qrStream = await navigator.mediaDevices.getUserMedia({
|
||||||
video: {
|
video: {
|
||||||
facingMode: 'environment', // Use back camera if available
|
facingMode: 'environment', // Use back camera if available
|
||||||
width: { ideal: 1280 },
|
width: { ideal: 1280 },
|
||||||
height: { ideal: 720 }
|
height: { ideal: 720 }
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
const video = document.getElementById('qrVideo');
|
const video = document.getElementById('qrVideo');
|
||||||
const canvas = document.getElementById('qrCanvas');
|
const canvas = document.getElementById('qrCanvas');
|
||||||
const context = canvas.getContext('2d');
|
const context = canvas.getContext('2d');
|
||||||
|
|
||||||
video.srcObject = qrStream;
|
video.srcObject = qrStream;
|
||||||
video.play();
|
video.play();
|
||||||
|
|
||||||
// Show camera container and update buttons
|
// Show camera container and update buttons
|
||||||
document.getElementById('cameraContainer').style.display = 'block';
|
document.getElementById('cameraContainer').style.display = 'block';
|
||||||
document.getElementById('startScanBtn').style.display = 'none';
|
document.getElementById('startScanBtn').style.display = 'none';
|
||||||
document.getElementById('stopScanBtn').style.display = 'inline-block';
|
document.getElementById('stopScanBtn').style.display = 'inline-block';
|
||||||
document.getElementById('scanningStatus').style.display = 'block';
|
document.getElementById('scanningStatus').style.display = 'block';
|
||||||
|
|
||||||
qrScanning = true;
|
qrScanning = true;
|
||||||
|
|
||||||
// Start scanning loop
|
// Start scanning loop
|
||||||
video.addEventListener('loadedmetadata', () => {
|
video.addEventListener('loadedmetadata', () => {
|
||||||
canvas.width = video.videoWidth;
|
canvas.width = video.videoWidth;
|
||||||
canvas.height = video.videoHeight;
|
canvas.height = video.videoHeight;
|
||||||
scanQRCode();
|
scanQRCode();
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error accessing camera:', error);
|
console.error('Error accessing camera:', error);
|
||||||
showMessage('rfidMessage', 'Kamera-Zugriff fehlgeschlagen. Bitte verwende die manuelle Eingabe.', 'error');
|
showMessage('rfidMessage', 'Kamera-Zugriff fehlgeschlagen. Bitte verwende die manuelle Eingabe.', 'error');
|
||||||
@@ -192,12 +192,12 @@ async function startQRScanner() {
|
|||||||
// Stop QR Scanner
|
// Stop QR Scanner
|
||||||
function stopQRScanner() {
|
function stopQRScanner() {
|
||||||
qrScanning = false;
|
qrScanning = false;
|
||||||
|
|
||||||
if (qrStream) {
|
if (qrStream) {
|
||||||
qrStream.getTracks().forEach(track => track.stop());
|
qrStream.getTracks().forEach(track => track.stop());
|
||||||
qrStream = null;
|
qrStream = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset UI
|
// Reset UI
|
||||||
document.getElementById('cameraContainer').style.display = 'none';
|
document.getElementById('cameraContainer').style.display = 'none';
|
||||||
document.getElementById('startScanBtn').style.display = 'inline-block';
|
document.getElementById('startScanBtn').style.display = 'inline-block';
|
||||||
@@ -208,26 +208,26 @@ function stopQRScanner() {
|
|||||||
// Scan QR Code from video stream
|
// Scan QR Code from video stream
|
||||||
function scanQRCode() {
|
function scanQRCode() {
|
||||||
if (!qrScanning) return;
|
if (!qrScanning) return;
|
||||||
|
|
||||||
const video = document.getElementById('qrVideo');
|
const video = document.getElementById('qrVideo');
|
||||||
const canvas = document.getElementById('qrCanvas');
|
const canvas = document.getElementById('qrCanvas');
|
||||||
const context = canvas.getContext('2d');
|
const context = canvas.getContext('2d');
|
||||||
|
|
||||||
if (video.readyState === video.HAVE_ENOUGH_DATA) {
|
if (video.readyState === video.HAVE_ENOUGH_DATA) {
|
||||||
canvas.width = video.videoWidth;
|
canvas.width = video.videoWidth;
|
||||||
canvas.height = video.videoHeight;
|
canvas.height = video.videoHeight;
|
||||||
context.drawImage(video, 0, 0, canvas.width, canvas.height);
|
context.drawImage(video, 0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
const imageData = context.getImageData(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);
|
const code = jsQR(imageData.data, imageData.width, imageData.height);
|
||||||
|
|
||||||
if (code) {
|
if (code) {
|
||||||
console.log('QR Code detected:', code.data);
|
console.log('QR Code detected:', code.data);
|
||||||
handleQRCodeDetected(code.data);
|
handleQRCodeDetected(code.data);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Continue scanning
|
// Continue scanning
|
||||||
if (qrScanning) {
|
if (qrScanning) {
|
||||||
requestAnimationFrame(scanQRCode);
|
requestAnimationFrame(scanQRCode);
|
||||||
@@ -238,7 +238,7 @@ function scanQRCode() {
|
|||||||
function formatRfidUid(rawUid) {
|
function formatRfidUid(rawUid) {
|
||||||
// Remove any existing formatting (spaces, colons, etc.)
|
// Remove any existing formatting (spaces, colons, etc.)
|
||||||
let cleanUid = rawUid.replace(/[^a-fA-F0-9]/g, '').toUpperCase();
|
let cleanUid = rawUid.replace(/[^a-fA-F0-9]/g, '').toUpperCase();
|
||||||
|
|
||||||
// Handle different UID lengths
|
// Handle different UID lengths
|
||||||
if (cleanUid.length === 6) {
|
if (cleanUid.length === 6) {
|
||||||
// Pad 6-digit UID to 8 digits by adding leading zeros
|
// Pad 6-digit UID to 8 digits by adding leading zeros
|
||||||
@@ -251,7 +251,7 @@ function formatRfidUid(rawUid) {
|
|||||||
} else {
|
} else {
|
||||||
throw new Error(`Ungültige RFID UID Länge: ${cleanUid.length} Zeichen (unterstützt: 6-8)`);
|
throw new Error(`Ungültige RFID UID Länge: ${cleanUid.length} Zeichen (unterstützt: 6-8)`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format as XX:XX:XX:XX
|
// Format as XX:XX:XX:XX
|
||||||
return cleanUid.match(/.{2}/g).join(':');
|
return cleanUid.match(/.{2}/g).join(':');
|
||||||
}
|
}
|
||||||
@@ -259,24 +259,24 @@ function formatRfidUid(rawUid) {
|
|||||||
// Handle detected QR code
|
// Handle detected QR code
|
||||||
async function handleQRCodeDetected(qrData) {
|
async function handleQRCodeDetected(qrData) {
|
||||||
stopQRScanner();
|
stopQRScanner();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Extract and format RFID UID from QR code
|
// Extract and format RFID UID from QR code
|
||||||
const rawUid = qrData.trim();
|
const rawUid = qrData.trim();
|
||||||
|
|
||||||
if (!rawUid) {
|
if (!rawUid) {
|
||||||
showMessage('rfidMessage', 'QR-Code enthält keine gültige RFID UID', 'error');
|
showMessage('rfidMessage', 'QR-Code enthält keine gültige RFID UID', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format the UID to match database format (XX:XX:XX:XX)
|
// Format the UID to match database format (XX:XX:XX:XX)
|
||||||
const formattedUid = formatRfidUid(rawUid);
|
const formattedUid = formatRfidUid(rawUid);
|
||||||
|
|
||||||
showMessage('rfidMessage', `QR-Code erkannt: ${rawUid} → ${formattedUid}`, 'info');
|
showMessage('rfidMessage', `QR-Code erkannt: ${rawUid} → ${formattedUid}`, 'info');
|
||||||
|
|
||||||
// Link the user using the formatted RFID UID
|
// Link the user using the formatted RFID UID
|
||||||
await linkUserByRfidUid(formattedUid);
|
await linkUserByRfidUid(formattedUid);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error formatting RFID UID:', error);
|
console.error('Error formatting RFID UID:', error);
|
||||||
showMessage('rfidMessage', `Fehler beim Formatieren der RFID UID: ${error.message}`, 'error');
|
showMessage('rfidMessage', `Fehler beim Formatieren der RFID UID: ${error.message}`, 'error');
|
||||||
@@ -286,20 +286,20 @@ async function handleQRCodeDetected(qrData) {
|
|||||||
// Manual RFID linking
|
// Manual RFID linking
|
||||||
async function linkManualRfid() {
|
async function linkManualRfid() {
|
||||||
const rawUid = document.getElementById('manualRfidInput').value.trim();
|
const rawUid = document.getElementById('manualRfidInput').value.trim();
|
||||||
|
|
||||||
if (!rawUid) {
|
if (!rawUid) {
|
||||||
showMessage('rfidMessage', 'Bitte gib eine RFID UID ein', 'error');
|
showMessage('rfidMessage', 'Bitte gib eine RFID UID ein', 'error');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Format the UID to match database format
|
// Format the UID to match database format
|
||||||
const formattedUid = formatRfidUid(rawUid);
|
const formattedUid = formatRfidUid(rawUid);
|
||||||
|
|
||||||
showMessage('rfidMessage', `Formatiert: ${rawUid} → ${formattedUid}`, 'info');
|
showMessage('rfidMessage', `Formatiert: ${rawUid} → ${formattedUid}`, 'info');
|
||||||
|
|
||||||
await linkUserByRfidUid(formattedUid);
|
await linkUserByRfidUid(formattedUid);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error formatting manual RFID UID:', error);
|
console.error('Error formatting manual RFID UID:', error);
|
||||||
showMessage('rfidMessage', `Fehler beim Formatieren: ${error.message}`, 'error');
|
showMessage('rfidMessage', `Fehler beim Formatieren: ${error.message}`, 'error');
|
||||||
@@ -327,7 +327,7 @@ async function linkUserByRfidUid(rfidUid) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
showMessage('rfidMessage', `✅ RFID erfolgreich verknüpft!\nSpieler: ${result.data.firstname} ${result.data.lastname}`, 'success');
|
showMessage('rfidMessage', `✅ RFID erfolgreich verknüpft!\nSpieler: ${result.data.firstname} ${result.data.lastname}`, 'success');
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
@@ -365,27 +365,27 @@ async function loadUserTimesSection(playerData) {
|
|||||||
try {
|
try {
|
||||||
const response = await fetch(`/api/v1/public/user-times/${currentUser.id}?t=${Date.now()}`);
|
const response = await fetch(`/api/v1/public/user-times/${currentUser.id}?t=${Date.now()}`);
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
|
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error(result.message || 'Failed to load times');
|
throw new Error(result.message || 'Failed to load times');
|
||||||
}
|
}
|
||||||
|
|
||||||
const times = result.data || result;
|
const times = result.data || result;
|
||||||
|
|
||||||
// Update stats
|
// Update stats
|
||||||
updateTimesStats(times, playerData);
|
updateTimesStats(times, playerData);
|
||||||
|
|
||||||
// Display times
|
// Display times
|
||||||
displayUserTimes(times);
|
displayUserTimes(times);
|
||||||
|
|
||||||
// Show the times display
|
// Show the times display
|
||||||
document.getElementById('timesLoading').style.display = 'none';
|
document.getElementById('timesLoading').style.display = 'none';
|
||||||
document.getElementById('timesNotLinked').style.display = 'none';
|
document.getElementById('timesNotLinked').style.display = 'none';
|
||||||
document.getElementById('timesDisplay').style.display = 'block';
|
document.getElementById('timesDisplay').style.display = 'block';
|
||||||
|
|
||||||
// Initialize achievements for this player
|
// Initialize achievements for this player
|
||||||
initializeAchievements(playerData.id);
|
initializeAchievements(playerData.id);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading user times:', error);
|
console.error('Error loading user times:', error);
|
||||||
showTimesNotLinked();
|
showTimesNotLinked();
|
||||||
@@ -396,7 +396,7 @@ async function loadUserTimesSection(playerData) {
|
|||||||
function updateTimesStats(times, playerData) {
|
function updateTimesStats(times, playerData) {
|
||||||
// Total runs
|
// Total runs
|
||||||
document.getElementById('totalRuns').textContent = times.length;
|
document.getElementById('totalRuns').textContent = times.length;
|
||||||
|
|
||||||
// Best time
|
// Best time
|
||||||
if (times.length > 0) {
|
if (times.length > 0) {
|
||||||
const bestTimeValue = times.reduce((best, current) => {
|
const bestTimeValue = times.reduce((best, current) => {
|
||||||
@@ -408,11 +408,11 @@ function updateTimesStats(times, playerData) {
|
|||||||
} else {
|
} else {
|
||||||
document.getElementById('bestTime').textContent = '--:--';
|
document.getElementById('bestTime').textContent = '--:--';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Unique locations count
|
// Unique locations count
|
||||||
const uniqueLocations = [...new Set(times.map(time => time.location_name))];
|
const uniqueLocations = [...new Set(times.map(time => time.location_name))];
|
||||||
document.getElementById('locationsCount').textContent = uniqueLocations.length;
|
document.getElementById('locationsCount').textContent = uniqueLocations.length;
|
||||||
|
|
||||||
// Linked player name
|
// Linked player name
|
||||||
document.getElementById('linkedPlayer').textContent = `${playerData.firstname} ${playerData.lastname}`;
|
document.getElementById('linkedPlayer').textContent = `${playerData.firstname} ${playerData.lastname}`;
|
||||||
}
|
}
|
||||||
@@ -420,7 +420,7 @@ function updateTimesStats(times, playerData) {
|
|||||||
// Display user times in grid
|
// Display user times in grid
|
||||||
function displayUserTimes(times) {
|
function displayUserTimes(times) {
|
||||||
const timesGrid = document.getElementById('userTimesGrid');
|
const timesGrid = document.getElementById('userTimesGrid');
|
||||||
|
|
||||||
if (times.length === 0) {
|
if (times.length === 0) {
|
||||||
timesGrid.innerHTML = `
|
timesGrid.innerHTML = `
|
||||||
<div style="grid-column: 1 / -1; text-align: center; padding: 3rem; color: #8892b0;">
|
<div style="grid-column: 1 / -1; text-align: center; padding: 3rem; color: #8892b0;">
|
||||||
@@ -454,7 +454,7 @@ function displayUserTimes(times) {
|
|||||||
const allRunsHtml = sortedTimes.map((run, runIndex) => {
|
const allRunsHtml = sortedTimes.map((run, runIndex) => {
|
||||||
let rankBadge = '';
|
let rankBadge = '';
|
||||||
let rankClass = '';
|
let rankClass = '';
|
||||||
|
|
||||||
if (runIndex === 0) {
|
if (runIndex === 0) {
|
||||||
rankBadge = '🥇 Beste';
|
rankBadge = '🥇 Beste';
|
||||||
rankClass = 'best';
|
rankClass = 'best';
|
||||||
@@ -512,14 +512,14 @@ function displayUserTimes(times) {
|
|||||||
// Toggle time card expansion
|
// Toggle time card expansion
|
||||||
function toggleTimeCard(cardElement) {
|
function toggleTimeCard(cardElement) {
|
||||||
const isExpanded = cardElement.classList.contains('expanded');
|
const isExpanded = cardElement.classList.contains('expanded');
|
||||||
|
|
||||||
// Close all other cards first
|
// Close all other cards first
|
||||||
document.querySelectorAll('.user-time-card.expanded').forEach(card => {
|
document.querySelectorAll('.user-time-card.expanded').forEach(card => {
|
||||||
if (card !== cardElement) {
|
if (card !== cardElement) {
|
||||||
card.classList.remove('expanded');
|
card.classList.remove('expanded');
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Toggle current card
|
// Toggle current card
|
||||||
if (isExpanded) {
|
if (isExpanded) {
|
||||||
cardElement.classList.remove('expanded');
|
cardElement.classList.remove('expanded');
|
||||||
@@ -552,7 +552,7 @@ function formatTime(interval) {
|
|||||||
const totalSeconds = hours * 3600 + minutes * 60 + seconds;
|
const totalSeconds = hours * 3600 + minutes * 60 + seconds;
|
||||||
return formatSeconds(totalSeconds);
|
return formatSeconds(totalSeconds);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fallback for string format
|
// Fallback for string format
|
||||||
if (typeof interval === 'string') {
|
if (typeof interval === 'string') {
|
||||||
// Parse format like "00:01:23.45"
|
// Parse format like "00:01:23.45"
|
||||||
@@ -565,14 +565,14 @@ function formatTime(interval) {
|
|||||||
return formatSeconds(totalSeconds);
|
return formatSeconds(totalSeconds);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return interval;
|
return interval;
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatSeconds(totalSeconds) {
|
function formatSeconds(totalSeconds) {
|
||||||
const minutes = Math.floor(totalSeconds / 60);
|
const minutes = Math.floor(totalSeconds / 60);
|
||||||
const seconds = (totalSeconds % 60).toFixed(2);
|
const seconds = (totalSeconds % 60).toFixed(2);
|
||||||
|
|
||||||
if (minutes > 0) {
|
if (minutes > 0) {
|
||||||
return `${minutes}:${seconds.padStart(5, '0')}`;
|
return `${minutes}:${seconds.padStart(5, '0')}`;
|
||||||
} else {
|
} else {
|
||||||
@@ -625,7 +625,7 @@ async function loadPlayerAchievements() {
|
|||||||
// Show achievements
|
// Show achievements
|
||||||
displayAchievementStats();
|
displayAchievementStats();
|
||||||
displayAchievements();
|
displayAchievements();
|
||||||
|
|
||||||
// Hide loading state
|
// Hide loading state
|
||||||
document.getElementById('achievementsLoading').style.display = 'none';
|
document.getElementById('achievementsLoading').style.display = 'none';
|
||||||
document.getElementById('achievementStats').style.display = 'flex';
|
document.getElementById('achievementStats').style.display = 'flex';
|
||||||
@@ -656,7 +656,7 @@ function displayAchievementStats() {
|
|||||||
if (!window.achievementStats) return;
|
if (!window.achievementStats) return;
|
||||||
|
|
||||||
const stats = window.achievementStats;
|
const stats = window.achievementStats;
|
||||||
|
|
||||||
document.getElementById('totalPoints').textContent = stats.total_points;
|
document.getElementById('totalPoints').textContent = stats.total_points;
|
||||||
document.getElementById('completedAchievements').textContent = `${stats.completed_achievements}/${stats.total_achievements}`;
|
document.getElementById('completedAchievements').textContent = `${stats.completed_achievements}/${stats.total_achievements}`;
|
||||||
document.getElementById('achievementsToday').textContent = stats.achievements_today;
|
document.getElementById('achievementsToday').textContent = stats.achievements_today;
|
||||||
@@ -666,7 +666,7 @@ function displayAchievementStats() {
|
|||||||
// Display achievements in grid
|
// Display achievements in grid
|
||||||
function displayAchievements() {
|
function displayAchievements() {
|
||||||
const achievementsGrid = document.getElementById('achievementsGrid');
|
const achievementsGrid = document.getElementById('achievementsGrid');
|
||||||
|
|
||||||
if (!window.allAchievements || window.allAchievements.length === 0) {
|
if (!window.allAchievements || window.allAchievements.length === 0) {
|
||||||
achievementsGrid.innerHTML = `
|
achievementsGrid.innerHTML = `
|
||||||
<div class="no-achievements">
|
<div class="no-achievements">
|
||||||
@@ -681,7 +681,7 @@ function displayAchievements() {
|
|||||||
// Filter achievements by category
|
// Filter achievements by category
|
||||||
let filteredAchievements = window.allAchievements;
|
let filteredAchievements = window.allAchievements;
|
||||||
if (currentAchievementCategory !== 'all') {
|
if (currentAchievementCategory !== 'all') {
|
||||||
filteredAchievements = window.allAchievements.filter(achievement =>
|
filteredAchievements = window.allAchievements.filter(achievement =>
|
||||||
achievement.category === currentAchievementCategory
|
achievement.category === currentAchievementCategory
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -691,16 +691,16 @@ function displayAchievements() {
|
|||||||
const isCompleted = achievement.is_completed;
|
const isCompleted = achievement.is_completed;
|
||||||
const progress = achievement.progress || 0;
|
const progress = achievement.progress || 0;
|
||||||
const earnedAt = achievement.earned_at;
|
const earnedAt = achievement.earned_at;
|
||||||
|
|
||||||
// Debug logging
|
// Debug logging
|
||||||
if (achievement.name === 'Tageskönig') {
|
if (achievement.name === 'Tageskönig') {
|
||||||
console.log('Tageskönig Debug:', { isCompleted, progress, earnedAt });
|
console.log('Tageskönig Debug:', { isCompleted, progress, earnedAt });
|
||||||
}
|
}
|
||||||
|
|
||||||
let progressText = '';
|
let progressText = '';
|
||||||
if (isCompleted) {
|
if (isCompleted) {
|
||||||
progressText = earnedAt ?
|
progressText = earnedAt ?
|
||||||
`Erreicht am ${new Date(earnedAt).toLocaleDateString('de-DE')}` :
|
`Erreicht am ${new Date(earnedAt).toLocaleDateString('de-DE')}` :
|
||||||
'Abgeschlossen';
|
'Abgeschlossen';
|
||||||
} else if (progress > 0) {
|
} else if (progress > 0) {
|
||||||
// Show progress for incomplete achievements
|
// Show progress for incomplete achievements
|
||||||
@@ -754,13 +754,13 @@ function getAchievementConditionValue(achievementName) {
|
|||||||
// Show achievement category
|
// Show achievement category
|
||||||
function showAchievementCategory(category) {
|
function showAchievementCategory(category) {
|
||||||
currentAchievementCategory = category;
|
currentAchievementCategory = category;
|
||||||
|
|
||||||
// Update active tab
|
// Update active tab
|
||||||
document.querySelectorAll('.category-tab').forEach(tab => {
|
document.querySelectorAll('.category-tab').forEach(tab => {
|
||||||
tab.classList.remove('active');
|
tab.classList.remove('active');
|
||||||
});
|
});
|
||||||
document.querySelector(`[data-category="${category}"]`).classList.add('active');
|
document.querySelector(`[data-category="${category}"]`).classList.add('active');
|
||||||
|
|
||||||
// Display filtered achievements
|
// Display filtered achievements
|
||||||
displayAchievements();
|
displayAchievements();
|
||||||
}
|
}
|
||||||
@@ -790,7 +790,7 @@ async function checkPlayerAchievements() {
|
|||||||
const response = await fetch(`/api/achievements/check/${currentPlayerId}?t=${Date.now()}`, {
|
const response = await fetch(`/api/achievements/check/${currentPlayerId}?t=${Date.now()}`, {
|
||||||
method: 'POST'
|
method: 'POST'
|
||||||
});
|
});
|
||||||
|
|
||||||
if (response.ok) {
|
if (response.ok) {
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
if (result.data.count > 0) {
|
if (result.data.count > 0) {
|
||||||
@@ -820,10 +820,10 @@ function showAchievementNotification(newAchievements) {
|
|||||||
<button class="notification-close" onclick="this.parentElement.parentElement.remove()">×</button>
|
<button class="notification-close" onclick="this.parentElement.parentElement.remove()">×</button>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
||||||
// Add to page
|
// Add to page
|
||||||
document.body.appendChild(notification);
|
document.body.appendChild(notification);
|
||||||
|
|
||||||
// Auto-remove after 5 seconds
|
// Auto-remove after 5 seconds
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (notification.parentElement) {
|
if (notification.parentElement) {
|
||||||
@@ -848,14 +848,14 @@ function showWebNotification(title, message, icon = '🏆') {
|
|||||||
tag: 'ninjacross-achievement',
|
tag: 'ninjacross-achievement',
|
||||||
requireInteraction: true
|
requireInteraction: true
|
||||||
});
|
});
|
||||||
|
|
||||||
// Auto-close after 10 seconds
|
// Auto-close after 10 seconds
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
notification.close();
|
notification.close();
|
||||||
}, 10000);
|
}, 10000);
|
||||||
|
|
||||||
// Handle click
|
// Handle click
|
||||||
notification.onclick = function() {
|
notification.onclick = function () {
|
||||||
window.focus();
|
window.focus();
|
||||||
notification.close();
|
notification.close();
|
||||||
};
|
};
|
||||||
@@ -867,10 +867,10 @@ async function checkBestTimeNotifications() {
|
|||||||
try {
|
try {
|
||||||
const response = await fetch('/api/v1/public/best-times');
|
const response = await fetch('/api/v1/public/best-times');
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
|
|
||||||
if (result.success && result.data) {
|
if (result.success && result.data) {
|
||||||
const { daily, weekly, monthly } = result.data;
|
const { daily, weekly, monthly } = result.data;
|
||||||
|
|
||||||
// Check if current player has best times
|
// Check if current player has best times
|
||||||
if (currentPlayerId) {
|
if (currentPlayerId) {
|
||||||
if (daily && daily.player_id === currentPlayerId) {
|
if (daily && daily.player_id === currentPlayerId) {
|
||||||
@@ -880,7 +880,7 @@ async function checkBestTimeNotifications() {
|
|||||||
'👑'
|
'👑'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (weekly && weekly.player_id === currentPlayerId) {
|
if (weekly && weekly.player_id === currentPlayerId) {
|
||||||
showWebNotification(
|
showWebNotification(
|
||||||
'🏆 Wochenchampion!',
|
'🏆 Wochenchampion!',
|
||||||
@@ -888,7 +888,7 @@ async function checkBestTimeNotifications() {
|
|||||||
'🏆'
|
'🏆'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (monthly && monthly.player_id === currentPlayerId) {
|
if (monthly && monthly.player_id === currentPlayerId) {
|
||||||
showWebNotification(
|
showWebNotification(
|
||||||
'🏆 Monatsmeister!',
|
'🏆 Monatsmeister!',
|
||||||
@@ -907,10 +907,10 @@ async function checkBestTimeNotifications() {
|
|||||||
async function checkAchievementNotifications() {
|
async function checkAchievementNotifications() {
|
||||||
try {
|
try {
|
||||||
if (!currentPlayerId) return;
|
if (!currentPlayerId) return;
|
||||||
|
|
||||||
const response = await fetch(`/api/achievements/player/${currentPlayerId}?t=${Date.now()}`);
|
const response = await fetch(`/api/achievements/player/${currentPlayerId}?t=${Date.now()}`);
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
|
|
||||||
if (result.success && result.data) {
|
if (result.success && result.data) {
|
||||||
const newAchievements = result.data.filter(achievement => {
|
const newAchievements = result.data.filter(achievement => {
|
||||||
// Check if achievement was earned in the last 5 minutes
|
// Check if achievement was earned in the last 5 minutes
|
||||||
@@ -918,7 +918,7 @@ async function checkAchievementNotifications() {
|
|||||||
const fiveMinutesAgo = new Date(Date.now() - 5 * 60 * 1000);
|
const fiveMinutesAgo = new Date(Date.now() - 5 * 60 * 1000);
|
||||||
return earnedAt > fiveMinutesAgo;
|
return earnedAt > fiveMinutesAgo;
|
||||||
});
|
});
|
||||||
|
|
||||||
if (newAchievements.length > 0) {
|
if (newAchievements.length > 0) {
|
||||||
newAchievements.forEach(achievement => {
|
newAchievements.forEach(achievement => {
|
||||||
showWebNotification(
|
showWebNotification(
|
||||||
@@ -952,21 +952,28 @@ function showSettings() {
|
|||||||
|
|
||||||
async function loadSettings() {
|
async function loadSettings() {
|
||||||
try {
|
try {
|
||||||
if (!currentPlayerId) {
|
if (!currentUser || !currentUser.id) {
|
||||||
console.error('No player ID available');
|
console.error('No user ID available');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load current player settings
|
// Load current player settings using user ID
|
||||||
const response = await fetch(`/api/v1/public/user-player/${currentUser.id}`);
|
const response = await fetch(`/api/v1/public/user-player/${currentUser.id}`);
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
|
|
||||||
if (result.success && result.data) {
|
if (result.success && result.data) {
|
||||||
const showInLeaderboard = result.data.show_in_leaderboard || false;
|
const showInLeaderboard = result.data.show_in_leaderboard || false;
|
||||||
document.getElementById('showInLeaderboard').checked = showInLeaderboard;
|
document.getElementById('showInLeaderboard').checked = showInLeaderboard;
|
||||||
|
console.log('Loaded settings - showInLeaderboard:', showInLeaderboard);
|
||||||
|
} else {
|
||||||
|
console.error('Failed to load player settings:', result.message);
|
||||||
|
// Set default to false if loading fails
|
||||||
|
document.getElementById('showInLeaderboard').checked = false;
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading settings:', error);
|
console.error('Error loading settings:', error);
|
||||||
|
// Set default to false if loading fails
|
||||||
|
document.getElementById('showInLeaderboard').checked = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -981,9 +988,9 @@ async function saveSettings() {
|
|||||||
console.error('No player ID available');
|
console.error('No player ID available');
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const showInLeaderboard = document.getElementById('showInLeaderboard').checked;
|
const showInLeaderboard = document.getElementById('showInLeaderboard').checked;
|
||||||
|
|
||||||
// Update player settings using public endpoint (no API key needed)
|
// Update player settings using public endpoint (no API key needed)
|
||||||
const response = await fetch(`/api/v1/public/update-player-settings`, {
|
const response = await fetch(`/api/v1/public/update-player-settings`, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
@@ -995,9 +1002,9 @@ async function saveSettings() {
|
|||||||
show_in_leaderboard: showInLeaderboard
|
show_in_leaderboard: showInLeaderboard
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
const result = await response.json();
|
const result = await response.json();
|
||||||
|
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
showNotification('Einstellungen erfolgreich gespeichert!', 'success');
|
showNotification('Einstellungen erfolgreich gespeichert!', 'success');
|
||||||
closeModal('settingsModal');
|
closeModal('settingsModal');
|
||||||
@@ -1028,9 +1035,9 @@ function showNotification(message, type = 'info') {
|
|||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
`;
|
`;
|
||||||
notification.textContent = message;
|
notification.textContent = message;
|
||||||
|
|
||||||
document.body.appendChild(notification);
|
document.body.appendChild(notification);
|
||||||
|
|
||||||
// Remove notification after 3 seconds
|
// Remove notification after 3 seconds
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
if (notification.parentNode) {
|
if (notification.parentNode) {
|
||||||
@@ -1039,11 +1046,11 @@ function showNotification(message, type = 'info') {
|
|||||||
}, 3000);
|
}, 3000);
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', function() {
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
// Add cookie settings button functionality
|
// Add cookie settings button functionality
|
||||||
const cookieSettingsBtn = document.getElementById('cookie-settings-footer');
|
const cookieSettingsBtn = document.getElementById('cookie-settings-footer');
|
||||||
if (cookieSettingsBtn) {
|
if (cookieSettingsBtn) {
|
||||||
cookieSettingsBtn.addEventListener('click', function() {
|
cookieSettingsBtn.addEventListener('click', function () {
|
||||||
if (window.cookieConsent) {
|
if (window.cookieConsent) {
|
||||||
window.cookieConsent.resetConsent();
|
window.cookieConsent.resetConsent();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1256,7 +1256,7 @@ router.get('/v1/public/user-player/:supabase_user_id', async (req, res) => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await pool.query(
|
const result = await pool.query(
|
||||||
'SELECT id, firstname, lastname, birthdate, rfiduid FROM players WHERE supabase_user_id = $1',
|
'SELECT id, firstname, lastname, birthdate, rfiduid, show_in_leaderboard FROM players WHERE supabase_user_id = $1',
|
||||||
[supabase_user_id]
|
[supabase_user_id]
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -2915,14 +2915,14 @@ router.get('/achievements/leaderboard', async (req, res) => {
|
|||||||
router.post('/v1/public/update-player-settings', async (req, res) => {
|
router.post('/v1/public/update-player-settings', async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { player_id, show_in_leaderboard } = req.body;
|
const { player_id, show_in_leaderboard } = req.body;
|
||||||
|
|
||||||
if (!player_id) {
|
if (!player_id) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Player ID ist erforderlich'
|
message: 'Player ID ist erforderlich'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update player settings
|
// Update player settings
|
||||||
const updateQuery = `
|
const updateQuery = `
|
||||||
UPDATE players
|
UPDATE players
|
||||||
@@ -2930,22 +2930,22 @@ router.post('/v1/public/update-player-settings', async (req, res) => {
|
|||||||
WHERE id = $2
|
WHERE id = $2
|
||||||
RETURNING id, firstname, lastname, show_in_leaderboard
|
RETURNING id, firstname, lastname, show_in_leaderboard
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const result = await pool.query(updateQuery, [show_in_leaderboard || false, player_id]);
|
const result = await pool.query(updateQuery, [show_in_leaderboard || false, player_id]);
|
||||||
|
|
||||||
if (result.rows.length === 0) {
|
if (result.rows.length === 0) {
|
||||||
return res.status(404).json({
|
return res.status(404).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Spieler nicht gefunden'
|
message: 'Spieler nicht gefunden'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
message: 'Einstellungen erfolgreich aktualisiert',
|
message: 'Einstellungen erfolgreich aktualisiert',
|
||||||
data: result.rows[0]
|
data: result.rows[0]
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error updating player settings:', error);
|
console.error('Error updating player settings:', error);
|
||||||
res.status(500).json({
|
res.status(500).json({
|
||||||
@@ -2959,14 +2959,14 @@ router.post('/v1/public/update-player-settings', async (req, res) => {
|
|||||||
router.post('/v1/private/update-player-settings', requireApiKey, async (req, res) => {
|
router.post('/v1/private/update-player-settings', requireApiKey, async (req, res) => {
|
||||||
try {
|
try {
|
||||||
const { player_id, show_in_leaderboard } = req.body;
|
const { player_id, show_in_leaderboard } = req.body;
|
||||||
|
|
||||||
if (!player_id) {
|
if (!player_id) {
|
||||||
return res.status(400).json({
|
return res.status(400).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Player ID ist erforderlich'
|
message: 'Player ID ist erforderlich'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update player settings
|
// Update player settings
|
||||||
const updateQuery = `
|
const updateQuery = `
|
||||||
UPDATE players
|
UPDATE players
|
||||||
@@ -2974,22 +2974,22 @@ router.post('/v1/private/update-player-settings', requireApiKey, async (req, res
|
|||||||
WHERE id = $2
|
WHERE id = $2
|
||||||
RETURNING id, firstname, lastname, show_in_leaderboard
|
RETURNING id, firstname, lastname, show_in_leaderboard
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const result = await pool.query(updateQuery, [show_in_leaderboard || false, player_id]);
|
const result = await pool.query(updateQuery, [show_in_leaderboard || false, player_id]);
|
||||||
|
|
||||||
if (result.rows.length === 0) {
|
if (result.rows.length === 0) {
|
||||||
return res.status(404).json({
|
return res.status(404).json({
|
||||||
success: false,
|
success: false,
|
||||||
message: 'Spieler nicht gefunden'
|
message: 'Spieler nicht gefunden'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
res.json({
|
res.json({
|
||||||
success: true,
|
success: true,
|
||||||
message: 'Einstellungen erfolgreich aktualisiert',
|
message: 'Einstellungen erfolgreich aktualisiert',
|
||||||
data: result.rows[0]
|
data: result.rows[0]
|
||||||
});
|
});
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error updating player settings:', error);
|
console.error('Error updating player settings:', error);
|
||||||
res.status(500).json({
|
res.status(500).json({
|
||||||
|
|||||||
Reference in New Issue
Block a user