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:
@@ -4,8 +4,6 @@ const Street = require("../models/Street");
|
||||
const auth = require("../middleware/auth");
|
||||
const { asyncHandler } = require("../middleware/errorHandler");
|
||||
const { userIdValidation } = require("../middleware/validators/userValidator");
|
||||
const { upload, handleUploadError } = require("../middleware/upload");
|
||||
const { uploadImage, deleteImage } = require("../config/cloudinary");
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@@ -38,7 +36,7 @@ router.get(
|
||||
}
|
||||
|
||||
const userWithStreets = {
|
||||
...user,
|
||||
...user.toSafeObject(),
|
||||
adoptedStreets,
|
||||
};
|
||||
|
||||
@@ -46,71 +44,4 @@ router.get(
|
||||
}),
|
||||
);
|
||||
|
||||
// Upload profile picture
|
||||
router.post(
|
||||
"/profile-picture",
|
||||
auth,
|
||||
upload.single("image"),
|
||||
handleUploadError,
|
||||
asyncHandler(async (req, res) => {
|
||||
if (!req.file) {
|
||||
return res.status(400).json({ msg: "No image file provided" });
|
||||
}
|
||||
|
||||
const user = await User.findById(req.user.id);
|
||||
if (!user) {
|
||||
return res.status(404).json({ msg: "User not found" });
|
||||
}
|
||||
|
||||
// Delete old profile picture if exists
|
||||
if (user.cloudinaryPublicId) {
|
||||
await deleteImage(user.cloudinaryPublicId);
|
||||
}
|
||||
|
||||
// Upload new image to Cloudinary
|
||||
const result = await uploadImage(
|
||||
req.file.buffer,
|
||||
"adopt-a-street/profiles",
|
||||
);
|
||||
|
||||
// Update user with new profile picture
|
||||
const updatedUser = await User.update(req.user.id, {
|
||||
profilePicture: result.url,
|
||||
cloudinaryPublicId: result.publicId,
|
||||
});
|
||||
|
||||
res.json({
|
||||
msg: "Profile picture updated successfully",
|
||||
profilePicture: updatedUser.profilePicture,
|
||||
});
|
||||
}),
|
||||
);
|
||||
|
||||
// Delete profile picture
|
||||
router.delete(
|
||||
"/profile-picture",
|
||||
auth,
|
||||
asyncHandler(async (req, res) => {
|
||||
const user = await User.findById(req.user.id);
|
||||
if (!user) {
|
||||
return res.status(404).json({ msg: "User not found" });
|
||||
}
|
||||
|
||||
if (!user.cloudinaryPublicId) {
|
||||
return res.status(400).json({ msg: "No profile picture to delete" });
|
||||
}
|
||||
|
||||
// Delete image from Cloudinary
|
||||
await deleteImage(user.cloudinaryPublicId);
|
||||
|
||||
// Remove from user
|
||||
await User.update(req.user.id, {
|
||||
profilePicture: undefined,
|
||||
cloudinaryPublicId: undefined,
|
||||
});
|
||||
|
||||
res.json({ msg: "Profile picture deleted successfully" });
|
||||
}),
|
||||
);
|
||||
|
||||
module.exports = router;
|
||||
|
||||
Reference in New Issue
Block a user