generated from nhcarrigan/template
feat: implement user profiles with achievements and primary badge system #58
@@ -255,6 +255,40 @@ const usersRoutes: FastifyPluginAsync = async (app) => {
|
|||||||
return user;
|
return user;
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
app.put<{ Params: { id: string }; Body: UpdateUserSettingsBody; Reply: User | { error: string } }>(
|
||||||
|
"/:id",
|
||||||
|
{
|
||||||
|
preValidation: [app.authenticate, adminGuard],
|
||||||
|
preHandler: [app.csrfProtection],
|
||||||
|
},
|
||||||
|
async (request, reply) => {
|
||||||
|
const { id } = request.params;
|
||||||
|
const updates = request.body;
|
||||||
|
|
||||||
|
// If slug is being updated, check if it's unique
|
||||||
|
if (updates.slug) {
|
||||||
|
const existingUser = await userService.getUserBySlug(updates.slug);
|
||||||
|
if (existingUser && existingUser.id !== id) {
|
||||||
|
return reply.code(400).send({ error: "Slug already taken" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const updatedUser = await userService.updateUserSettings(id, updates);
|
||||||
|
if (!updatedUser) {
|
||||||
|
return reply.code(404).send({ error: "User not found" });
|
||||||
|
}
|
||||||
|
|
||||||
|
await AuditService.logFromRequest(request, {
|
||||||
|
action: AuditAction.entryUpdate,
|
||||||
|
category: AuditCategory.admin,
|
||||||
|
targetUserId: id,
|
||||||
|
details: `Admin updated profile for user: ${updatedUser.username}`,
|
||||||
|
});
|
||||||
|
|
||||||
|
return updatedUser;
|
||||||
|
}
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default usersRoutes;
|
export default usersRoutes;
|
||||||
|
|||||||
@@ -1529,10 +1529,30 @@ export class AdminReportsComponent implements OnInit {
|
|||||||
|
|
||||||
editProfile(report: ProfileReportWithUsers): void {
|
editProfile(report: ProfileReportWithUsers): void {
|
||||||
const userId = report.reportedUser.id;
|
const userId = report.reportedUser.id;
|
||||||
|
const username = report.reportedUser.username;
|
||||||
|
|
||||||
// Navigate to profile page using ObjectId
|
// Load the full profile first to get current values
|
||||||
this.router.navigate(['/profile', userId]);
|
this.userService.getProfile(userId).subscribe({
|
||||||
this.toastService.success('Navigate to profile to edit');
|
next: (profile) => {
|
||||||
|
const newBio = prompt(`Edit bio for ${username}:`, profile.bio || '');
|
||||||
|
|
||||||
|
if (newBio !== null) {
|
||||||
|
this.userService.adminUpdateUser(userId, { bio: newBio }).subscribe({
|
||||||
|
next: () => {
|
||||||
|
this.toastService.success('Profile updated successfully');
|
||||||
|
this.loadReports();
|
||||||
|
this.closeProfileReviewModal();
|
||||||
|
},
|
||||||
|
error: (err) => {
|
||||||
|
this.toastService.error(err.message ?? 'Failed to update profile');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: (err) => {
|
||||||
|
this.toastService.error(err.message ?? 'Failed to load profile');
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
makeProfilePrivate(report: ProfileReportWithUsers): void {
|
makeProfilePrivate(report: ProfileReportWithUsers): void {
|
||||||
|
|||||||
@@ -85,4 +85,8 @@ export class UserService {
|
|||||||
makeProfilePrivate(userId: string): Observable<User> {
|
makeProfilePrivate(userId: string): Observable<User> {
|
||||||
return this.api.post<User>(`/users/${userId}/make-private`, {});
|
return this.api.post<User>(`/users/${userId}/make-private`, {});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
adminUpdateUser(userId: string, settings: UpdateUserSettingsRequest): Observable<User> {
|
||||||
|
return this.api.put<User>(`/users/${userId}`, settings);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user