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:
52
backend/middleware/validators/profileValidator.js
Normal file
52
backend/middleware/validators/profileValidator.js
Normal file
@@ -0,0 +1,52 @@
|
||||
const { body, validationResult } = require("express-validator");
|
||||
|
||||
const URL_REGEX = /^(https?|ftp):\\/\\/[^\\s\\/$.?#].[^\\s]*$/i;
|
||||
|
||||
const validateProfile = [
|
||||
body("bio")
|
||||
.optional()
|
||||
.isLength({ max: 500 })
|
||||
.withMessage("Bio cannot exceed 500 characters."),
|
||||
body("location").optional().isString(),
|
||||
body("website")
|
||||
.optional()
|
||||
.if(body("website").notEmpty())
|
||||
.matches(URL_REGEX)
|
||||
.withMessage("Invalid website URL."),
|
||||
body("social.twitter")
|
||||
.optional()
|
||||
.if(body("social.twitter").notEmpty())
|
||||
.matches(URL_REGEX)
|
||||
.withMessage("Invalid Twitter URL."),
|
||||
body("social.github")
|
||||
.optional()
|
||||
.if(body("social.github").notEmpty())
|
||||
.matches(URL_REGEX)
|
||||
.withMessage("Invalid Github URL."),
|
||||
body("social.linkedin")
|
||||
.optional()
|
||||
.if(body("social.linkedin").notEmpty())
|
||||
.matches(URL_REGEX)
|
||||
.withMessage("Invalid LinkedIn URL."),
|
||||
body("privacySettings.profileVisibility")
|
||||
.optional()
|
||||
.isIn(["public", "private"])
|
||||
.withMessage("Profile visibility must be public or private."),
|
||||
body("preferences.emailNotifications").optional().isBoolean(),
|
||||
body("preferences.pushNotifications").optional().isBoolean(),
|
||||
body("preferences.theme")
|
||||
.optional()
|
||||
.isIn(["light", "dark"])
|
||||
.withMessage("Theme must be light or dark."),
|
||||
|
||||
(req, res, next) => {
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(400).json({ errors: errors.array() });
|
||||
}
|
||||
next();
|
||||
},
|
||||
];
|
||||
|
||||
module.exports = { validateProfile };
|
||||
|
||||
Reference in New Issue
Block a user