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>
96 lines
2.2 KiB
TypeScript
96 lines
2.2 KiB
TypeScript
/**
|
|
* @copyright 2026 NHCarrigan
|
|
* @license Naomi's Public License
|
|
* @author Naomi Carrigan
|
|
*/
|
|
|
|
import { Injectable, inject } from '@angular/core';
|
|
import { Observable } from 'rxjs';
|
|
import { ApiService } from './api.service';
|
|
import { User, PrimaryBadge } from '@library/shared-types';
|
|
|
|
export interface UserProfileResponse {
|
|
id: string;
|
|
username: string;
|
|
displayName?: string;
|
|
avatar?: string;
|
|
bio?: string;
|
|
slug?: string;
|
|
primaryBadge?: PrimaryBadge;
|
|
website?: string;
|
|
discordServer?: string;
|
|
bluesky?: string;
|
|
github?: string;
|
|
linkedin?: string;
|
|
twitch?: string;
|
|
youtube?: string;
|
|
achievementPoints: number;
|
|
badges: {
|
|
isStaff: boolean;
|
|
isMod: boolean;
|
|
isVip: boolean;
|
|
inDiscord: boolean;
|
|
};
|
|
stats: {
|
|
suggestionsCount: number;
|
|
suggestionsAcceptedCount: number;
|
|
likesCount: number;
|
|
commentsCount: number;
|
|
};
|
|
createdAt: Date;
|
|
}
|
|
|
|
export interface UpdateUserSettingsRequest {
|
|
slug?: string;
|
|
displayName?: string;
|
|
bio?: string;
|
|
profilePublic?: boolean;
|
|
primaryBadge?: PrimaryBadge;
|
|
website?: string;
|
|
discordServer?: string;
|
|
bluesky?: string;
|
|
github?: string;
|
|
linkedin?: string;
|
|
twitch?: string;
|
|
youtube?: string;
|
|
}
|
|
|
|
@Injectable({
|
|
providedIn: 'root'
|
|
})
|
|
export class UserService {
|
|
private api = inject(ApiService);
|
|
|
|
getAllUsers(): Observable<User[]> {
|
|
return this.api.get<User[]>('/users');
|
|
}
|
|
|
|
banUser(userId: string): Observable<User> {
|
|
return this.api.post<User>(`/users/${userId}/ban`, {});
|
|
}
|
|
|
|
unbanUser(userId: string): Observable<User> {
|
|
return this.api.post<User>(`/users/${userId}/unban`, {});
|
|
}
|
|
|
|
getMe(): Observable<User> {
|
|
return this.api.get<User>('/users/me');
|
|
}
|
|
|
|
updateSettings(settings: UpdateUserSettingsRequest): Observable<User> {
|
|
return this.api.put<User>('/users/me', settings);
|
|
}
|
|
|
|
getProfile(identifier: string): Observable<UserProfileResponse> {
|
|
return this.api.get<UserProfileResponse>(`/users/profile/${identifier}`);
|
|
}
|
|
|
|
makeProfilePrivate(userId: string): Observable<User> {
|
|
return this.api.post<User>(`/users/${userId}/make-private`, {});
|
|
}
|
|
|
|
adminUpdateUser(userId: string, settings: UpdateUserSettingsRequest): Observable<User> {
|
|
return this.api.put<User>(`/users/${userId}`, settings);
|
|
}
|
|
}
|