This commit is contained in:
Dvorinka
2025-06-20 10:25:39 +02:00
parent da06928087
commit 94b3bac95a
4 changed files with 199 additions and 104 deletions
+28 -9
View File
@@ -96,17 +96,36 @@ async function checkAchievements() {
try { try {
const response = await fetch('/api/visitor-stats'); const response = await fetch('/api/visitor-stats');
const stats = await response.json(); const stats = await response.json();
const visitorId = getCookie('visitor_id');
// Check for monthly achievements
Object.values(ACHIEVEMENTS).forEach(achievement => {
if (achievement.period === "monthly" && stats.monthly_visits >= achievement.threshold) {
unlockAchievement(achievement);
}
});
// First visit achievement // First visit achievement
if (stats.total_visits === 1) { if (!localStorage.getItem('first_visit_' + visitorId)) {
unlockAchievement(ACHIEVEMENTS.first_visit); unlockAchievement('first_visit');
localStorage.setItem('first_visit_' + visitorId, 'true');
}
// Get visitor's stats
const visitor = stats.unique_visitors[visitorId];
if (!visitor) return;
// Monthly achievements
const monthlyVisits = stats.monthly_visits;
if (monthlyVisits >= ACHIEVEMENTS.frequent_visitor.threshold &&
!localStorage.getItem('frequent_visitor_' + visitorId)) {
unlockAchievement('frequent_visitor');
localStorage.setItem('frequent_visitor_' + visitorId, 'true');
}
if (monthlyVisits >= ACHIEVEMENTS.power_user.threshold &&
!localStorage.getItem('power_user_' + visitorId)) {
unlockAchievement('power_user');
localStorage.setItem('power_user_' + visitorId, 'true');
}
if (monthlyVisits >= ACHIEVEMENTS.super_fan.threshold &&
!localStorage.getItem('super_fan_' + visitorId)) {
unlockAchievement('super_fan');
localStorage.setItem('super_fan_' + visitorId, 'true');
} }
// Apply highest unlocked achievement theme // Apply highest unlocked achievement theme
+21
View File
@@ -4367,6 +4367,7 @@ document.addEventListener('DOMContentLoaded', function() {
// Browser stats // Browser stats
const browserStats = document.getElementById('browserStats'); const browserStats = document.getElementById('browserStats');
if (stats.browser_stats) {
browserStats.innerHTML = Object.entries(stats.browser_stats) browserStats.innerHTML = Object.entries(stats.browser_stats)
.sort((a, b) => b[1] - a[1]) .sort((a, b) => b[1] - a[1])
.map(([browser, count]) => ` .map(([browser, count]) => `
@@ -4375,9 +4376,13 @@ document.addEventListener('DOMContentLoaded', function() {
<span class="text-sm text-gray-600">${count}</span> <span class="text-sm text-gray-600">${count}</span>
</div> </div>
`).join(''); `).join('');
} else {
browserStats.innerHTML = '<div class="text-sm text-gray-500">Žádná data</div>';
}
// OS stats // OS stats
const osStats = document.getElementById('osStats'); const osStats = document.getElementById('osStats');
if (stats.os_stats) {
osStats.innerHTML = Object.entries(stats.os_stats) osStats.innerHTML = Object.entries(stats.os_stats)
.sort((a, b) => b[1] - a[1]) .sort((a, b) => b[1] - a[1])
.map(([os, count]) => ` .map(([os, count]) => `
@@ -4386,9 +4391,13 @@ document.addEventListener('DOMContentLoaded', function() {
<span class="text-sm text-gray-600">${count}</span> <span class="text-sm text-gray-600">${count}</span>
</div> </div>
`).join(''); `).join('');
} else {
osStats.innerHTML = '<div class="text-sm text-gray-500">Žádná data</div>';
}
// Active hours // Active hours
const activeHours = document.getElementById('activeHours'); const activeHours = document.getElementById('activeHours');
if (stats.most_active_hours && stats.most_active_hours.length > 0) {
activeHours.innerHTML = stats.most_active_hours activeHours.innerHTML = stats.most_active_hours
.sort((a, b) => b.count - a.count) .sort((a, b) => b.count - a.count)
.map(hour => ` .map(hour => `
@@ -4397,6 +4406,9 @@ document.addEventListener('DOMContentLoaded', function() {
<span class="text-sm text-gray-600">${hour.count}</span> <span class="text-sm text-gray-600">${hour.count}</span>
</div> </div>
`).join(''); `).join('');
} else {
activeHours.innerHTML = '<div class="text-sm text-gray-500">Žádná data</div>';
}
// Active days // Active days
const activeDays = document.getElementById('activeDays'); const activeDays = document.getElementById('activeDays');
@@ -4410,17 +4422,23 @@ document.addEventListener('DOMContentLoaded', function() {
'Sunday': 'Neděle' 'Sunday': 'Neděle'
}; };
if (stats.most_active_days && stats.most_active_days.length > 0) {
activeDays.innerHTML = stats.most_active_days activeDays.innerHTML = stats.most_active_days
.sort((a, b) => b.count - a.count) .sort((a, b) => b.count - a.count)
.filter(day => day.count > 0) // Filter out days with 0 visits
.map(day => ` .map(day => `
<div class="flex justify-between items-center"> <div class="flex justify-between items-center">
<span class="text-sm">${dayMap[day.day] || day.day}</span> <span class="text-sm">${dayMap[day.day] || day.day}</span>
<span class="text-sm text-gray-600">${day.count}</span> <span class="text-sm text-gray-600">${day.count}</span>
</div> </div>
`).join(''); `).join('');
} else {
activeDays.innerHTML = '<div class="text-sm text-gray-500">Žádná data</div>';
}
// Unique visitors // Unique visitors
const uniqueVisitors = document.getElementById('uniqueVisitors'); const uniqueVisitors = document.getElementById('uniqueVisitors');
if (stats.unique_visitors) {
uniqueVisitors.innerHTML = Object.entries(stats.unique_visitors) uniqueVisitors.innerHTML = Object.entries(stats.unique_visitors)
.map(([id, visitor]) => ` .map(([id, visitor]) => `
<div class="flex justify-between items-center p-2 hover:bg-gray-50 rounded"> <div class="flex justify-between items-center p-2 hover:bg-gray-50 rounded">
@@ -4435,6 +4453,9 @@ document.addEventListener('DOMContentLoaded', function() {
</div> </div>
</div> </div>
`).join(''); `).join('');
} else {
uniqueVisitors.innerHTML = '<div class="text-sm text-gray-500">Žádná data</div>';
}
initializeCharts(); initializeCharts();
updateVisitorStats(stats); updateVisitorStats(stats);
+13 -5
View File
@@ -618,29 +618,36 @@
}); });
</script> </script>
<!-- Achievements display --> <!-- Achievements display -->
<div id="achievementsDisplay"> <div id="achievementsDisplay" class="hidden">
<div class="achievement-item"> <div class="achievement-item bg-white rounded-lg shadow-md p-4 mb-4">
<div class="flex items-center space-x-4">
<i class="fas fa-star achievement-icon text-yellow-500"></i> <i class="fas fa-star achievement-icon text-yellow-500"></i>
<div> <div>
<h4 class="font-bold">Nováček</h4> <h4 class="font-bold">Nováček</h4>
<p class="text-sm text-gray-600">První návštěva na portálu</p> <p class="text-sm text-gray-600">První návštěva na portálu</p>
</div> </div>
</div> </div>
<div class="achievement-item"> </div>
<div class="achievement-item bg-white rounded-lg shadow-md p-4 mb-4">
<div class="flex items-center space-x-4">
<i class="fas fa-clock-rotate-left achievement-icon text-blue-500"></i> <i class="fas fa-clock-rotate-left achievement-icon text-blue-500"></i>
<div> <div>
<h4 class="font-bold">Pravidelný návštěvník</h4> <h4 class="font-bold">Pravidelný návštěvník</h4>
<p class="text-sm text-gray-600">10 návštěv za měsíc</p> <p class="text-sm text-gray-600">10 návštěv za měsíc</p>
</div> </div>
</div> </div>
<div class="achievement-item"> </div>
<div class="achievement-item bg-white rounded-lg shadow-md p-4 mb-4">
<div class="flex items-center space-x-4">
<i class="fas fa-rocket achievement-icon text-purple-500"></i> <i class="fas fa-rocket achievement-icon text-purple-500"></i>
<div> <div>
<h4 class="font-bold">Power User</h4> <h4 class="font-bold">Power User</h4>
<p class="text-sm text-gray-600">50 návštěv za měsíc</p> <p class="text-sm text-gray-600">50 návštěv za měsíc</p>
</div> </div>
</div> </div>
<div class="achievement-item"> </div>
<div class="achievement-item bg-white rounded-lg shadow-md p-4 mb-4">
<div class="flex items-center space-x-4">
<i class="fas fa-award achievement-icon text-gold"></i> <i class="fas fa-award achievement-icon text-gold"></i>
<div> <div>
<h4 class="font-bold">Super Fan</h4> <h4 class="font-bold">Super Fan</h4>
@@ -648,6 +655,7 @@
</div> </div>
</div> </div>
</div> </div>
</div>
<script src="achievements.js"></script> <script src="achievements.js"></script>
<script> <script>
+67 -20
View File
@@ -199,6 +199,63 @@ func trackVisit(w http.ResponseWriter, r *http.Request) {
stats.UniqueVisitors[visitorId] = visitor stats.UniqueVisitors[visitorId] = visitor
} }
// Update browser stats
browser := detectBrowser(userAgent)
if stats.BrowserStats == nil {
stats.BrowserStats = make(map[string]int)
}
stats.BrowserStats[browser]++
// Update OS stats
os := detectOS(userAgent)
if stats.OSStats == nil {
stats.OSStats = make(map[string]int)
}
stats.OSStats[os]++
// Update referrer stats
referrer := r.Referer()
if referrer != "" {
if stats.ReferrerStats == nil {
stats.ReferrerStats = make(map[string]int)
}
stats.ReferrerStats[referrer]++
}
// Update active hours
hour := time.Now().Hour()
found := false
for i, h := range stats.MostActiveHours {
if h.Hour == hour {
stats.MostActiveHours[i].Count++
found = true
break
}
}
if !found {
stats.MostActiveHours = append(stats.MostActiveHours, struct {
Hour int `json:"hour"`
Count int `json:"count"`
}{Hour: hour, Count: 1})
}
// Update active days
day := time.Now().Weekday().String()
found = false
for i, d := range stats.MostActiveDays {
if d.Day == day {
stats.MostActiveDays[i].Count++
found = true
break
}
}
if !found {
stats.MostActiveDays = append(stats.MostActiveDays, struct {
Day string `json:"day"`
Count int `json:"count"`
}{Day: day, Count: 1})
}
// Save visitor stats // Save visitor stats
if err := saveVisitorStats(stats); err != nil { if err := saveVisitorStats(stats); err != nil {
log.Printf("Error saving visitor stats: %v", err) log.Printf("Error saving visitor stats: %v", err)
@@ -218,20 +275,8 @@ func detectBrowser(userAgent string) string {
return "Internet Explorer" return "Internet Explorer"
} else if strings.Contains(userAgent, "edge") { } else if strings.Contains(userAgent, "edge") {
return "Edge" return "Edge"
} else { } else if strings.Contains(userAgent, "opera") {
return "Unknown" return "Opera"
}
userAgent = strings.ToLower(userAgent)
if strings.Contains(userAgent, "chrome") {
return "Chrome"
} else if strings.Contains(userAgent, "safari") && !strings.Contains(userAgent, "chrome") {
return "Safari"
} else if strings.Contains(userAgent, "firefox") {
return "Firefox"
} else if strings.Contains(userAgent, "msie") || strings.Contains(userAgent, "trident") {
return "Internet Explorer"
} else if strings.Contains(userAgent, "edge") {
return "Edge"
} else { } else {
return "Unknown" return "Unknown"
} }
@@ -242,14 +287,16 @@ func detectOS(userAgent string) string {
userAgent = strings.ToLower(userAgent) userAgent = strings.ToLower(userAgent)
if strings.Contains(userAgent, "windows") { if strings.Contains(userAgent, "windows") {
return "Windows" return "Windows"
} else if strings.Contains(userAgent, "mac os") { } else if strings.Contains(userAgent, "mac os") || strings.Contains(userAgent, "macintosh") {
return "MacOS" return "macOS"
} else if strings.Contains(userAgent, "linux") { } else if strings.Contains(userAgent, "iphone") || strings.Contains(userAgent, "ipad") || strings.Contains(userAgent, "ipod") {
return "Linux" return "iOS"
} else if strings.Contains(userAgent, "android") { } else if strings.Contains(userAgent, "android") {
return "Android" return "Android"
} else if strings.Contains(userAgent, "ios") { } else if strings.Contains(userAgent, "linux") {
return "iOS" return "Linux"
} else if strings.Contains(userAgent, "bsd") {
return "BSD"
} else { } else {
return "Unknown" return "Unknown"
} }