Viel Push und achivements + AGB
This commit is contained in:
239
public/agb.html
Normal file
239
public/agb.html
Normal file
@@ -0,0 +1,239 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Allgemeine Geschäftsbedingungen | NinjaCross</title>
|
||||
<link rel="stylesheet" href="css/dashboard.css">
|
||||
<style>
|
||||
.agb-container {
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background: #1e293b;
|
||||
border-radius: 10px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
margin-top: 20px;
|
||||
color: #e2e8f0;
|
||||
}
|
||||
|
||||
.agb-header {
|
||||
text-align: center;
|
||||
margin-bottom: 30px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 2px solid #334155;
|
||||
}
|
||||
|
||||
.agb-header h1 {
|
||||
color: #00d4ff;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.agb-header .subtitle {
|
||||
color: #94a3b8;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.agb-content {
|
||||
line-height: 1.6;
|
||||
color: #e2e8f0;
|
||||
}
|
||||
|
||||
.agb-section {
|
||||
margin-bottom: 25px;
|
||||
}
|
||||
|
||||
.agb-section h2 {
|
||||
color: #00d4ff;
|
||||
font-size: 20px;
|
||||
margin-bottom: 15px;
|
||||
padding-bottom: 5px;
|
||||
border-bottom: 1px solid #334155;
|
||||
}
|
||||
|
||||
.agb-section h3 {
|
||||
color: #e2e8f0;
|
||||
font-size: 16px;
|
||||
margin-bottom: 10px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.agb-section p {
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.agb-section ul {
|
||||
margin-left: 20px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.agb-section li {
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.highlight-box {
|
||||
background: #0f172a;
|
||||
border-left: 4px solid #00d4ff;
|
||||
padding: 15px;
|
||||
margin: 20px 0;
|
||||
border-radius: 0 5px 5px 0;
|
||||
}
|
||||
|
||||
.warning-box {
|
||||
background: #451a03;
|
||||
border-left: 4px solid #fbbf24;
|
||||
padding: 15px;
|
||||
margin: 20px 0;
|
||||
border-radius: 0 5px 5px 0;
|
||||
}
|
||||
|
||||
.back-button {
|
||||
display: inline-block;
|
||||
background: #00d4ff;
|
||||
color: #0f172a;
|
||||
padding: 12px 24px;
|
||||
text-decoration: none;
|
||||
border-radius: 5px;
|
||||
margin-top: 30px;
|
||||
transition: background-color 0.3s;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.back-button:hover {
|
||||
background: #0891b2;
|
||||
}
|
||||
|
||||
.last-updated {
|
||||
text-align: center;
|
||||
color: #94a3b8;
|
||||
font-style: italic;
|
||||
margin-top: 30px;
|
||||
padding-top: 20px;
|
||||
border-top: 1px solid #334155;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body style="background: #0f172a; min-height: 100vh; padding: 20px;">
|
||||
<div class="agb-container">
|
||||
<div class="agb-header">
|
||||
<h1>Allgemeine Geschäftsbedingungen</h1>
|
||||
<p class="subtitle">NinjaCross Parkour System</p>
|
||||
</div>
|
||||
|
||||
<div class="agb-content">
|
||||
<div class="highlight-box">
|
||||
<strong>Wichtig:</strong> Durch die Nutzung des NinjaCross Systems stimmen Sie zu, dass Ihre Daten
|
||||
(Name, Zeiten, Standorte) im öffentlichen Leaderboard angezeigt werden können.
|
||||
</div>
|
||||
|
||||
<div class="agb-section">
|
||||
<h2>1. Geltungsbereich</h2>
|
||||
<p>Diese Allgemeinen Geschäftsbedingungen (AGB) gelten für die Nutzung des NinjaCross Parkour Systems
|
||||
im Schwimmbad. Mit der Registrierung und Nutzung des Systems erkennen Sie diese AGB als verbindlich an.</p>
|
||||
</div>
|
||||
|
||||
<div class="agb-section">
|
||||
<h2>2. Datenverarbeitung und Datenschutz</h2>
|
||||
|
||||
<h3>2.1 Erhebung von Daten</h3>
|
||||
<p>Wir erheben folgende personenbezogene Daten:</p>
|
||||
<ul>
|
||||
<li>Vor- und Nachname</li>
|
||||
<li>Geburtsdatum (zur Altersberechnung)</li>
|
||||
<li>RFID-Kartennummer</li>
|
||||
<li>Laufzeiten und Standortdaten</li>
|
||||
<li>Zeitstempel der Aktivitäten</li>
|
||||
</ul>
|
||||
|
||||
<h3>2.2 Verwendung der Daten</h3>
|
||||
<p>Ihre Daten werden für folgende Zwecke verwendet:</p>
|
||||
<ul>
|
||||
<li><strong>Leaderboard-Anzeige:</strong> Name, Zeiten und Standorte werden im öffentlichen Leaderboard angezeigt</li>
|
||||
<li><strong>Leistungsauswertung:</strong> Erfassung und Bewertung Ihrer Parkour-Zeiten</li>
|
||||
<li><strong>System-Funktionalität:</strong> Zuordnung von Zeiten zu Ihrem Profil über RFID-Karten</li>
|
||||
<li><strong>Statistiken:</strong> Anonymisierte Auswertungen für Systemverbesserungen</li>
|
||||
</ul>
|
||||
|
||||
<div class="warning-box">
|
||||
<strong>Wichtiger Hinweis:</strong> Durch die Annahme dieser AGB stimmen Sie ausdrücklich zu,
|
||||
dass Ihr Name und Ihre Laufzeiten im öffentlichen Leaderboard sichtbar sind.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="agb-section">
|
||||
<h2>3. Leaderboard und Öffentlichkeit</h2>
|
||||
<p>Das NinjaCross System verfügt über ein öffentlich zugängliches Leaderboard, das folgende Informationen anzeigt:</p>
|
||||
<ul>
|
||||
<li>Vollständiger Name der Teilnehmer</li>
|
||||
<li>Erreichte Laufzeiten</li>
|
||||
<li>Standort der Aktivität</li>
|
||||
<li>Datum und Uhrzeit der Aktivität</li>
|
||||
</ul>
|
||||
|
||||
<p><strong>Durch die Nutzung des Systems erklären Sie sich damit einverstanden, dass diese Daten öffentlich angezeigt werden.</strong></p>
|
||||
</div>
|
||||
|
||||
<div class="agb-section">
|
||||
<h2>4. Ihre Rechte</h2>
|
||||
<h3>4.1 Recht auf Auskunft</h3>
|
||||
<p>Sie haben das Recht, Auskunft über die zu Ihrer Person gespeicherten Daten zu verlangen.</p>
|
||||
|
||||
<h3>4.2 Recht auf Löschung</h3>
|
||||
<p>Sie können jederzeit die Löschung Ihrer Daten und Ihres Profils beantragen.</p>
|
||||
|
||||
<h3>4.3 Recht auf Widerspruch</h3>
|
||||
<p>Sie können der Verarbeitung Ihrer Daten für das Leaderboard widersprechen.
|
||||
In diesem Fall werden Ihre Daten aus dem öffentlichen Leaderboard entfernt,
|
||||
aber weiterhin für die Systemfunktionalität verwendet.</p>
|
||||
</div>
|
||||
|
||||
<div class="agb-section">
|
||||
<h2>5. Haftung und Verantwortung</h2>
|
||||
<p>Die Teilnahme am NinjaCross System erfolgt auf eigene Gefahr. Wir haften nicht für:</p>
|
||||
<ul>
|
||||
<li>Verletzungen während der Nutzung der Parkour-Anlage</li>
|
||||
<li>Verlust oder Diebstahl der RFID-Karte</li>
|
||||
<li>Technische Ausfälle des Systems</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="agb-section">
|
||||
<h2>6. Systemregeln</h2>
|
||||
<p>Bei der Nutzung des Systems sind folgende Regeln zu beachten:</p>
|
||||
<ul>
|
||||
<li>Keine Manipulation der Zeiterfassung</li>
|
||||
<li>Respektvoller Umgang mit anderen Teilnehmern</li>
|
||||
<li>Beachtung der Sicherheitshinweise der Anlage</li>
|
||||
<li>Keine Verwendung falscher Identitäten</li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="agb-section">
|
||||
<h2>7. Änderungen der AGB</h2>
|
||||
<p>Wir behalten uns vor, diese AGB zu ändern. Wesentliche Änderungen werden Ihnen
|
||||
mitgeteilt und erfordern Ihre erneute Zustimmung.</p>
|
||||
</div>
|
||||
|
||||
<div class="agb-section">
|
||||
<h2>8. Kontakt</h2>
|
||||
<p>Bei Fragen zu diesen AGB oder zum Datenschutz wenden Sie sich an:</p>
|
||||
<p>
|
||||
<strong>NinjaCross Team</strong><br>
|
||||
Schwimmbad Ulm<br>
|
||||
E-Mail: info@ninjacross.de<br>
|
||||
Telefon: 0731-123456
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style="text-align: center;">
|
||||
<a href="javascript:history.back()" class="back-button">Zurück</a>
|
||||
</div>
|
||||
|
||||
<div class="last-updated">
|
||||
<p>Stand: September 2024</p>
|
||||
<p>Diese AGB sind Teil der Registrierung und gelten ab dem Zeitpunkt der Zustimmung.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -703,6 +703,21 @@
|
||||
<input type="date" id="playerBirthdate" class="form-input" style="text-align: center;">
|
||||
</div>
|
||||
|
||||
<!-- AGB Section -->
|
||||
<div class="agb-section" style="background: #1e293b; border: 1px solid #334155; border-radius: 8px; padding: 15px; margin: 15px 0;">
|
||||
<div class="agb-checkbox" style="display: flex; align-items: flex-start; gap: 10px; margin-bottom: 10px;">
|
||||
<input type="checkbox" id="agbAccepted" name="agbAccepted" required style="width: auto; margin: 0; margin-top: 3px;">
|
||||
<label for="agbAccepted" style="color: #e2e8f0; font-size: 0.85rem; line-height: 1.4; margin: 0; font-weight: normal;">
|
||||
Ich habe die <a href="/agb.html" target="_blank" style="color: #00d4ff; text-decoration: none; font-weight: bold;">Allgemeinen Geschäftsbedingungen</a>
|
||||
gelesen und stimme zu, dass mein Name und meine Laufzeiten im öffentlichen Leaderboard angezeigt werden.
|
||||
</label>
|
||||
</div>
|
||||
<div class="agb-warning" style="color: #fbbf24; font-size: 0.8rem; margin-top: 10px;">
|
||||
⚠️ <strong>Wichtig:</strong> Ohne Zustimmung zu den AGB können Sie das System nutzen,
|
||||
aber Ihre Zeiten werden nicht im öffentlichen Leaderboard angezeigt.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary" onclick="createRfidPlayerRecord()" style="width: 100%;" data-de="Spieler erstellen" data-en="Create Player">
|
||||
Spieler erstellen
|
||||
</button>
|
||||
@@ -768,6 +783,6 @@
|
||||
</footer>
|
||||
|
||||
<script src="/js/cookie-consent.js"></script>
|
||||
<script src="/js/dashboard.js?v=1.1"></script>
|
||||
<script src="/js/dashboard.js?v=1.6"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -400,7 +400,7 @@ function filterData() {
|
||||
displayAchievements();
|
||||
} else {
|
||||
currentPlayers = filteredData;
|
||||
displayPlayers();
|
||||
displayPlayersWithAchievements();
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -427,7 +427,7 @@ function refreshData() {
|
||||
if (currentAchievementMode === 'achievements') {
|
||||
loadAchievements();
|
||||
} else {
|
||||
loadPlayers();
|
||||
loadPlayersWithAchievements();
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1372,7 +1372,7 @@ async function toggleAchievementMode() {
|
||||
currentAchievementMode = 'players';
|
||||
document.getElementById('dataTitle').textContent = '👥 Spieler-Achievements';
|
||||
document.getElementById('searchInput').placeholder = 'Spieler durchsuchen...';
|
||||
await loadPlayers();
|
||||
await loadPlayersWithAchievements();
|
||||
} else {
|
||||
currentAchievementMode = 'achievements';
|
||||
document.getElementById('dataTitle').textContent = '🏆 Achievement-Verwaltung';
|
||||
@@ -1382,7 +1382,7 @@ async function toggleAchievementMode() {
|
||||
}
|
||||
|
||||
// Load all players with achievement statistics
|
||||
async function loadPlayers() {
|
||||
async function loadPlayersWithAchievements() {
|
||||
try {
|
||||
const response = await fetch('/api/v1/admin/achievements/players');
|
||||
const result = await response.json();
|
||||
@@ -1390,7 +1390,7 @@ async function loadPlayers() {
|
||||
if (result.success) {
|
||||
currentPlayers = result.data;
|
||||
currentData = result.data; // Set for filtering
|
||||
displayPlayers();
|
||||
displayPlayersWithAchievements();
|
||||
} else {
|
||||
showError('Fehler beim Laden der Spieler: ' + result.message);
|
||||
}
|
||||
@@ -1401,7 +1401,7 @@ async function loadPlayers() {
|
||||
}
|
||||
|
||||
// Display players in table
|
||||
function displayPlayers() {
|
||||
function displayPlayersWithAchievements() {
|
||||
const content = document.getElementById('dataContent');
|
||||
|
||||
if (currentPlayers.length === 0) {
|
||||
|
||||
@@ -618,6 +618,7 @@ async function createRfidPlayerRecord() {
|
||||
const firstname = document.getElementById('playerFirstname').value.trim();
|
||||
const lastname = document.getElementById('playerLastname').value.trim();
|
||||
const birthdate = document.getElementById('playerBirthdate').value;
|
||||
const agbAccepted = document.getElementById('agbAccepted').checked;
|
||||
|
||||
// Validation
|
||||
if (!rawUid) {
|
||||
@@ -652,6 +653,14 @@ async function createRfidPlayerRecord() {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!agbAccepted) {
|
||||
const agbErrorMsg = currentLanguage === 'de' ?
|
||||
'Bitte stimme den Allgemeinen Geschäftsbedingungen zu' :
|
||||
'Please accept the Terms of Service';
|
||||
showMessage('rfidMessage', agbErrorMsg, 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Format the UID to match database format
|
||||
const formattedUid = formatRfidUid(rawUid);
|
||||
@@ -672,7 +681,8 @@ async function createRfidPlayerRecord() {
|
||||
firstname: firstname,
|
||||
lastname: lastname,
|
||||
birthdate: birthdate,
|
||||
supabase_user_id: currentUser?.id || null
|
||||
supabase_user_id: currentUser?.id || null,
|
||||
agb_accepted: agbAccepted
|
||||
})
|
||||
});
|
||||
|
||||
@@ -689,6 +699,7 @@ async function createRfidPlayerRecord() {
|
||||
document.getElementById('playerFirstname').value = '';
|
||||
document.getElementById('playerLastname').value = '';
|
||||
document.getElementById('playerBirthdate').value = '';
|
||||
document.getElementById('agbAccepted').checked = false;
|
||||
|
||||
// Hide create player section since user is now linked
|
||||
const createPlayerSection = document.getElementById('createPlayerSection');
|
||||
@@ -1139,7 +1150,7 @@ let currentAchievementCategory = 'all';
|
||||
|
||||
// Load achievements for the current player
|
||||
async function loadPlayerAchievements() {
|
||||
if (!currentPlayerId) {
|
||||
if (!currentPlayerId || currentPlayerId === 'undefined' || currentPlayerId === 'null') {
|
||||
showAchievementsNotAvailable();
|
||||
return;
|
||||
}
|
||||
@@ -1372,7 +1383,7 @@ function showStatistics() {
|
||||
|
||||
async function loadAnalyticsData() {
|
||||
try {
|
||||
if (!currentPlayerId) {
|
||||
if (!currentPlayerId || currentPlayerId === 'undefined' || currentPlayerId === 'null') {
|
||||
console.error('No player ID available - user not linked');
|
||||
// Show fallback data when user is not linked
|
||||
displayAnalyticsFallback();
|
||||
@@ -1400,7 +1411,7 @@ async function loadAnalyticsData() {
|
||||
|
||||
async function loadStatisticsData() {
|
||||
try {
|
||||
if (!currentPlayerId) {
|
||||
if (!currentPlayerId || currentPlayerId === 'undefined' || currentPlayerId === 'null') {
|
||||
console.error('No player ID available - user not linked');
|
||||
// Show fallback data when user is not linked
|
||||
displayStatisticsFallback();
|
||||
@@ -1682,7 +1693,7 @@ function showAchievementsNotAvailable() {
|
||||
|
||||
// Check achievements for current player
|
||||
async function checkPlayerAchievements() {
|
||||
if (!currentPlayerId) return;
|
||||
if (!currentPlayerId || currentPlayerId === 'undefined' || currentPlayerId === 'null') return;
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/achievements/check/${currentPlayerId}?t=${Date.now()}`, {
|
||||
@@ -1738,8 +1749,13 @@ function showAchievementNotification(newAchievements) {
|
||||
|
||||
// Initialize achievements when player is loaded
|
||||
function initializeAchievements(playerId) {
|
||||
currentPlayerId = playerId;
|
||||
loadPlayerAchievements();
|
||||
if (playerId && playerId !== 'undefined' && playerId !== 'null') {
|
||||
currentPlayerId = playerId;
|
||||
loadPlayerAchievements();
|
||||
} else {
|
||||
console.warn('Invalid player ID provided to initializeAchievements:', playerId);
|
||||
currentPlayerId = null;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert VAPID key from base64url to Uint8Array
|
||||
@@ -1764,6 +1780,16 @@ function urlBase64ToUint8Array(base64String) {
|
||||
// Web Notification Functions
|
||||
function showWebNotification(title, message, icon = '🏆') {
|
||||
if ('Notification' in window && Notification.permission === 'granted') {
|
||||
// Log notification details to console
|
||||
console.log('🔔 Web Notification sent:', {
|
||||
title: title,
|
||||
message: message,
|
||||
icon: icon,
|
||||
playerId: currentPlayerId || 'unknown',
|
||||
pushPlayerId: localStorage.getItem('pushPlayerId') || 'unknown',
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
|
||||
const notification = new Notification(title, {
|
||||
body: message,
|
||||
icon: '/pictures/icon-192.png',
|
||||
@@ -1809,7 +1835,7 @@ async function checkBestTimeNotifications() {
|
||||
const { daily, weekly, monthly } = result.data;
|
||||
|
||||
// Check if current player has best times
|
||||
if (currentPlayerId) {
|
||||
if (currentPlayerId && currentPlayerId !== 'undefined' && currentPlayerId !== 'null') {
|
||||
const now = new Date();
|
||||
const isEvening = now.getHours() >= 19;
|
||||
|
||||
@@ -1910,30 +1936,87 @@ async function checkBestTimeNotifications() {
|
||||
// Check for new achievements and show notifications
|
||||
async function checkAchievementNotifications() {
|
||||
try {
|
||||
if (!currentPlayerId) return;
|
||||
|
||||
console.log('🔍 checkAchievementNotifications() called');
|
||||
|
||||
// Check if push notifications are enabled
|
||||
const pushPlayerId = localStorage.getItem('pushPlayerId');
|
||||
if (!pushPlayerId) {
|
||||
console.log('🔕 Push notifications disabled, skipping achievement check');
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('🔍 Push notifications enabled for player:', pushPlayerId);
|
||||
|
||||
const response = await fetch(`/api/achievements/player/${currentPlayerId}?t=${Date.now()}`);
|
||||
// Use pushPlayerId for notifications instead of currentPlayerId
|
||||
if (!pushPlayerId || pushPlayerId === 'undefined' || pushPlayerId === 'null') return;
|
||||
|
||||
const response = await fetch(`/api/achievements/player/${pushPlayerId}?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;
|
||||
console.log('🔍 Checking achievements for notifications:', {
|
||||
totalAchievements: result.data.length,
|
||||
playerId: pushPlayerId
|
||||
});
|
||||
|
||||
const newAchievements = result.data.filter(achievement => {
|
||||
// Only check completed achievements
|
||||
if (!achievement.is_completed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check if achievement was earned in the last 10 minutes (extended window)
|
||||
const earnedAt = achievement.earned_at ? new Date(achievement.earned_at) : null;
|
||||
const tenMinutesAgo = new Date(Date.now() - 10 * 60 * 1000);
|
||||
|
||||
// If no earned_at date, check if it was completed recently by checking completion_count
|
||||
if (!earnedAt) {
|
||||
// Send to server console
|
||||
fetch('/api/v1/public/log', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
level: 'info',
|
||||
message: `🔍 Achievement completed but no earned_at: ${achievement.name} (completion_count: ${achievement.completion_count})`,
|
||||
playerId: pushPlayerId
|
||||
})
|
||||
}).catch(() => {}); // Ignore errors
|
||||
|
||||
// For achievements without earned_at, assume they are new if completion_count is 1
|
||||
// This is a fallback for recently completed achievements
|
||||
return achievement.completion_count === 1;
|
||||
}
|
||||
|
||||
const isNew = earnedAt > tenMinutesAgo;
|
||||
|
||||
// Send detailed info to server console
|
||||
fetch('/api/v1/public/log', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
level: 'info',
|
||||
message: `🔍 Achievement check: ${achievement.name} - earned_at: ${earnedAt.toISOString()}, isNew: ${isNew}, completed: ${achievement.is_completed}`,
|
||||
playerId: pushPlayerId
|
||||
})
|
||||
}).catch(() => {}); // Ignore errors
|
||||
|
||||
return isNew;
|
||||
});
|
||||
|
||||
console.log('🔍 New achievements found:', newAchievements.length);
|
||||
|
||||
if (newAchievements.length > 0) {
|
||||
for (const achievement of newAchievements) {
|
||||
// Check if notification was already sent for this achievement
|
||||
const achievementCheck = await fetch(`/api/v1/public/notification-sent/${currentPlayerId}/achievement?achievementId=${achievement.achievement_id}&locationId=${achievement.location_id || ''}`);
|
||||
const achievementId = achievement.id || achievement.achievement_id;
|
||||
const locationId = achievement.location_id || '';
|
||||
|
||||
if (!achievementId) {
|
||||
console.warn('Achievement ID is missing for achievement:', achievement);
|
||||
continue;
|
||||
}
|
||||
|
||||
const achievementCheck = await fetch(`/api/v1/public/notification-sent/${pushPlayerId}/achievement?achievementId=${achievementId}&locationId=${locationId}`);
|
||||
const achievementResult = await achievementCheck.json();
|
||||
|
||||
if (!achievementResult.wasSent) {
|
||||
@@ -1949,10 +2032,10 @@ async function checkAchievementNotifications() {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
playerId: currentPlayerId,
|
||||
playerId: pushPlayerId,
|
||||
notificationType: 'achievement',
|
||||
achievementId: achievement.achievement_id,
|
||||
locationId: achievement.location_id || null
|
||||
achievementId: achievementId,
|
||||
locationId: locationId || null
|
||||
})
|
||||
});
|
||||
console.log(`🏆 Achievement notification sent: ${achievement.name}`);
|
||||
@@ -2015,7 +2098,7 @@ function updateLeaderboardSetting() {
|
||||
|
||||
async function saveSettings() {
|
||||
try {
|
||||
if (!currentPlayerId) {
|
||||
if (!currentPlayerId || currentPlayerId === 'undefined' || currentPlayerId === 'null') {
|
||||
console.error('No player ID available');
|
||||
return;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user