generated from nhcarrigan/template
feat: add admin profile editing capability
Added admin endpoint and functionality for editing user profiles from the report management interface: Backend Changes: - Added PUT /api/users/:id endpoint for admin-only profile editing - Reuses existing updateUserSettings logic with slug uniqueness validation - Protected with adminGuard and csrfProtection - Proper audit logging for admin profile edits Frontend Changes: - Added adminUpdateUser() method to UserService - Updated editProfile() in admin-reports to load profile and allow bio editing - Uses prompt for quick bio editing (can be expanded to full form later) - Shows success/error toasts and refreshes report list after edit Admins can now quickly edit reported user profiles directly from the report management interface.
This commit is contained in:
@@ -255,6 +255,40 @@ const usersRoutes: FastifyPluginAsync = async (app) => {
|
||||
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;
|
||||
|
||||
@@ -1529,10 +1529,30 @@ export class AdminReportsComponent implements OnInit {
|
||||
|
||||
editProfile(report: ProfileReportWithUsers): void {
|
||||
const userId = report.reportedUser.id;
|
||||
const username = report.reportedUser.username;
|
||||
|
||||
// Navigate to profile page using ObjectId
|
||||
this.router.navigate(['/profile', userId]);
|
||||
this.toastService.success('Navigate to profile to edit');
|
||||
// Load the full profile first to get current values
|
||||
this.userService.getProfile(userId).subscribe({
|
||||
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 {
|
||||
|
||||
@@ -85,4 +85,8 @@ export class UserService {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user