Statistiken und Analytics im dashboard gefixed
This commit is contained in:
@@ -47,6 +47,9 @@ async function initDashboard() {
|
|||||||
// Load times section
|
// Load times section
|
||||||
checkLinkStatusAndLoadTimes();
|
checkLinkStatusAndLoadTimes();
|
||||||
|
|
||||||
|
// Update analytics and statistics cards
|
||||||
|
updateAnalyticsAndStatisticsCards();
|
||||||
|
|
||||||
} 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';
|
||||||
@@ -149,6 +152,9 @@ function updateDynamicContent() {
|
|||||||
// Update achievement progress text
|
// Update achievement progress text
|
||||||
updateAchievementProgressText();
|
updateAchievementProgressText();
|
||||||
|
|
||||||
|
// Update analytics and statistics cards
|
||||||
|
updateAnalyticsAndStatisticsCards();
|
||||||
|
|
||||||
// Reload achievements if they're loaded
|
// Reload achievements if they're loaded
|
||||||
if (window.allAchievements && window.allAchievements.length > 0) {
|
if (window.allAchievements && window.allAchievements.length > 0) {
|
||||||
displayAchievements();
|
displayAchievements();
|
||||||
@@ -175,6 +181,114 @@ function loadLanguagePreference() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update analytics and statistics cards based on link status
|
||||||
|
function updateAnalyticsAndStatisticsCards() {
|
||||||
|
const analyticsCard = document.getElementById('analyticsCard');
|
||||||
|
const statisticsCard = document.getElementById('statisticsCard');
|
||||||
|
|
||||||
|
if (!currentPlayerId) {
|
||||||
|
// User not linked - show appropriate message
|
||||||
|
if (analyticsCard) {
|
||||||
|
const isGerman = currentLanguage === 'de';
|
||||||
|
const message = isGerman ?
|
||||||
|
'RFID verknüpfen erforderlich' :
|
||||||
|
'RFID linking required';
|
||||||
|
const description = isGerman ?
|
||||||
|
'Verknüpfe deine RFID-Karte, um Analytics zu sehen.' :
|
||||||
|
'Link your RFID card to view analytics.';
|
||||||
|
|
||||||
|
analyticsCard.innerHTML = `
|
||||||
|
<h3>📊 Analytics</h3>
|
||||||
|
<div style="background: rgba(239, 68, 68, 0.1); border: 1px solid rgba(239, 68, 68, 0.3); border-radius: 0.5rem; padding: 1rem; margin: 1rem 0;">
|
||||||
|
<div style="display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.5rem;">
|
||||||
|
<span style="color: #ef4444; font-weight: 600;">⚠️</span>
|
||||||
|
<span style="color: #ef4444; font-weight: 600;">${message}</span>
|
||||||
|
</div>
|
||||||
|
<div style="font-size: 0.9rem; color: #8892b0;">
|
||||||
|
${description}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-primary" style="margin-top: 1rem;" onclick="event.stopPropagation(); showRFIDSettings();" data-de="RFID verknüpfen" data-en="Link RFID">RFID verknüpfen</button>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (statisticsCard) {
|
||||||
|
const isGerman = currentLanguage === 'de';
|
||||||
|
const message = isGerman ?
|
||||||
|
'RFID verknüpfen erforderlich' :
|
||||||
|
'RFID linking required';
|
||||||
|
const description = isGerman ?
|
||||||
|
'Verknüpfe deine RFID-Karte, um Statistiken zu sehen.' :
|
||||||
|
'Link your RFID card to view statistics.';
|
||||||
|
|
||||||
|
statisticsCard.innerHTML = `
|
||||||
|
<h3>📊 Statistiken</h3>
|
||||||
|
<div style="background: rgba(239, 68, 68, 0.1); border: 1px solid rgba(239, 68, 68, 0.3); border-radius: 0.5rem; padding: 1rem; margin: 1rem 0;">
|
||||||
|
<div style="display: flex; align-items: center; gap: 0.5rem; margin-bottom: 0.5rem;">
|
||||||
|
<span style="color: #ef4444; font-weight: 600;">⚠️</span>
|
||||||
|
<span style="color: #ef4444; font-weight: 600;">${message}</span>
|
||||||
|
</div>
|
||||||
|
<div style="font-size: 0.9rem; color: #8892b0;">
|
||||||
|
${description}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button class="btn btn-primary" style="margin-top: 1rem;" onclick="event.stopPropagation(); showRFIDSettings();" data-de="RFID verknüpfen" data-en="Link RFID">RFID verknüpfen</button>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// User is linked - restore original cards
|
||||||
|
if (analyticsCard) {
|
||||||
|
const isGerman = currentLanguage === 'de';
|
||||||
|
analyticsCard.innerHTML = `
|
||||||
|
<h3>📊 Analytics</h3>
|
||||||
|
<div id="analyticsPreview" style="display: none;">
|
||||||
|
<div class="analytics-stats">
|
||||||
|
<div class="mini-stat">
|
||||||
|
<div class="mini-stat-number" id="avgTimeThisWeek">--:--</div>
|
||||||
|
<div class="mini-stat-label">${isGerman ? 'Durchschnitt diese Woche' : 'Average this week'}</div>
|
||||||
|
</div>
|
||||||
|
<div class="mini-stat">
|
||||||
|
<div class="mini-stat-number" id="improvementThisWeek">+0.0%</div>
|
||||||
|
<div class="mini-stat-label">${isGerman ? 'Verbesserung' : 'Improvement'}</div>
|
||||||
|
</div>
|
||||||
|
<div class="mini-stat">
|
||||||
|
<div class="mini-stat-number" id="runsThisWeek">0</div>
|
||||||
|
<div class="mini-stat-label">${isGerman ? 'Läufe diese Woche' : 'Runs this week'}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p>${isGerman ? 'Verfolge deine Leistung und überwache wichtige Metriken.' : 'Track your performance and monitor important metrics.'}</p>
|
||||||
|
<button class="btn btn-primary" style="margin-top: 1rem;" onclick="event.stopPropagation(); showAnalytics();" data-de="Analytics öffnen" data-en="Open Analytics">${isGerman ? 'Analytics öffnen' : 'Open Analytics'}</button>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (statisticsCard) {
|
||||||
|
const isGerman = currentLanguage === 'de';
|
||||||
|
statisticsCard.innerHTML = `
|
||||||
|
<h3>📊 Statistiken</h3>
|
||||||
|
<div id="statisticsPreview" style="display: none;">
|
||||||
|
<div class="statistics-stats">
|
||||||
|
<div class="mini-stat">
|
||||||
|
<div class="mini-stat-number" id="personalBest">--:--</div>
|
||||||
|
<div class="mini-stat-label">${isGerman ? 'Persönliche Bestzeit' : 'Personal Best'}</div>
|
||||||
|
</div>
|
||||||
|
<div class="mini-stat">
|
||||||
|
<div class="mini-stat-number" id="totalRunsCount">0</div>
|
||||||
|
<div class="mini-stat-label">${isGerman ? 'Gesamte Läufe' : 'Total Runs'}</div>
|
||||||
|
</div>
|
||||||
|
<div class="mini-stat">
|
||||||
|
<div class="mini-stat-number" id="rankPosition">-</div>
|
||||||
|
<div class="mini-stat-label">${isGerman ? 'Ranglisten-Position' : 'Ranking Position'}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<p>${isGerman ? 'Detaillierte Statistiken zu deinen Läufen - beste Zeiten, Verbesserungen und Vergleiche.' : 'Detailed statistics about your runs - best times, improvements and comparisons.'}</p>
|
||||||
|
<button class="btn btn-primary" style="margin-top: 1rem;" onclick="event.stopPropagation(); showStatistics();" data-de="Statistiken öffnen" data-en="Open Statistics">${isGerman ? 'Statistiken öffnen' : 'Open Statistics'}</button>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Update achievement notifications
|
// Update achievement notifications
|
||||||
function updateAchievementNotifications() {
|
function updateAchievementNotifications() {
|
||||||
// This will be called when achievements are displayed
|
// This will be called when achievements are displayed
|
||||||
@@ -798,6 +912,9 @@ async function loadUserTimesSection(playerData) {
|
|||||||
// Initialize achievements for this player
|
// Initialize achievements for this player
|
||||||
initializeAchievements(playerData.id);
|
initializeAchievements(playerData.id);
|
||||||
|
|
||||||
|
// Update analytics and statistics cards (user is now linked)
|
||||||
|
updateAnalyticsAndStatisticsCards();
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading user times:', error);
|
console.error('Error loading user times:', error);
|
||||||
showTimesNotLinked();
|
showTimesNotLinked();
|
||||||
@@ -963,6 +1080,11 @@ function convertTimeToSeconds(timeValue) {
|
|||||||
|
|
||||||
// Format time interval to readable format
|
// Format time interval to readable format
|
||||||
function formatTime(interval) {
|
function formatTime(interval) {
|
||||||
|
// Handle numeric values (seconds)
|
||||||
|
if (typeof interval === 'number') {
|
||||||
|
return formatSeconds(interval);
|
||||||
|
}
|
||||||
|
|
||||||
// Postgres interval format: {"hours":0,"minutes":1,"seconds":23.45}
|
// Postgres interval format: {"hours":0,"minutes":1,"seconds":23.45}
|
||||||
if (typeof interval === 'object') {
|
if (typeof interval === 'object') {
|
||||||
const { hours = 0, minutes = 0, seconds = 0 } = interval;
|
const { hours = 0, minutes = 0, seconds = 0 } = interval;
|
||||||
@@ -1212,9 +1334,10 @@ function showAnalytics() {
|
|||||||
console.log('Analytics section shown');
|
console.log('Analytics section shown');
|
||||||
} else {
|
} else {
|
||||||
console.error('Analytics section not found');
|
console.error('Analytics section not found');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load analytics data
|
// Load analytics data (will show fallback if not linked)
|
||||||
loadAnalyticsData();
|
loadAnalyticsData();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1237,30 +1360,33 @@ function showStatistics() {
|
|||||||
console.log('Statistics section shown');
|
console.log('Statistics section shown');
|
||||||
} else {
|
} else {
|
||||||
console.error('Statistics section not found');
|
console.error('Statistics section not found');
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load statistics data
|
// Load statistics data (will show fallback if not linked)
|
||||||
loadStatisticsData();
|
loadStatisticsData();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function loadAnalyticsData() {
|
async function loadAnalyticsData() {
|
||||||
try {
|
try {
|
||||||
if (!currentPlayerId) {
|
if (!currentPlayerId) {
|
||||||
console.error('No player ID available');
|
console.error('No player ID available - user not linked');
|
||||||
|
// Show fallback data when user is not linked
|
||||||
|
displayAnalyticsFallback();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load analytics data from API
|
// Load analytics data from API
|
||||||
const response = await fetch(`/api/v1/analytics/player/${currentPlayerId}`);
|
const response = await fetch(`/api/analytics/player/${currentPlayerId}`);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Failed to load analytics data');
|
throw new Error('Failed to load analytics data');
|
||||||
}
|
}
|
||||||
|
|
||||||
const analyticsData = await response.json();
|
const analyticsData = await response.json();
|
||||||
displayAnalyticsData(analyticsData);
|
displayAnalyticsData(analyticsData.data);
|
||||||
|
|
||||||
// Update preview in main card
|
// Update preview in main card
|
||||||
updateAnalyticsPreview(analyticsData);
|
updateAnalyticsPreview(analyticsData.data);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading analytics data:', error);
|
console.error('Error loading analytics data:', error);
|
||||||
@@ -1272,21 +1398,23 @@ async function loadAnalyticsData() {
|
|||||||
async function loadStatisticsData() {
|
async function loadStatisticsData() {
|
||||||
try {
|
try {
|
||||||
if (!currentPlayerId) {
|
if (!currentPlayerId) {
|
||||||
console.error('No player ID available');
|
console.error('No player ID available - user not linked');
|
||||||
|
// Show fallback data when user is not linked
|
||||||
|
displayStatisticsFallback();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load statistics data from API
|
// Load statistics data from API
|
||||||
const response = await fetch(`/api/v1/statistics/player/${currentPlayerId}`);
|
const response = await fetch(`/api/statistics/player/${currentPlayerId}`);
|
||||||
if (!response.ok) {
|
if (!response.ok) {
|
||||||
throw new Error('Failed to load statistics data');
|
throw new Error('Failed to load statistics data');
|
||||||
}
|
}
|
||||||
|
|
||||||
const statisticsData = await response.json();
|
const statisticsData = await response.json();
|
||||||
displayStatisticsData(statisticsData);
|
displayStatisticsData(statisticsData.data);
|
||||||
|
|
||||||
// Update preview in main card
|
// Update preview in main card
|
||||||
updateStatisticsPreview(statisticsData);
|
updateStatisticsPreview(statisticsData.data);
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error loading statistics data:', error);
|
console.error('Error loading statistics data:', error);
|
||||||
@@ -1299,12 +1427,12 @@ function displayAnalyticsData(data) {
|
|||||||
// Performance Trends
|
// Performance Trends
|
||||||
document.getElementById('avgTimeThisWeekDetail').textContent = formatTime(data.performance.avgTimeThisWeek);
|
document.getElementById('avgTimeThisWeekDetail').textContent = formatTime(data.performance.avgTimeThisWeek);
|
||||||
document.getElementById('avgTimeLastWeek').textContent = formatTime(data.performance.avgTimeLastWeek);
|
document.getElementById('avgTimeLastWeek').textContent = formatTime(data.performance.avgTimeLastWeek);
|
||||||
document.getElementById('improvementDetail').textContent = data.performance.improvement + '%';
|
document.getElementById('improvementDetail').textContent = data.performance.improvement.toFixed(2) + '%';
|
||||||
|
|
||||||
// Activity Stats
|
// Activity Stats
|
||||||
document.getElementById('runsToday').textContent = data.activity.runsToday + ' Läufe';
|
document.getElementById('runsToday').textContent = data.activity.runsToday + ' Läufe';
|
||||||
document.getElementById('runsThisWeekDetail').textContent = data.activity.runsThisWeek + ' Läufe';
|
document.getElementById('runsThisWeekDetail').textContent = data.activity.runsThisWeek + ' Läufe';
|
||||||
document.getElementById('avgRunsPerDay').textContent = data.activity.avgRunsPerDay.toFixed(1);
|
document.getElementById('avgRunsPerDay').textContent = data.activity.avgRunsPerDay.toFixed(2);
|
||||||
|
|
||||||
// Location Performance
|
// Location Performance
|
||||||
displayLocationPerformance(data.locationPerformance);
|
displayLocationPerformance(data.locationPerformance);
|
||||||
@@ -1322,7 +1450,7 @@ function displayStatisticsData(data) {
|
|||||||
// Consistency Metrics
|
// Consistency Metrics
|
||||||
document.getElementById('averageTime').textContent = formatTime(data.consistency.averageTime);
|
document.getElementById('averageTime').textContent = formatTime(data.consistency.averageTime);
|
||||||
document.getElementById('timeDeviation').textContent = formatTime(data.consistency.timeDeviation);
|
document.getElementById('timeDeviation').textContent = formatTime(data.consistency.timeDeviation);
|
||||||
document.getElementById('consistencyScore').textContent = data.consistency.consistencyScore + '%';
|
document.getElementById('consistencyScore').textContent = data.consistency.consistencyScore.toFixed(2) + '%';
|
||||||
|
|
||||||
// Ranking Stats
|
// Ranking Stats
|
||||||
displayRankingStats(data.rankings);
|
displayRankingStats(data.rankings);
|
||||||
@@ -1397,40 +1525,58 @@ function displayRankingStats(rankings) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function updateAnalyticsPreview(data) {
|
function updateAnalyticsPreview(data) {
|
||||||
document.getElementById('avgTimeThisWeek').textContent = formatTime(data.performance.avgTimeThisWeek);
|
if (data && data.performance && data.activity) {
|
||||||
document.getElementById('improvementThisWeek').textContent = data.performance.improvement + '%';
|
document.getElementById('avgTimeThisWeek').textContent = formatTime(data.performance.avgTimeThisWeek);
|
||||||
document.getElementById('runsThisWeek').textContent = data.activity.runsThisWeek;
|
document.getElementById('improvementThisWeek').textContent = data.performance.improvement.toFixed(2) + '%';
|
||||||
document.getElementById('analyticsPreview').style.display = 'block';
|
document.getElementById('runsThisWeek').textContent = data.activity.runsThisWeek;
|
||||||
|
document.getElementById('analyticsPreview').style.display = 'block';
|
||||||
|
} else {
|
||||||
|
// Hide preview if no data
|
||||||
|
document.getElementById('analyticsPreview').style.display = 'none';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateStatisticsPreview(data) {
|
function updateStatisticsPreview(data) {
|
||||||
document.getElementById('personalBest').textContent = formatTime(data.personalRecords[0]?.time || 0);
|
if (data && data.personalRecords && data.progress) {
|
||||||
document.getElementById('totalRunsCount').textContent = data.progress.totalRuns;
|
document.getElementById('personalBest').textContent = formatTime(data.personalRecords[0]?.time || 0);
|
||||||
document.getElementById('rankPosition').textContent = data.rankings[0]?.position || '-';
|
document.getElementById('totalRunsCount').textContent = data.progress.totalRuns;
|
||||||
document.getElementById('statisticsPreview').style.display = 'block';
|
document.getElementById('rankPosition').textContent = data.rankings[0]?.position || '-';
|
||||||
|
document.getElementById('statisticsPreview').style.display = 'block';
|
||||||
|
} else {
|
||||||
|
// Hide preview if no data
|
||||||
|
document.getElementById('statisticsPreview').style.display = 'none';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function displayAnalyticsFallback() {
|
function displayAnalyticsFallback() {
|
||||||
// Show fallback data when API fails
|
// Show fallback data when API fails or user not linked
|
||||||
|
const notLinkedMessage = currentLanguage === 'de' ?
|
||||||
|
'RFID nicht verknüpft - Analytics nicht verfügbar' :
|
||||||
|
'RFID not linked - Analytics not available';
|
||||||
|
|
||||||
document.getElementById('avgTimeThisWeekDetail').textContent = '--:--';
|
document.getElementById('avgTimeThisWeekDetail').textContent = '--:--';
|
||||||
document.getElementById('avgTimeLastWeek').textContent = '--:--';
|
document.getElementById('avgTimeLastWeek').textContent = '--:--';
|
||||||
document.getElementById('improvementDetail').textContent = '+0.0%';
|
document.getElementById('improvementDetail').textContent = '+0.0%';
|
||||||
document.getElementById('runsToday').textContent = '0 Läufe';
|
document.getElementById('runsToday').textContent = '0 Läufe';
|
||||||
document.getElementById('runsThisWeekDetail').textContent = '0 Läufe';
|
document.getElementById('runsThisWeekDetail').textContent = '0 Läufe';
|
||||||
document.getElementById('avgRunsPerDay').textContent = '0.0';
|
document.getElementById('avgRunsPerDay').textContent = '0.0';
|
||||||
document.getElementById('locationPerformance').innerHTML = '<p>Daten nicht verfügbar</p>';
|
document.getElementById('locationPerformance').innerHTML = `<p style="color: #8892b0; text-align: center; padding: 2rem;">${notLinkedMessage}</p>`;
|
||||||
document.getElementById('runsThisMonth').textContent = '0 Läufe';
|
document.getElementById('runsThisMonth').textContent = '0 Läufe';
|
||||||
document.getElementById('runsLastMonth').textContent = '0 Läufe';
|
document.getElementById('runsLastMonth').textContent = '0 Läufe';
|
||||||
document.getElementById('bestTimeThisMonth').textContent = '--:--';
|
document.getElementById('bestTimeThisMonth').textContent = '--:--';
|
||||||
}
|
}
|
||||||
|
|
||||||
function displayStatisticsFallback() {
|
function displayStatisticsFallback() {
|
||||||
// Show fallback data when API fails
|
// Show fallback data when API fails or user not linked
|
||||||
document.getElementById('personalRecords').innerHTML = '<p>Daten nicht verfügbar</p>';
|
const notLinkedMessage = currentLanguage === 'de' ?
|
||||||
|
'RFID nicht verknüpft - Statistiken nicht verfügbar' :
|
||||||
|
'RFID not linked - Statistics not available';
|
||||||
|
|
||||||
|
document.getElementById('personalRecords').innerHTML = `<p style="color: #8892b0; text-align: center; padding: 2rem;">${notLinkedMessage}</p>`;
|
||||||
document.getElementById('averageTime').textContent = '--:--';
|
document.getElementById('averageTime').textContent = '--:--';
|
||||||
document.getElementById('timeDeviation').textContent = '--:--';
|
document.getElementById('timeDeviation').textContent = '--:--';
|
||||||
document.getElementById('consistencyScore').textContent = '0%';
|
document.getElementById('consistencyScore').textContent = '0%';
|
||||||
document.getElementById('rankingStats').innerHTML = '<p>Daten nicht verfügbar</p>';
|
document.getElementById('rankingStats').innerHTML = `<p style="color: #8892b0; text-align: center; padding: 2rem;">${notLinkedMessage}</p>`;
|
||||||
document.getElementById('totalRunsStats').textContent = '0';
|
document.getElementById('totalRunsStats').textContent = '0';
|
||||||
document.getElementById('activeDays').textContent = '0';
|
document.getElementById('activeDays').textContent = '0';
|
||||||
document.getElementById('locationsVisited').textContent = '0';
|
document.getElementById('locationsVisited').textContent = '0';
|
||||||
|
|||||||
@@ -2990,9 +2990,9 @@ async function getPerformanceTrends(playerId) {
|
|||||||
((avgTimeLastWeek - avgTimeThisWeek) / avgTimeLastWeek * 100) : 0;
|
((avgTimeLastWeek - avgTimeThisWeek) / avgTimeLastWeek * 100) : 0;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
avgTimeThisWeek: avgTimeThisWeek,
|
avgTimeThisWeek: Math.round(avgTimeThisWeek * 100) / 100,
|
||||||
avgTimeLastWeek: avgTimeLastWeek,
|
avgTimeLastWeek: Math.round(avgTimeLastWeek * 100) / 100,
|
||||||
improvement: Math.round(improvement * 10) / 10
|
improvement: Math.round(improvement * 100) / 100
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3028,7 +3028,7 @@ async function getActivityStats(playerId) {
|
|||||||
return {
|
return {
|
||||||
runsToday: parseInt(todayResult.rows[0].count),
|
runsToday: parseInt(todayResult.rows[0].count),
|
||||||
runsThisWeek: parseInt(weekResult.rows[0].count),
|
runsThisWeek: parseInt(weekResult.rows[0].count),
|
||||||
avgRunsPerDay: parseFloat(avgResult.rows[0].avg_runs) || 0
|
avgRunsPerDay: Math.round((parseFloat(avgResult.rows[0].avg_runs) || 0) * 100) / 100
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3048,7 +3048,7 @@ async function getLocationPerformance(playerId) {
|
|||||||
|
|
||||||
return result.rows.map(row => ({
|
return result.rows.map(row => ({
|
||||||
name: row.name,
|
name: row.name,
|
||||||
bestTime: convertTimeToSeconds(row.best_time),
|
bestTime: Math.round(convertTimeToSeconds(row.best_time) * 100) / 100,
|
||||||
runs: parseInt(row.runs)
|
runs: parseInt(row.runs)
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -3086,7 +3086,7 @@ async function getMonthlyStats(playerId) {
|
|||||||
return {
|
return {
|
||||||
runsThisMonth: parseInt(thisMonthResult.rows[0].count),
|
runsThisMonth: parseInt(thisMonthResult.rows[0].count),
|
||||||
runsLastMonth: parseInt(lastMonthResult.rows[0].count),
|
runsLastMonth: parseInt(lastMonthResult.rows[0].count),
|
||||||
bestTimeThisMonth: convertTimeToSeconds(bestTimeResult.rows[0].best_time) || 0
|
bestTimeThisMonth: Math.round((convertTimeToSeconds(bestTimeResult.rows[0].best_time) || 0) * 100) / 100
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3103,7 +3103,7 @@ async function getPersonalRecords(playerId) {
|
|||||||
`, [playerId]);
|
`, [playerId]);
|
||||||
|
|
||||||
return result.rows.map(row => ({
|
return result.rows.map(row => ({
|
||||||
time: convertTimeToSeconds(row.recorded_time),
|
time: Math.round(convertTimeToSeconds(row.recorded_time) * 100) / 100,
|
||||||
location: row.location
|
location: row.location
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
@@ -3125,9 +3125,9 @@ async function getConsistencyMetrics(playerId) {
|
|||||||
Math.max(0, Math.min(100, (1 - (stddevSeconds / avgSeconds)) * 100)) : 0;
|
Math.max(0, Math.min(100, (1 - (stddevSeconds / avgSeconds)) * 100)) : 0;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
averageTime: avgSeconds,
|
averageTime: Math.round(avgSeconds * 100) / 100,
|
||||||
timeDeviation: stddevSeconds,
|
timeDeviation: Math.round(stddevSeconds * 100) / 100,
|
||||||
consistencyScore: Math.round(consistencyScore)
|
consistencyScore: Math.round(consistencyScore * 100) / 100
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user