diff --git a/public/admin-dashboard.html b/public/admin-dashboard.html index 7278c5a..e38d7d9 100644 --- a/public/admin-dashboard.html +++ b/public/admin-dashboard.html @@ -4,6 +4,7 @@ Admin Dashboard - NinjaCross + @@ -40,6 +41,48 @@ + +
+

🏠 Hauptseiten-Besuche

+
+
+

Heute

+
Lade...
+
+
+

Diese Woche

+
Lade...
+
+
+

Dieser Monat

+
Lade...
+
+
+

Gesamt

+
Lade...
+
+
+ + + +
+
diff --git a/public/adminlogin.html b/public/adminlogin.html index 87412a1..3742489 100644 --- a/public/adminlogin.html +++ b/public/adminlogin.html @@ -4,6 +4,7 @@ Login - Lizenzgenerator + diff --git a/public/css/admin-dashboard.css b/public/css/admin-dashboard.css index 5903be4..6b89da1 100644 --- a/public/css/admin-dashboard.css +++ b/public/css/admin-dashboard.css @@ -326,6 +326,121 @@ body { margin: 5% auto; width: 95%; } + + .page-stats-grid { + grid-template-columns: 1fr; + } + + .link-stats-grid { + grid-template-columns: repeat(2, 1fr); + } +} + +/* Page Statistics Styles */ +.page-stats-section { + background: white; + border-radius: 10px; + padding: 25px; + margin: 20px 0; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.1); +} + +.page-stats-section h2 { + color: #1e3c72; + margin-bottom: 20px; + font-size: 1.5em; +} + +.page-stats-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(250px, 1fr)); + gap: 20px; + margin-bottom: 30px; +} + +.page-stat-card { + background: #f8f9fa; + border-radius: 8px; + padding: 20px; + border-left: 4px solid #1e3c72; +} + +.page-stat-card h3 { + color: #1e3c72; + margin-bottom: 15px; + font-size: 1.2em; +} + +.page-stats-content { + font-size: 0.9em; + line-height: 1.6; +} + +.page-stats-content .page-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px 0; + border-bottom: 1px solid #e9ecef; +} + +.page-stats-content .page-item:last-child { + border-bottom: none; +} + +.page-stats-content .page-name { + font-weight: 500; + color: #495057; +} + +.page-stats-content .page-count { + background: #1e3c72; + color: white; + padding: 4px 8px; + border-radius: 12px; + font-size: 0.8em; + font-weight: bold; +} + +/* Link Statistics Styles */ +.link-stats-section { + background: #f8f9fa; + border-radius: 8px; + padding: 20px; + margin-top: 20px; +} + +.link-stats-section h3 { + color: #1e3c72; + margin-bottom: 15px; + font-size: 1.3em; +} + +.link-stats-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + gap: 20px; +} + +.link-stat-card { + text-align: center; + background: white; + border-radius: 8px; + padding: 20px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); +} + +.link-stat-number { + font-size: 2em; + font-weight: bold; + color: #1e3c72; + margin-bottom: 8px; +} + +.link-stat-label { + color: #6c757d; + font-size: 0.9em; + font-weight: 500; } @media (max-width: 480px) { diff --git a/public/dashboard.html b/public/dashboard.html index 37e9010..eeb58cc 100644 --- a/public/dashboard.html +++ b/public/dashboard.html @@ -4,6 +4,7 @@ SPEEDRUN ARENA - Admin Dashboard + diff --git a/public/generator.html b/public/generator.html index 216097c..737eb7f 100644 --- a/public/generator.html +++ b/public/generator.html @@ -4,6 +4,7 @@ Lizenzgenerator + diff --git a/public/index.html b/public/index.html index 37a522b..dd4c32e 100644 --- a/public/index.html +++ b/public/index.html @@ -4,6 +4,8 @@ Timer Leaderboard + + diff --git a/public/js/admin-dashboard.js b/public/js/admin-dashboard.js index 0cea831..95bb68f 100644 --- a/public/js/admin-dashboard.js +++ b/public/js/admin-dashboard.js @@ -6,6 +6,7 @@ let currentData = []; document.addEventListener('DOMContentLoaded', function() { checkAuth(); loadStatistics(); + loadPageStatistics(); setupEventListeners(); }); @@ -87,6 +88,66 @@ async function loadStatistics() { } } +async function loadPageStatistics() { + try { + const response = await fetch('/api/admin-page-stats'); + const result = await response.json(); + + if (result.success) { + const data = result.data; + + // Display page view statistics + displayPageStats('todayStats', data.today); + displayPageStats('weekStats', data.week); + displayPageStats('monthStats', data.month); + displayPageStats('totalStats', data.total); + + // Display link statistics + if (data.linkStats) { + document.getElementById('totalPlayersCount').textContent = data.linkStats.total_players || 0; + document.getElementById('linkedPlayersCount').textContent = data.linkStats.linked_players || 0; + document.getElementById('linkPercentage').textContent = `${data.linkStats.link_percentage || 0}%`; + } + } + } catch (error) { + console.error('Failed to load page statistics:', error); + // Show error in all stat containers + ['todayStats', 'weekStats', 'monthStats', 'totalStats'].forEach(id => { + document.getElementById(id).innerHTML = '
Fehler beim Laden
'; + }); + } +} + +function displayPageStats(containerId, stats) { + const container = document.getElementById(containerId); + + if (!stats || stats.length === 0) { + container.innerHTML = '
0
'; + return; + } + + // Find main page visits + const mainPageStat = stats.find(stat => stat.page === 'main_page_visit'); + const count = mainPageStat ? mainPageStat.count : 0; + + container.innerHTML = `
${count}
`; +} + +function getPageDisplayName(page) { + const pageNames = { + 'main_page_visit': '🏠 Hauptseiten-Besuche', + 'home': '🏠 Hauptseite', + 'login': '🔐 Login', + 'dashboard': '📊 Dashboard', + 'admin_login': '🛡️ Admin Login', + 'admin_dashboard': '🛡️ Admin Dashboard', + 'license_generator': '🔧 Lizenzgenerator', + 'reset_password': '🔑 Passwort Reset' + }; + + return pageNames[page] || page; +} + function showUserManagement() { showDataSection('users', 'Benutzer-Verwaltung'); loadUsers(); diff --git a/public/js/page-tracking.js b/public/js/page-tracking.js new file mode 100644 index 0000000..327d95a --- /dev/null +++ b/public/js/page-tracking.js @@ -0,0 +1,33 @@ +// Page tracking functionality +function trackPageView(pageName) { + // Get user information + const userAgent = navigator.userAgent; + const referer = document.referrer || ''; + + // Send tracking data to server + fetch('/api/track-page-view', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify({ + page: pageName, + userAgent: userAgent, + ipAddress: null, // Will be determined by server + referer: referer + }) + }).catch(error => { + console.log('Page tracking failed:', error); + // Silently fail - don't interrupt user experience + }); +} + +// Auto-track page on load - only track main page visits +document.addEventListener('DOMContentLoaded', function() { + // Only track the main page (index.html or root path) + const path = window.location.pathname; + + if (path === '/' || path === '/index.html') { + trackPageView('main_page_visit'); + } +}); diff --git a/public/login.html b/public/login.html index 97d50b0..7aff8cf 100644 --- a/public/login.html +++ b/public/login.html @@ -4,6 +4,7 @@ Ninja Server - Admin Login + diff --git a/public/pictures/favicon.ico b/public/pictures/favicon.ico new file mode 100644 index 0000000..5f2904b Binary files /dev/null and b/public/pictures/favicon.ico differ diff --git a/public/reset-password.html b/public/reset-password.html index 0699a8c..993c114 100644 --- a/public/reset-password.html +++ b/public/reset-password.html @@ -4,6 +4,7 @@ Passwort zurücksetzen - NinjaCross + diff --git a/routes/api.js b/routes/api.js index 4cfa411..625d694 100644 --- a/routes/api.js +++ b/routes/api.js @@ -1622,6 +1622,108 @@ router.delete('/admin-adminusers/:id', requireAdminAuth, async (req, res) => { } }); +// ============================================================================ +// PAGE VIEWS TRACKING +// ============================================================================ + +// Track page view +router.post('/track-page-view', async (req, res) => { + try { + const { page, userAgent, ipAddress, referer } = req.body; + + await pool.query(` + INSERT INTO page_views (page, user_agent, ip_address, referer) + VALUES ($1, $2, $3, $4) + `, [page, userAgent, ipAddress, referer]); + + res.json({ success: true }); + } catch (error) { + console.error('Error tracking page view:', error); + res.status(500).json({ + success: false, + message: 'Fehler beim Tracking der Seitenaufrufe' + }); + } +}); + +// Get page statistics +router.get('/admin-page-stats', requireAdminAuth, async (req, res) => { + try { + // Page views for today, this week, this month + const today = new Date(); + const startOfDay = new Date(today.getFullYear(), today.getMonth(), today.getDate()); + const startOfWeek = new Date(today); + startOfWeek.setDate(today.getDate() - today.getDay()); + startOfWeek.setHours(0, 0, 0, 0); + const startOfMonth = new Date(today.getFullYear(), today.getMonth(), 1); + + // Today's page views + const todayViews = await pool.query(` + SELECT page, COUNT(*) as count + FROM page_views + WHERE created_at >= $1 + GROUP BY page + ORDER BY count DESC + `, [startOfDay]); + + // This week's page views + const weekViews = await pool.query(` + SELECT page, COUNT(*) as count + FROM page_views + WHERE created_at >= $1 + GROUP BY page + ORDER BY count DESC + `, [startOfWeek]); + + // This month's page views + const monthViews = await pool.query(` + SELECT page, COUNT(*) as count + FROM page_views + WHERE created_at >= $1 + GROUP BY page + ORDER BY count DESC + `, [startOfMonth]); + + // Total page views + const totalViews = await pool.query(` + SELECT page, COUNT(*) as count + FROM page_views + GROUP BY page + ORDER BY count DESC + `); + + // Player/Supabase link statistics + const linkStats = await pool.query(` + SELECT + COUNT(*) as total_players, + COUNT(CASE WHEN supabase_user_id IS NOT NULL THEN 1 END) as linked_players, + CAST( + ROUND( + (COUNT(CASE WHEN supabase_user_id IS NOT NULL THEN 1 END)::numeric / COUNT(*)) * 100, 2 + ) AS DECIMAL(5,2) + ) as link_percentage + FROM players + `); + + res.json({ + success: true, + data: { + today: todayViews.rows, + week: weekViews.rows, + month: monthViews.rows, + total: totalViews.rows, + linkStats: linkStats.rows[0] + } + }); + } catch (error) { + console.error('Error loading page statistics:', error); + res.status(500).json({ + success: false, + message: 'Fehler beim Laden der Seitenstatistiken' + }); + } +}); + // ============================================================================ // POST/PUT ROUTES FÜR CRUD-OPERATIONEN // ============================================================================