generated from nhcarrigan/template
f839059dd2
Implements issue #55 with multiple leaderboard categories: - Top Suggestions (by count and acceptance rate) - Top Likes (by total likes given) - Top Comments (by total comments posted) - Overall Leaders (weighted by achievement points and engagement diversity) Features: - Tabbed UI with reactive state management - Medal indicators for top 3 positions - User avatars and badges display - Current user highlighting - Privacy controls via profilePublic setting - Configurable result limits (max 100) - Detailed statistics per category Backend: - Created LeaderboardService with aggregation logic - Filters for public profiles and non-banned users - Efficient sorting algorithms for each category - Parallel data fetching for all leaderboards Frontend: - Standalone Angular component with signals - Responsive card-based layout - Integration with existing user profile system - Navigation link in header dropdown Technical notes: - Uses Fastify AutoLoad with FastifyPluginAsync pattern - Shared types across monorepo for type safety - Leverages existing achievement system data
86 lines
2.2 KiB
TypeScript
86 lines
2.2 KiB
TypeScript
/**
|
|
* @copyright 2026 NHCarrigan
|
|
* @license Naomi's Public License
|
|
* @author Naomi Carrigan
|
|
*/
|
|
|
|
import { FastifyPluginAsync } from "fastify";
|
|
import type {
|
|
LeaderboardResponse,
|
|
SuggestionsLeaderboard,
|
|
LikesLeaderboard,
|
|
CommentsLeaderboard,
|
|
OverallLeaderboard,
|
|
} from "@library/shared-types";
|
|
import { LeaderboardService } from "../../services/leaderboard.service";
|
|
|
|
const leaderboardRoutes: FastifyPluginAsync = async (app) => {
|
|
const leaderboardService = new LeaderboardService();
|
|
/**
|
|
* Get all leaderboards at once.
|
|
*/
|
|
app.get<{
|
|
Querystring: { limit?: number };
|
|
Reply: LeaderboardResponse;
|
|
}>("/", async (request) => {
|
|
const limit = request.query.limit && request.query.limit > 0
|
|
? Math.min(request.query.limit, 100)
|
|
: 25;
|
|
return leaderboardService.getAllLeaderboards(limit);
|
|
});
|
|
|
|
/**
|
|
* Get top users by suggestions.
|
|
*/
|
|
app.get<{
|
|
Querystring: { limit?: number };
|
|
Reply: SuggestionsLeaderboard[];
|
|
}>("/suggestions", async (request) => {
|
|
const limit = request.query.limit && request.query.limit > 0
|
|
? Math.min(request.query.limit, 100)
|
|
: 25;
|
|
return leaderboardService.getTopSuggestions(limit);
|
|
});
|
|
|
|
/**
|
|
* Get top users by likes.
|
|
*/
|
|
app.get<{
|
|
Querystring: { limit?: number };
|
|
Reply: LikesLeaderboard[];
|
|
}>("/likes", async (request) => {
|
|
const limit = request.query.limit && request.query.limit > 0
|
|
? Math.min(request.query.limit, 100)
|
|
: 25;
|
|
return leaderboardService.getTopLikes(limit);
|
|
});
|
|
|
|
/**
|
|
* Get top users by comments.
|
|
*/
|
|
app.get<{
|
|
Querystring: { limit?: number };
|
|
Reply: CommentsLeaderboard[];
|
|
}>("/comments", async (request) => {
|
|
const limit = request.query.limit && request.query.limit > 0
|
|
? Math.min(request.query.limit, 100)
|
|
: 25;
|
|
return leaderboardService.getTopComments(limit);
|
|
});
|
|
|
|
/**
|
|
* Get overall leaderboard.
|
|
*/
|
|
app.get<{
|
|
Querystring: { limit?: number };
|
|
Reply: OverallLeaderboard[];
|
|
}>("/overall", async (request) => {
|
|
const limit = request.query.limit && request.query.limit > 0
|
|
? Math.min(request.query.limit, 100)
|
|
: 25;
|
|
return leaderboardService.getOverallLeaderboard(limit);
|
|
});
|
|
};
|
|
|
|
export default leaderboardRoutes;
|