Statistik backend and favicon

This commit is contained in:
2025-09-04 17:27:45 +02:00
parent eb1d713942
commit 1f82c98646
12 changed files with 361 additions and 0 deletions

View File

@@ -4,6 +4,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin Dashboard - NinjaCross</title>
<link rel="icon" type="image/x-icon" href="/pictures/favicon.ico">
<link rel="stylesheet" href="/css/admin-dashboard.css">
</head>
<body>
@@ -40,6 +41,48 @@
</div>
</div>
<!-- Hauptseiten-Besuche -->
<div class="page-stats-section">
<h2>🏠 Hauptseiten-Besuche</h2>
<div class="page-stats-grid">
<div class="page-stat-card">
<h3>Heute</h3>
<div id="todayStats" class="page-stats-content">Lade...</div>
</div>
<div class="page-stat-card">
<h3>Diese Woche</h3>
<div id="weekStats" class="page-stats-content">Lade...</div>
</div>
<div class="page-stat-card">
<h3>Dieser Monat</h3>
<div id="monthStats" class="page-stats-content">Lade...</div>
</div>
<div class="page-stat-card">
<h3>Gesamt</h3>
<div id="totalStats" class="page-stats-content">Lade...</div>
</div>
</div>
<!-- Verlinkungs-Statistiken -->
<div class="link-stats-section">
<h3>🔗 Account-Verknüpfungen</h3>
<div class="link-stats-grid">
<div class="link-stat-card">
<div class="link-stat-number" id="totalPlayersCount">-</div>
<div class="link-stat-label">Gesamt Spieler</div>
</div>
<div class="link-stat-card">
<div class="link-stat-number" id="linkedPlayersCount">-</div>
<div class="link-stat-label">Verknüpfte Spieler</div>
</div>
<div class="link-stat-card">
<div class="link-stat-number" id="linkPercentage">-</div>
<div class="link-stat-label">Verknüpfungsrate</div>
</div>
</div>
</div>
</div>
<!-- Dashboard Cards -->
<div class="dashboard-grid">
<!-- Benutzer-Verwaltung -->

View File

@@ -4,6 +4,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Login - Lizenzgenerator</title>
<link rel="icon" type="image/x-icon" href="/pictures/favicon.ico">
<link rel="stylesheet" href="/css/adminlogin.css">
</head>
<body>

View File

@@ -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) {

View File

@@ -4,6 +4,7 @@
<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">
<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>

View File

@@ -4,6 +4,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Lizenzgenerator</title>
<link rel="icon" type="image/x-icon" href="/pictures/favicon.ico">
<link rel="stylesheet" href="/css/generator.css">
</head>
<body>

View File

@@ -4,6 +4,8 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Timer Leaderboard</title>
<link rel="icon" type="image/x-icon" href="/pictures/favicon.ico">
<script src="/js/page-tracking.js"></script>
<link rel="stylesheet" href="/css/leaderboard.css">
</head>
<body>

View File

@@ -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 = '<div style="color: #dc3545;">Fehler beim Laden</div>';
});
}
}
function displayPageStats(containerId, stats) {
const container = document.getElementById(containerId);
if (!stats || stats.length === 0) {
container.innerHTML = '<div style="color: #6c757d;">0</div>';
return;
}
// Find main page visits
const mainPageStat = stats.find(stat => stat.page === 'main_page_visit');
const count = mainPageStat ? mainPageStat.count : 0;
container.innerHTML = `<div style="font-size: 1.5em; font-weight: bold; color: #1e3c72;">${count}</div>`;
}
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();

View File

@@ -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');
}
});

View File

@@ -4,6 +4,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ninja Server - Admin Login</title>
<link rel="icon" type="image/x-icon" href="/pictures/favicon.ico">
<script src="https://unpkg.com/@supabase/supabase-js@2"></script>
<link rel="stylesheet" href="/css/login.css">
</head>

BIN
public/pictures/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -4,6 +4,7 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Passwort zurücksetzen - NinjaCross</title>
<link rel="icon" type="image/x-icon" href="/pictures/favicon.ico">
<!-- Supabase -->
<script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script>

View File

@@ -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
// ============================================================================