From bbc3b040d0987a20aa6ac23871dbee1a9fda73b9 Mon Sep 17 00:00:00 2001 From: Hikari Date: Thu, 19 Feb 2026 20:15:03 -0800 Subject: [PATCH] feat: add comprehensive profile editing modal for admins Replaced the simple prompt-based profile editing with a full-featured modal that allows admins to edit all profile fields: Profile Information: - Display Name - Profile URL Slug (with uniqueness validation) - Bio (with character counter) Social Links: - Website - Discord Server - GitHub username - Bluesky handle - LinkedIn username - Twitch username - YouTube handle/channel All fields include: - Proper validation patterns - Help text explaining format requirements - Styled form sections for organization - Loading states during submission - Success/error toast notifications The modal opens when clicking "Edit Profile" on a profile report, loads the current profile data, and saves all changes via the admin API endpoint. --- .../admin-reports/admin-reports.component.ts | 299 +++++++++++++++++- 1 file changed, 286 insertions(+), 13 deletions(-) diff --git a/apps/frontend/src/app/components/admin-reports/admin-reports.component.ts b/apps/frontend/src/app/components/admin-reports/admin-reports.component.ts index 73ca422..65d21a4 100644 --- a/apps/frontend/src/app/components/admin-reports/admin-reports.component.ts +++ b/apps/frontend/src/app/components/admin-reports/admin-reports.component.ts @@ -598,6 +598,201 @@ import { ProfileReportWithUsers, CommentReportWithDetails, ReportStatus, ReportR } + + + @if (editingProfile()) { + + } `, styles: [` @@ -1137,6 +1332,29 @@ import { ProfileReportWithUsers, CommentReportWithDetails, ReportStatus, ReportR font-size: 0.95rem; } + .form-help { + font-size: 0.85rem; + color: var(--witch-mauve); + font-style: italic; + } + + .form-section { + margin-bottom: 1.5rem; + padding-bottom: 1.5rem; + border-bottom: 1px solid var(--witch-lavender); + } + + .form-section:last-of-type { + border-bottom: none; + padding-bottom: 0; + } + + .form-section h3 { + color: var(--witch-purple); + margin-bottom: 1rem; + font-size: 1.1rem; + } + .form-control { padding: 0.75rem; border: 2px solid var(--witch-lavender); @@ -1256,6 +1474,7 @@ export class AdminReportsComponent implements OnInit { reviewingProfileReport = signal(null); reviewingCommentReport = signal(null); + editingProfile = signal<{ userId: string; username: string; profile: any } | null>(null); submitting = signal(false); reviewForm = { @@ -1263,6 +1482,20 @@ export class AdminReportsComponent implements OnInit { reviewNotes: '' }; + profileEditForm = { + displayName: '', + slug: '', + bio: '', + profilePublic: true, + website: '', + discordServer: '', + bluesky: '', + github: '', + linkedin: '', + twitch: '', + youtube: '' + }; + filteredProfileReports = computed(() => { const filter = this.activeFilter(); if (filter === 'all') { @@ -1534,20 +1767,23 @@ export class AdminReportsComponent implements OnInit { // 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 || ''); + // Populate the edit form with current values + this.profileEditForm = { + displayName: profile.displayName || '', + slug: profile.slug || '', + bio: profile.bio || '', + profilePublic: true, // We'll get this from the full user object if needed + website: profile.website || '', + discordServer: profile.discordServer || '', + bluesky: profile.bluesky || '', + github: profile.github || '', + linkedin: profile.linkedin || '', + twitch: profile.twitch || '', + youtube: profile.youtube || '' + }; - 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'); - } - }); - } + // Open the edit modal + this.editingProfile.set({ userId, username, profile }); }, error: (err) => { this.toastService.error(err.message ?? 'Failed to load profile'); @@ -1555,6 +1791,43 @@ export class AdminReportsComponent implements OnInit { }); } + closeProfileEditModal(): void { + this.editingProfile.set(null); + this.profileEditForm = { + displayName: '', + slug: '', + bio: '', + profilePublic: true, + website: '', + discordServer: '', + bluesky: '', + github: '', + linkedin: '', + twitch: '', + youtube: '' + }; + } + + saveProfileEdit(): void { + const editing = this.editingProfile(); + if (!editing) return; + + this.submitting.set(true); + this.userService.adminUpdateUser(editing.userId, this.profileEditForm).subscribe({ + next: () => { + this.toastService.success('Profile updated successfully'); + this.loadReports(); + this.closeProfileEditModal(); + this.closeProfileReviewModal(); + this.submitting.set(false); + }, + error: (err) => { + this.toastService.error(err.message ?? 'Failed to update profile'); + this.submitting.set(false); + } + }); + } + makeProfilePrivate(report: ProfileReportWithUsers): void { const userId = report.reportedUser.id; const username = report.reportedUser.username;