Files
adopt-a-street/backend/routes/badges.js
William Valentin 3e4c730860 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>
2025-11-03 13:53:48 -08:00

88 lines
2.1 KiB
JavaScript

const express = require("express");
const Badge = require("../models/Badge");
const UserBadge = require("../models/UserBadge");
const auth = require("../middleware/auth");
const { asyncHandler } = require("../middleware/errorHandler");
const { getUserBadgeProgress } = require("../services/gamificationService");
const { getCacheMiddleware } = require("../middleware/cache");
const router = express.Router();
/**
* GET /api/badges
* Get all available badges
*/
router.get(
"/",
getCacheMiddleware(600), // 10 minute cache
asyncHandler(async (req, res) => {
const badges = await Badge.findAll();
// Sort by order and rarity in JavaScript since CouchDB doesn't support complex sorting
badges.sort((a, b) => {
if (a.order !== b.order) return a.order - b.order;
return a.rarity.localeCompare(b.rarity);
});
res.json(badges);
})
);
/**
* GET /api/badges/progress
* Get current user's badge progress (requires authentication)
*/
router.get(
"/progress",
auth,
getCacheMiddleware(600), // 10 minute cache
asyncHandler(async (req, res) => {
const progress = await getUserBadgeProgress(req.user.id);
res.json(progress);
})
);
/**
* GET /api/users/:userId/badges
* Get badges earned by a specific user with progress
*/
router.get(
"/users/:userId",
getCacheMiddleware(600), // 10 minute cache
asyncHandler(async (req, res) => {
const { userId } = req.params;
const userBadges = await UserBadge.findByUser(userId);
// Sort by earnedAt in JavaScript
userBadges.sort((a, b) => new Date(b.earnedAt) - new Date(a.earnedAt));
res.json(
userBadges.map((ub) => ({
badge: ub.badge,
earnedAt: ub.earnedAt,
progress: ub.progress,
}))
);
})
);
/**
* GET /api/badges/:badgeId
* Get a specific badge by ID
*/
router.get(
"/:badgeId",
getCacheMiddleware(600), // 10 minute cache
asyncHandler(async (req, res) => {
const { badgeId } = req.params;
const badge = await Badge.findById(badgeId);
if (!badge) {
return res.status(404).json({ msg: "Badge not found" });
}
res.json(badge);
})
);
module.exports = router;