generated from nhcarrigan/template
3da648544e
Adds a complete achievement system with gamification features across all user interactions. **Database & Types:** - Add UserAchievement model to track user progress and earned achievements - Add achievement-related fields to User model (achievementPoints, currentStreak, lastStreakCheck) - Add ACHIEVEMENT_UNLOCKED audit action type - Define 62 achievements as TypeScript constants across 5 categories **Achievement Categories:** - Suggestions (15): First suggestion through ultimate curator milestones - Likes (12): First like through legendary fan milestones - Comments (12): First comment through review legend milestones - Engagement (15): Login streaks and total activity tracking - Reports (8): Valid reports and accuracy tracking **Backend Implementation:** - Create AchievementService with comprehensive checking logic - Add achievement route with 6 API endpoints - Integrate achievement checking into all user interaction points: - Suggestions (create + accept) - Likes (toggle) - Comments (all 6 media types) - Login streaks - Reports (profile + comment) - Update UserService to include achievement points in profiles **Frontend Implementation:** - Create AchievementService for API communication - Create achievements page showing all 62 achievements with: - Category filtering (All, Suggestions, Likes, Comments, Engagement, Reports) - Tier-based styling (Bronze, Silver, Gold, Platinum, Diamond) - Progress indicators for in-progress achievements - Earned date display for completed achievements - Add "Recent Achievements" section to user profiles - Add "🏆 Achievements" link to header navigation - Only show "View All" link on own profile **Technical Features:** - Real-time achievement checking on user actions - Progress tracking to avoid recalculation - Points system for gamification - Tier-based gradient styling - Leaderboard-ready architecture Resolves #48 Co-Authored-By: Hikari <hikari@nhcarrigan.com>
123 lines
2.9 KiB
TypeScript
123 lines
2.9 KiB
TypeScript
/**
|
|
* @copyright 2026 NHCarrigan
|
|
* @license Naomi's Public License
|
|
* @author Naomi Carrigan
|
|
*/
|
|
|
|
import type { FastifyPluginAsync } from "fastify";
|
|
import {
|
|
ACHIEVEMENT_LIST,
|
|
ACHIEVEMENTS,
|
|
AchievementProgress,
|
|
UserAchievementSummary,
|
|
} from "@library/shared-types";
|
|
import { AchievementService } from "../../services/achievement.service";
|
|
|
|
const achievementsRoutes: FastifyPluginAsync = async (app) => {
|
|
const achievementService = new AchievementService();
|
|
|
|
/**
|
|
* Get all achievement definitions (public route).
|
|
*/
|
|
app.get("/definitions", async () => {
|
|
return ACHIEVEMENT_LIST;
|
|
});
|
|
|
|
/**
|
|
* Get a specific achievement definition by key (public route).
|
|
*/
|
|
app.get<{ Params: { key: string } }>(
|
|
"/definitions/:key",
|
|
async (request, reply) => {
|
|
const { key } = request.params;
|
|
const achievement = ACHIEVEMENTS[key];
|
|
|
|
if (!achievement) {
|
|
return reply.notFound("Achievement not found");
|
|
}
|
|
|
|
return achievement;
|
|
},
|
|
);
|
|
|
|
/**
|
|
* Get current user's achievement summary (authenticated users).
|
|
*/
|
|
app.get<{ Reply: UserAchievementSummary }>(
|
|
"/summary",
|
|
{
|
|
preValidation: [app.authenticate],
|
|
},
|
|
async (request) => {
|
|
const userId = request.user.id;
|
|
const summary = await achievementService.getUserAchievementSummary(
|
|
userId,
|
|
);
|
|
return summary;
|
|
},
|
|
);
|
|
|
|
/**
|
|
* Get current user's achievement progress (authenticated users).
|
|
*/
|
|
app.get<{ Reply: AchievementProgress[] }>(
|
|
"/progress",
|
|
{
|
|
preValidation: [app.authenticate],
|
|
},
|
|
async (request) => {
|
|
const userId = request.user.id;
|
|
const progress = await achievementService.getUserAchievementProgress(
|
|
userId,
|
|
);
|
|
return progress;
|
|
},
|
|
);
|
|
|
|
/**
|
|
* Get another user's achievement summary by ID (authenticated users).
|
|
*/
|
|
app.get<{ Params: { userId: string }; Reply: UserAchievementSummary }>(
|
|
"/users/:userId/summary",
|
|
{
|
|
preValidation: [app.authenticate],
|
|
},
|
|
async (request, reply) => {
|
|
const { userId } = request.params;
|
|
|
|
try {
|
|
const summary = await achievementService.getUserAchievementSummary(
|
|
userId,
|
|
);
|
|
return summary;
|
|
} catch (error) {
|
|
return reply.notFound("User not found");
|
|
}
|
|
},
|
|
);
|
|
|
|
/**
|
|
* Get another user's achievement progress by ID (authenticated users).
|
|
*/
|
|
app.get<{ Params: { userId: string }; Reply: AchievementProgress[] }>(
|
|
"/users/:userId/progress",
|
|
{
|
|
preValidation: [app.authenticate],
|
|
},
|
|
async (request, reply) => {
|
|
const { userId } = request.params;
|
|
|
|
try {
|
|
const progress = await achievementService.getUserAchievementProgress(
|
|
userId,
|
|
);
|
|
return progress;
|
|
} catch (error) {
|
|
return reply.notFound("User not found");
|
|
}
|
|
},
|
|
);
|
|
};
|
|
|
|
export default achievementsRoutes;
|