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:
@@ -0,0 +1,79 @@
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
|
||||
/**
|
||||
* BadgeProgress component - displays progress bars for badges in progress
|
||||
* @param {Array} badges - Array of badge objects with progress information
|
||||
*/
|
||||
const BadgeProgress = ({ badges }) => {
|
||||
// Filter to show only badges that are in progress (not earned and have some progress)
|
||||
const inProgressBadges = badges.filter(
|
||||
(badge) => !badge.isEarned && badge.progress > 0 && badge.threshold > 0
|
||||
);
|
||||
|
||||
if (inProgressBadges.length === 0) {
|
||||
return (
|
||||
<div className="alert alert-info">
|
||||
<p className="mb-0">
|
||||
Complete tasks and participate in events to earn badges!
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="badge-progress-container">
|
||||
<h5 className="mb-3">Badges In Progress</h5>
|
||||
{inProgressBadges.map((badge) => {
|
||||
const percentage = Math.round((badge.progress / badge.threshold) * 100);
|
||||
return (
|
||||
<div key={badge._id} className="mb-3">
|
||||
<div className="d-flex justify-content-between align-items-center mb-1">
|
||||
<div className="d-flex align-items-center">
|
||||
<span style={{ fontSize: "1.5rem", marginRight: "0.5rem" }}>
|
||||
{badge.icon || "🏆"}
|
||||
</span>
|
||||
<div>
|
||||
<strong>{badge.name}</strong>
|
||||
<br />
|
||||
<small className="text-muted">{badge.description}</small>
|
||||
</div>
|
||||
</div>
|
||||
<span className="badge badge-info">
|
||||
{badge.progress} / {badge.threshold}
|
||||
</span>
|
||||
</div>
|
||||
<div className="progress" style={{ height: "20px" }}>
|
||||
<div
|
||||
className={`progress-bar ${percentage >= 75 ? "bg-success" : percentage >= 50 ? "bg-info" : "bg-warning"}`}
|
||||
role="progressbar"
|
||||
style={{ width: `${percentage}%` }}
|
||||
aria-valuenow={badge.progress}
|
||||
aria-valuemin="0"
|
||||
aria-valuemax={badge.threshold}
|
||||
>
|
||||
{percentage}%
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
BadgeProgress.propTypes = {
|
||||
badges: PropTypes.arrayOf(
|
||||
PropTypes.shape({
|
||||
_id: PropTypes.string.isRequired,
|
||||
name: PropTypes.string.isRequired,
|
||||
description: PropTypes.string.isRequired,
|
||||
icon: PropTypes.string,
|
||||
progress: PropTypes.number.isRequired,
|
||||
threshold: PropTypes.number.isRequired,
|
||||
isEarned: PropTypes.bool.isRequired,
|
||||
})
|
||||
).isRequired,
|
||||
};
|
||||
|
||||
export default BadgeProgress;
|
||||
Reference in New Issue
Block a user