feat: implement comprehensive gamification, analytics, and leaderboard system

This commit adds a complete gamification system with analytics dashboards,
leaderboards, and enhanced badge tracking functionality.

Backend Features:
- Analytics API with overview, user stats, activity trends, top contributors,
  and street statistics endpoints
- Leaderboard API supporting global, weekly, monthly, and friends views
- Profile API for viewing and managing user profiles
- Enhanced gamification service with badge progress tracking and user stats
- Comprehensive test coverage for analytics and leaderboard endpoints
- Profile validation middleware for secure profile updates

Frontend Features:
- Analytics dashboard with multiple tabs (Overview, Activity, Personal Stats)
- Interactive charts for activity trends and street statistics
- Leaderboard component with pagination and timeframe filtering
- Badge collection display with progress tracking
- Personal stats component showing user achievements
- Contributors list for top performing users
- Profile management components (View/Edit)
- Toast notifications integrated throughout
- Comprehensive test coverage for Leaderboard component

Enhancements:
- User model enhanced with stats tracking and badge management
- Fixed express.Router() capitalization bug in users route
- Badge service improvements for better criteria matching
- Removed unused imports in Profile component

This feature enables users to track their contributions, view community
analytics, compete on leaderboards, and earn badges for achievements.

🤖 Generated with OpenCode

Co-Authored-By: AI Assistant <noreply@opencode.ai>
This commit is contained in:
William Valentin
2025-11-03 13:53:48 -08:00
parent ae77e30ffb
commit 3e4c730860
34 changed files with 5533 additions and 190 deletions
@@ -0,0 +1,71 @@
import React, { useState, useEffect, useContext } from \"react\";
import { useParams, Link } from \"react-router-dom\";
import axios from \"axios\";
const { AuthContext } = require(\"../../context/AuthContext\");
const ProfileView = () => {
const { userId } = useParams();
const { auth } = useContext(AuthContext);
const [profile, setProfile] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
useEffect(() => {
const fetchProfile = async () => {
try {
const res = await axios.get((/api/profile/${userId}`);
setProfile(res.data);
} catch (err) {
setError(err.response?.ager = == auth.user._id;
return (
<div className=\"container mt-5\">
<div className=\"row\">
<div className=\"col-md-4 text-center\">
<img
src={`npame"} id: auth.user._id });
setProfile(res.data);
} catch (err) {
setError(err.response?.data?.msg || \"Error fetching profile\");
}
setLoading(false);
};
fetchProfile();
}, [userId]);
if (loading) {
return <div className=\"container\"><p>Loading profile...</p></div>;
}
if (error) {
return <div className=\"container\"><div className=\"alert alert-danger\">{error}</div></div>;
}
if (!profile) {
return <div className=\"container\"><p>Profile not found.</p></div>;
}
const { name, avatar, bio, location, website, social, preferences } = profile;
const isOwnProfile = auth.isAuthenticated && auth.user._id === userId;
return (
<div className=\"container mt-5\">
<div className=\"row\">
<div className=\"col-md-4 text-center\">
<img
src={avatar || \"/logo512.png\"}
alt={`${name}\'s avatar`}
className=\"img-fluid rounded-circle mb-3\"
style={{ width: \"150px\", height: \"150px\" }}
/>
<h3>{name}</h3>
{location && <p className=\"text-muted\">{location}</p>}
{isOwnProfile && (
<Link to=\"/profile/edit\" className=\"btn btn-primary mb-3\">Edit Profile</Link>
)}
</div>
<div className=\"col-md-8\">
<div className=\"card\">
<div className=\"card-body\">