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
+29 -10
View File
@@ -96,19 +96,38 @@ async function checkAchievements() {
try {
const response = await fetch('/api/visitor-stats');
const stats = await response.json();
// Check for monthly achievements
Object.values(ACHIEVEMENTS).forEach(achievement => {
if (achievement.period === "monthly" && stats.monthly_visits >= achievement.threshold) {
unlockAchievement(achievement);
}
});
const visitorId = getCookie('visitor_id');
// First visit achievement
if (stats.total_visits === 1) {
unlockAchievement(ACHIEVEMENTS.first_visit);
if (!localStorage.getItem('first_visit_' + visitorId)) {
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
const unlocked = Array.from(unlockedAchievements);
if (unlocked.length > 0) {
+66 -45
View File
@@ -4367,36 +4367,48 @@ document.addEventListener('DOMContentLoaded', function() {
// Browser stats
const browserStats = document.getElementById('browserStats');
browserStats.innerHTML = Object.entries(stats.browser_stats)
.sort((a, b) => b[1] - a[1])
.map(([browser, count]) => `
<div class="flex justify-between items-center">
<span class="text-sm">${browser}</span>
<span class="text-sm text-gray-600">${count}</span>
</div>
`).join('');
if (stats.browser_stats) {
browserStats.innerHTML = Object.entries(stats.browser_stats)
.sort((a, b) => b[1] - a[1])
.map(([browser, count]) => `
<div class="flex justify-between items-center">
<span class="text-sm">${browser}</span>
<span class="text-sm text-gray-600">${count}</span>
</div>
`).join('');
} else {
browserStats.innerHTML = '<div class="text-sm text-gray-500">Žádná data</div>';
}
// OS stats
const osStats = document.getElementById('osStats');
osStats.innerHTML = Object.entries(stats.os_stats)
.sort((a, b) => b[1] - a[1])
.map(([os, count]) => `
<div class="flex justify-between items-center">
<span class="text-sm">${os}</span>
<span class="text-sm text-gray-600">${count}</span>
</div>
`).join('');
if (stats.os_stats) {
osStats.innerHTML = Object.entries(stats.os_stats)
.sort((a, b) => b[1] - a[1])
.map(([os, count]) => `
<div class="flex justify-between items-center">
<span class="text-sm">${os}</span>
<span class="text-sm text-gray-600">${count}</span>
</div>
`).join('');
} else {
osStats.innerHTML = '<div class="text-sm text-gray-500">Žádná data</div>';
}
// Active hours
const activeHours = document.getElementById('activeHours');
activeHours.innerHTML = stats.most_active_hours
.sort((a, b) => b.count - a.count)
.map(hour => `
<div class="flex justify-between items-center">
<span class="text-sm">${hour.hour}:00</span>
<span class="text-sm text-gray-600">${hour.count}</span>
</div>
`).join('');
if (stats.most_active_hours && stats.most_active_hours.length > 0) {
activeHours.innerHTML = stats.most_active_hours
.sort((a, b) => b.count - a.count)
.map(hour => `
<div class="flex justify-between items-center">
<span class="text-sm">${hour.hour}:00</span>
<span class="text-sm text-gray-600">${hour.count}</span>
</div>
`).join('');
} else {
activeHours.innerHTML = '<div class="text-sm text-gray-500">Žádná data</div>';
}
// Active days
const activeDays = document.getElementById('activeDays');
@@ -4410,31 +4422,40 @@ document.addEventListener('DOMContentLoaded', function() {
'Sunday': 'Neděle'
};
activeDays.innerHTML = stats.most_active_days
.sort((a, b) => b.count - a.count)
.map(day => `
<div class="flex justify-between items-center">
<span class="text-sm">${dayMap[day.day] || day.day}</span>
<span class="text-sm text-gray-600">${day.count}</span>
</div>
`).join('');
if (stats.most_active_days && stats.most_active_days.length > 0) {
activeDays.innerHTML = stats.most_active_days
.sort((a, b) => b.count - a.count)
.filter(day => day.count > 0) // Filter out days with 0 visits
.map(day => `
<div class="flex justify-between items-center">
<span class="text-sm">${dayMap[day.day] || day.day}</span>
<span class="text-sm text-gray-600">${day.count}</span>
</div>
`).join('');
} else {
activeDays.innerHTML = '<div class="text-sm text-gray-500">Žádná data</div>';
}
// Unique visitors
const uniqueVisitors = document.getElementById('uniqueVisitors');
uniqueVisitors.innerHTML = Object.entries(stats.unique_visitors)
.map(([id, visitor]) => `
<div class="flex justify-between items-center p-2 hover:bg-gray-50 rounded">
<div class="flex-1">
<div class="text-sm">${visitor.ip}</div>
<div class="text-xs text-gray-500">${visitor.user_agent}</div>
if (stats.unique_visitors) {
uniqueVisitors.innerHTML = Object.entries(stats.unique_visitors)
.map(([id, visitor]) => `
<div class="flex justify-between items-center p-2 hover:bg-gray-50 rounded">
<div class="flex-1">
<div class="text-sm">${visitor.ip}</div>
<div class="text-xs text-gray-500">${visitor.user_agent}</div>
</div>
<div class="flex items-center space-x-2">
<span class="text-xs text-gray-600">${visitor.visits} návštěv</span>
<span class="text-xs text-gray-400">|</span>
<span class="text-xs text-gray-600">${visitor.last_visit.toLocaleString()}</span>
</div>
</div>
<div class="flex items-center space-x-2">
<span class="text-xs text-gray-600">${visitor.visits} návštěv</span>
<span class="text-xs text-gray-400">|</span>
<span class="text-xs text-gray-600">${visitor.last_visit.toLocaleString()}</span>
</div>
</div>
`).join('');
`).join('');
} else {
uniqueVisitors.innerHTML = '<div class="text-sm text-gray-500">Žádná data</div>';
}
initializeCharts();
updateVisitorStats(stats);
+29 -21
View File
@@ -618,33 +618,41 @@
});
</script>
<!-- Achievements display -->
<div id="achievementsDisplay">
<div class="achievement-item">
<i class="fas fa-star achievement-icon text-yellow-500"></i>
<div>
<h4 class="font-bold">Nováček</h4>
<p class="text-sm text-gray-600">První návštěva na portálu</p>
<div id="achievementsDisplay" class="hidden">
<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>
<div>
<h4 class="font-bold">Nováček</h4>
<p class="text-sm text-gray-600">První návštěva na portálu</p>
</div>
</div>
</div>
<div class="achievement-item">
<i class="fas fa-clock-rotate-left achievement-icon text-blue-500"></i>
<div>
<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>
<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>
<div>
<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>
</div>
</div>
</div>
<div class="achievement-item">
<i class="fas fa-rocket achievement-icon text-purple-500"></i>
<div>
<h4 class="font-bold">Power User</h4>
<p class="text-sm text-gray-600">50 návštěv za měsíc</p>
<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>
<div>
<h4 class="font-bold">Power User</h4>
<p class="text-sm text-gray-600">50 návštěv za měsíc</p>
</div>
</div>
</div>
<div class="achievement-item">
<i class="fas fa-award achievement-icon text-gold"></i>
<div>
<h4 class="font-bold">Super Fan</h4>
<p class="text-sm text-gray-600">100 návštěv za měsíc</p>
<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>
<div>
<h4 class="font-bold">Super Fan</h4>
<p class="text-sm text-gray-600">100 návštěv za měsíc</p>
</div>
</div>
</div>
</div>
+75 -28
View File
@@ -199,6 +199,63 @@ func trackVisit(w http.ResponseWriter, r *http.Request) {
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
if err := saveVisitorStats(stats); err != nil {
log.Printf("Error saving visitor stats: %v", err)
@@ -218,41 +275,31 @@ func detectBrowser(userAgent string) string {
return "Internet Explorer"
} else if strings.Contains(userAgent, "edge") {
return "Edge"
} else if strings.Contains(userAgent, "opera") {
return "Opera"
} else {
return "Unknown"
}
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 {
return "Unknown"
}
}
// Helper function to extract OS from User-Agent
func detectOS(userAgent string) string {
userAgent = strings.ToLower(userAgent)
if strings.Contains(userAgent, "windows") {
return "Windows"
} else if strings.Contains(userAgent, "mac os") {
return "MacOS"
} else if strings.Contains(userAgent, "linux") {
return "Linux"
} else if strings.Contains(userAgent, "android") {
return "Android"
} else if strings.Contains(userAgent, "ios") {
return "iOS"
} else {
return "Unknown"
}
userAgent = strings.ToLower(userAgent)
if strings.Contains(userAgent, "windows") {
return "Windows"
} else if strings.Contains(userAgent, "mac os") || strings.Contains(userAgent, "macintosh") {
return "macOS"
} else if strings.Contains(userAgent, "iphone") || strings.Contains(userAgent, "ipad") || strings.Contains(userAgent, "ipod") {
return "iOS"
} else if strings.Contains(userAgent, "android") {
return "Android"
} else if strings.Contains(userAgent, "linux") {
return "Linux"
} else if strings.Contains(userAgent, "bsd") {
return "BSD"
} else {
return "Unknown"
}
}
// Helper function to extract OS from User-Agent