feat: add comment report management to admin interface

Updated the admin-reports component to handle both profile and comment reports:
- Added report type toggle to switch between profile and comment reports
- Duplicated report display logic for comment reports
- Comment reports show the comment content with truncation
- Added separate review modals for profile and comment reports
- Comment reports display comment author instead of reported user
- Maintains all existing functionality for profile reports

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-19 19:17:50 -08:00
parent e1bac9fd3e
commit e728968fc9
@@ -9,17 +9,39 @@ import { CommonModule } from '@angular/common';
import { FormsModule } from '@angular/forms'; import { FormsModule } from '@angular/forms';
import { Router } from '@angular/router'; import { Router } from '@angular/router';
import { ReportService } from '../../services/report.service'; import { ReportService } from '../../services/report.service';
import { CommentReportService } from '../../services/comment-report.service';
import { AuthService } from '../../services/auth.service'; import { AuthService } from '../../services/auth.service';
import { ToastService } from '../../services/toast.service'; import { ToastService } from '../../services/toast.service';
import { ProfileReportWithUsers, ReportStatus, ReportReason } from '@library/shared-types'; import { ProfileReportWithUsers, CommentReportWithDetails, ReportStatus, ReportReason } from '@library/shared-types';
@Component({ @Component({
selector: 'app-admin-reports', selector: 'app-admin-reports',
standalone: true, standalone: true,
imports: [CommonModule, FormsModule], imports: [CommonModule, FormsModule],
template: ` template: `
<div class="admin-reports-wrapper">
<div class="admin-reports-container"> <div class="admin-reports-container">
<h1>Profile Reports</h1> <div class="header-section">
<h1>Reports</h1>
<div class="report-type-toggle">
<button
type="button"
class="toggle-btn"
[class.active]="reportType() === 'profile'"
(click)="setReportType('profile')"
>
Profile Reports
</button>
<button
type="button"
class="toggle-btn"
[class.active]="reportType() === 'comment'"
(click)="setReportType('comment')"
>
Comment Reports
</button>
</div>
</div>
@if (loading()) { @if (loading()) {
<div class="loading" role="status" aria-live="polite"> <div class="loading" role="status" aria-live="polite">
@@ -40,7 +62,7 @@ import { ProfileReportWithUsers, ReportStatus, ReportReason } from '@library/sha
[attr.aria-selected]="activeFilter() === 'all'" [attr.aria-selected]="activeFilter() === 'all'"
(click)="setFilter('all')" (click)="setFilter('all')"
> >
All ({{ allReports().length }}) All ({{ reportType() === 'profile' ? allProfileReports().length : allCommentReports().length }})
</button> </button>
<button <button
type="button" type="button"
@@ -86,7 +108,8 @@ import { ProfileReportWithUsers, ReportStatus, ReportReason } from '@library/sha
<!-- Reports List --> <!-- Reports List -->
<div class="reports-list" role="region" aria-label="Reports list"> <div class="reports-list" role="region" aria-label="Reports list">
@for (report of filteredReports(); track report.id) { @if (reportType() === 'profile') {
@for (report of filteredProfileReports(); track report.id) {
<div class="report-card"> <div class="report-card">
<!-- Report Header --> <!-- Report Header -->
<div class="report-header"> <div class="report-header">
@@ -182,7 +205,7 @@ import { ProfileReportWithUsers, ReportStatus, ReportReason } from '@library/sha
<button <button
type="button" type="button"
class="btn btn-review" class="btn btn-review"
(click)="openReviewModal(report)" (click)="openProfileReviewModal(report)"
[attr.aria-label]="'Review report for ' + report.reportedUser.username" [attr.aria-label]="'Review report for ' + report.reportedUser.username"
> >
Review Review
@@ -191,21 +214,133 @@ import { ProfileReportWithUsers, ReportStatus, ReportReason } from '@library/sha
</div> </div>
} @empty { } @empty {
<div class="empty-state"> <div class="empty-state">
<p>No reports found.</p> <p>No profile reports found.</p>
</div> </div>
} }
} @else {
@for (report of filteredCommentReports(); track report.id) {
<div class="report-card">
<!-- Report Header -->
<div class="report-header">
<div class="user-info">
<div class="user-section">
<h3>Comment Author</h3>
<div class="user-details">
@if (report.reportedComment.user.avatar) {
<img
[src]="report.reportedComment.user.avatar"
[alt]="report.reportedComment.user.username + ' avatar'"
class="avatar"
/>
} @else {
<div class="avatar-placeholder" [attr.aria-label]="report.reportedComment.user.username + ' avatar'">
{{ report.reportedComment.user.username.charAt(0).toUpperCase() }}
</div> </div>
} }
<div class="user-names">
<span class="username">{{ report.reportedComment.user.username }}</span>
</div>
</div>
</div>
<!-- Review Modal --> <div class="user-section">
@if (reviewingReport()) { <h3>Reporter</h3>
<div class="user-details">
@if (report.reporter.avatar) {
<img
[src]="report.reporter.avatar"
[alt]="report.reporter.username + ' avatar'"
class="avatar"
/>
} @else {
<div class="avatar-placeholder" [attr.aria-label]="report.reporter.username + ' avatar'">
{{ report.reporter.username.charAt(0).toUpperCase() }}
</div>
}
<div class="user-names">
<span class="username">{{ report.reporter.username }}</span>
@if (report.reporter.displayName) {
<span class="display-name">({{ report.reporter.displayName }})</span>
}
</div>
</div>
</div>
</div>
<div class="report-meta">
<span
class="status-badge"
[class.pending]="report.status === 'PENDING'"
[class.reviewed]="report.status === 'REVIEWED'"
[class.dismissed]="report.status === 'DISMISSED'"
[class.action-taken]="report.status === 'ACTION_TAKEN'"
[attr.aria-label]="'Status: ' + formatStatus(report.status)"
>
{{ formatStatus(report.status) }}
</span>
<span class="report-date">
{{ formatDate(report.createdAt) }}
</span>
</div>
</div>
<!-- Report Content -->
<div class="report-content">
<div class="comment-content-preview">
<strong>Comment:</strong>
<p class="comment-text">{{ truncateComment(report.reportedComment.content) }}</p>
</div>
<div class="reason">
<strong>Reason:</strong> {{ formatReason(report.reason) }}
</div>
<div class="details">
<strong>Details:</strong>
<p>{{ report.details }}</p>
</div>
@if (report.reviewNotes) {
<div class="review-notes">
<strong>Review Notes:</strong>
<p>{{ report.reviewNotes }}</p>
@if (report.reviewer) {
<small class="reviewer">
Reviewed by {{ report.reviewer.username }}
</small>
}
</div>
}
</div>
<!-- Report Actions -->
<div class="report-actions">
<button
type="button"
class="btn btn-review"
(click)="openCommentReviewModal(report)"
[attr.aria-label]="'Review report for comment by ' + report.reportedComment.user.username"
>
Review
</button>
</div>
</div>
} @empty {
<div class="empty-state">
<p>No comment reports found.</p>
</div>
}
}
</div>
}
</div>
<!-- Profile Review Modal -->
@if (reviewingProfileReport()) {
<div <div
class="modal-overlay" class="modal-overlay"
(click)="closeReviewModal()" (click)="closeProfileReviewModal()"
(keydown.escape)="closeReviewModal()" (keydown.escape)="closeProfileReviewModal()"
role="dialog" role="dialog"
aria-modal="true" aria-modal="true"
aria-labelledby="modal-title" aria-labelledby="profile-modal-title"
> >
<div <div
class="modal-content" class="modal-content"
@@ -215,11 +350,11 @@ import { ProfileReportWithUsers, ReportStatus, ReportReason } from '@library/sha
tabindex="-1" tabindex="-1"
> >
<div class="modal-header"> <div class="modal-header">
<h2 id="modal-title">Review Report</h2> <h2 id="profile-modal-title">Review Profile Report</h2>
<button <button
type="button" type="button"
class="modal-close" class="modal-close"
(click)="closeReviewModal()" (click)="closeProfileReviewModal()"
aria-label="Close modal" aria-label="Close modal"
> >
× ×
@@ -231,29 +366,29 @@ import { ProfileReportWithUsers, ReportStatus, ReportReason } from '@library/sha
<div class="review-summary"> <div class="review-summary">
<div class="summary-item"> <div class="summary-item">
<strong>Reported User:</strong> <strong>Reported User:</strong>
<span>{{ reviewingReport()!.reportedUser.username }}</span> <span>{{ reviewingProfileReport()!.reportedUser.username }}</span>
</div> </div>
<div class="summary-item"> <div class="summary-item">
<strong>Reporter:</strong> <strong>Reporter:</strong>
<span>{{ reviewingReport()!.reporter.username }}</span> <span>{{ reviewingProfileReport()!.reporter.username }}</span>
</div> </div>
<div class="summary-item"> <div class="summary-item">
<strong>Reason:</strong> <strong>Reason:</strong>
<span>{{ formatReason(reviewingReport()!.reason) }}</span> <span>{{ formatReason(reviewingProfileReport()!.reason) }}</span>
</div> </div>
<div class="summary-item"> <div class="summary-item">
<strong>Reported On:</strong> <strong>Reported On:</strong>
<span>{{ formatDate(reviewingReport()!.createdAt) }}</span> <span>{{ formatDate(reviewingProfileReport()!.createdAt) }}</span>
</div> </div>
</div> </div>
<div class="details-section"> <div class="details-section">
<strong>Details:</strong> <strong>Details:</strong>
<p class="details-text">{{ reviewingReport()!.details }}</p> <p class="details-text">{{ reviewingProfileReport()!.details }}</p>
</div> </div>
<!-- Review Form --> <!-- Review Form -->
<form (ngSubmit)="submitReview()" class="review-form"> <form (ngSubmit)="submitProfileReview()" class="review-form">
<div class="form-group"> <div class="form-group">
<label for="status">Update Status</label> <label for="status">Update Status</label>
<select <select
@@ -287,7 +422,121 @@ import { ProfileReportWithUsers, ReportStatus, ReportReason } from '@library/sha
<button <button
type="button" type="button"
class="btn btn-secondary" class="btn btn-secondary"
(click)="closeReviewModal()" (click)="closeProfileReviewModal()"
[disabled]="submitting()"
>
Cancel
</button>
<button
type="submit"
class="btn btn-primary"
[disabled]="submitting()"
>
{{ submitting() ? 'Submitting...' : 'Submit Review' }}
</button>
</div>
</form>
</div>
</div>
</div>
}
<!-- Comment Review Modal -->
@if (reviewingCommentReport()) {
<div
class="modal-overlay"
(click)="closeCommentReviewModal()"
(keydown.escape)="closeCommentReviewModal()"
role="dialog"
aria-modal="true"
aria-labelledby="comment-modal-title"
>
<div
class="modal-content"
(click)="$event.stopPropagation()"
(keydown)="$event.stopPropagation()"
role="document"
tabindex="-1"
>
<div class="modal-header">
<h2 id="comment-modal-title">Review Comment Report</h2>
<button
type="button"
class="modal-close"
(click)="closeCommentReviewModal()"
aria-label="Close modal"
>
×
</button>
</div>
<div class="modal-body">
<!-- Report Summary -->
<div class="review-summary">
<div class="summary-item">
<strong>Comment Author:</strong>
<span>{{ reviewingCommentReport()!.reportedComment.user.username }}</span>
</div>
<div class="summary-item">
<strong>Reporter:</strong>
<span>{{ reviewingCommentReport()!.reporter.username }}</span>
</div>
<div class="summary-item">
<strong>Reason:</strong>
<span>{{ formatReason(reviewingCommentReport()!.reason) }}</span>
</div>
<div class="summary-item">
<strong>Reported On:</strong>
<span>{{ formatDate(reviewingCommentReport()!.createdAt) }}</span>
</div>
</div>
<div class="comment-full-content">
<strong>Full Comment:</strong>
<p class="comment-text">{{ reviewingCommentReport()!.reportedComment.content }}</p>
</div>
<div class="details-section">
<strong>Report Details:</strong>
<p class="details-text">{{ reviewingCommentReport()!.details }}</p>
</div>
<!-- Review Form -->
<form (ngSubmit)="submitCommentReview()" class="review-form">
<div class="form-group">
<label for="comment-status">Update Status</label>
<select
id="comment-status"
name="status"
[(ngModel)]="reviewForm.status"
class="form-control"
required
aria-required="true"
>
<option value="PENDING">Pending</option>
<option value="REVIEWED">Reviewed</option>
<option value="DISMISSED">Dismissed</option>
<option value="ACTION_TAKEN">Action Taken</option>
</select>
</div>
<div class="form-group">
<label for="comment-reviewNotes">Review Notes (Optional)</label>
<textarea
id="comment-reviewNotes"
name="reviewNotes"
[(ngModel)]="reviewForm.reviewNotes"
class="form-control"
rows="4"
placeholder="Add any notes about your review..."
></textarea>
</div>
<div class="modal-actions">
<button
type="button"
class="btn btn-secondary"
(click)="closeCommentReviewModal()"
[disabled]="submitting()" [disabled]="submitting()"
> >
Cancel Cancel
@@ -308,18 +557,66 @@ import { ProfileReportWithUsers, ReportStatus, ReportReason } from '@library/sha
</div> </div>
`, `,
styles: [` styles: [`
.admin-reports-wrapper {
position: relative;
}
.admin-reports-container { .admin-reports-container {
max-width: 1200px; max-width: 1200px;
margin: 0 auto; margin: 0 auto;
padding: 2rem; padding: 2rem;
} }
.header-section {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 2rem;
flex-wrap: wrap;
gap: 1rem;
}
h1 { h1 {
color: var(--witch-purple); color: var(--witch-purple);
margin-bottom: 2rem; margin: 0;
font-size: 2rem; font-size: 2rem;
} }
.report-type-toggle {
display: flex;
gap: 0.5rem;
background: var(--witch-lavender);
padding: 0.25rem;
border-radius: 12px;
}
.toggle-btn {
padding: 0.75rem 1.5rem;
background: transparent;
border: none;
border-radius: 8px;
color: var(--witch-mauve);
font-size: 1rem;
font-weight: 500;
cursor: pointer;
transition: all 0.3s;
}
.toggle-btn:hover {
color: var(--witch-purple);
}
.toggle-btn.active {
background: var(--witch-purple);
color: var(--witch-moon);
font-weight: 600;
}
.toggle-btn:focus {
outline: 2px solid var(--witch-purple);
outline-offset: 2px;
}
.loading, .error, .empty-state { .loading, .error, .empty-state {
text-align: center; text-align: center;
padding: 3rem 2rem; padding: 3rem 2rem;
@@ -535,6 +832,41 @@ import { ProfileReportWithUsers, ReportStatus, ReportReason } from '@library/sha
font-style: italic; font-style: italic;
} }
.comment-content-preview {
padding: 1rem;
background: var(--witch-lavender);
border-radius: 8px;
margin-bottom: 1rem;
}
.comment-content-preview strong {
display: block;
margin-bottom: 0.5rem;
color: var(--witch-purple);
font-weight: 600;
}
.comment-text {
color: var(--witch-mauve);
line-height: 1.6;
margin: 0;
word-wrap: break-word;
}
.comment-full-content {
margin-bottom: 1.5rem;
padding: 1rem;
background: var(--witch-lavender);
border-radius: 8px;
}
.comment-full-content strong {
display: block;
margin-bottom: 0.5rem;
color: var(--witch-purple);
font-weight: 600;
}
/* Report Actions */ /* Report Actions */
.report-actions { .report-actions {
display: flex; display: flex;
@@ -578,17 +910,21 @@ import { ProfileReportWithUsers, ReportStatus, ReportReason } from '@library/sha
/* Modal */ /* Modal */
.modal-overlay { .modal-overlay {
position: fixed; position: fixed !important;
top: 0; top: 0 !important;
left: 0; left: 0 !important;
right: 0; right: 0 !important;
bottom: 0; bottom: 0 !important;
width: 100vw !important;
height: 100vh !important;
background: rgba(0, 0, 0, 0.7); background: rgba(0, 0, 0, 0.7);
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
z-index: 1000; z-index: 9999 !important;
padding: 1rem; padding: 1rem;
margin: 0 !important;
transform: none !important;
} }
.modal-content { .modal-content {
@@ -813,6 +1149,7 @@ import { ProfileReportWithUsers, ReportStatus, ReportReason } from '@library/sha
}) })
export class AdminReportsComponent implements OnInit { export class AdminReportsComponent implements OnInit {
private reportService = inject(ReportService); private reportService = inject(ReportService);
private commentReportService = inject(CommentReportService);
private authService = inject(AuthService); private authService = inject(AuthService);
private toastService = inject(ToastService); private toastService = inject(ToastService);
private router = inject(Router); private router = inject(Router);
@@ -820,12 +1157,15 @@ export class AdminReportsComponent implements OnInit {
// Make ReportStatus accessible in template // Make ReportStatus accessible in template
protected readonly ReportStatus = ReportStatus; protected readonly ReportStatus = ReportStatus;
allReports = signal<ProfileReportWithUsers[]>([]); reportType = signal<'profile' | 'comment'>('profile');
allProfileReports = signal<ProfileReportWithUsers[]>([]);
allCommentReports = signal<CommentReportWithDetails[]>([]);
loading = signal(true); loading = signal(true);
error = signal<string | null>(null); error = signal<string | null>(null);
activeFilter = signal<'all' | ReportStatus>('all'); activeFilter = signal<'all' | ReportStatus>('all');
reviewingReport = signal<ProfileReportWithUsers | null>(null); reviewingProfileReport = signal<ProfileReportWithUsers | null>(null);
reviewingCommentReport = signal<CommentReportWithDetails | null>(null);
submitting = signal(false); submitting = signal(false);
reviewForm = { reviewForm = {
@@ -833,29 +1173,49 @@ export class AdminReportsComponent implements OnInit {
reviewNotes: '' reviewNotes: ''
}; };
filteredReports = computed(() => { filteredProfileReports = computed(() => {
const filter = this.activeFilter(); const filter = this.activeFilter();
if (filter === 'all') { if (filter === 'all') {
return this.allReports(); return this.allProfileReports();
} }
return this.allReports().filter(report => report.status === filter); return this.allProfileReports().filter(report => report.status === filter);
}); });
pendingCount = computed(() => filteredCommentReports = computed(() => {
this.allReports().filter(r => r.status === ReportStatus.PENDING).length const filter = this.activeFilter();
); if (filter === 'all') {
return this.allCommentReports();
}
return this.allCommentReports().filter(report => report.status === filter);
});
reviewedCount = computed(() => pendingCount = computed(() => {
this.allReports().filter(r => r.status === ReportStatus.REVIEWED).length if (this.reportType() === 'profile') {
); return this.allProfileReports().filter(r => r.status === ReportStatus.PENDING).length;
}
return this.allCommentReports().filter(r => r.status === ReportStatus.PENDING).length;
});
dismissedCount = computed(() => reviewedCount = computed(() => {
this.allReports().filter(r => r.status === ReportStatus.DISMISSED).length if (this.reportType() === 'profile') {
); return this.allProfileReports().filter(r => r.status === ReportStatus.REVIEWED).length;
}
return this.allCommentReports().filter(r => r.status === ReportStatus.REVIEWED).length;
});
actionTakenCount = computed(() => dismissedCount = computed(() => {
this.allReports().filter(r => r.status === ReportStatus.ACTION_TAKEN).length if (this.reportType() === 'profile') {
); return this.allProfileReports().filter(r => r.status === ReportStatus.DISMISSED).length;
}
return this.allCommentReports().filter(r => r.status === ReportStatus.DISMISSED).length;
});
actionTakenCount = computed(() => {
if (this.reportType() === 'profile') {
return this.allProfileReports().filter(r => r.status === ReportStatus.ACTION_TAKEN).length;
}
return this.allCommentReports().filter(r => r.status === ReportStatus.ACTION_TAKEN).length;
});
ngOnInit(): void { ngOnInit(): void {
if (!this.authService.isAdmin()) { if (!this.authService.isAdmin()) {
@@ -870,34 +1230,54 @@ export class AdminReportsComponent implements OnInit {
this.loading.set(true); this.loading.set(true);
this.error.set(null); this.error.set(null);
if (this.reportType() === 'profile') {
this.reportService.getAllReports().subscribe({ this.reportService.getAllReports().subscribe({
next: (reports) => { next: (reports) => {
this.allReports.set(reports); this.allProfileReports.set(reports);
this.loading.set(false); this.loading.set(false);
}, },
error: (err) => { error: (err) => {
this.error.set(err.message ?? 'Failed to load reports'); this.error.set(err.message ?? 'Failed to load profile reports');
this.loading.set(false); this.loading.set(false);
this.toastService.error('Failed to load reports'); this.toastService.error('Failed to load profile reports');
} }
}); });
} else {
this.commentReportService.getAllReports().subscribe({
next: (reports) => {
this.allCommentReports.set(reports);
this.loading.set(false);
},
error: (err) => {
this.error.set(err.message ?? 'Failed to load comment reports');
this.loading.set(false);
this.toastService.error('Failed to load comment reports');
}
});
}
}
setReportType(type: 'profile' | 'comment'): void {
this.reportType.set(type);
this.activeFilter.set('all');
this.loadReports();
} }
setFilter(filter: 'all' | ReportStatus): void { setFilter(filter: 'all' | ReportStatus): void {
this.activeFilter.set(filter); this.activeFilter.set(filter);
} }
openReviewModal(report: ProfileReportWithUsers): void { openProfileReviewModal(report: ProfileReportWithUsers): void {
this.reviewingReport.set(report); this.reviewingProfileReport.set(report);
this.reviewForm = { this.reviewForm = {
status: report.status, status: report.status,
reviewNotes: report.reviewNotes || '' reviewNotes: report.reviewNotes || ''
}; };
} }
closeReviewModal(): void { closeProfileReviewModal(): void {
if (!this.submitting()) { if (!this.submitting()) {
this.reviewingReport.set(null); this.reviewingProfileReport.set(null);
this.reviewForm = { this.reviewForm = {
status: ReportStatus.PENDING, status: ReportStatus.PENDING,
reviewNotes: '' reviewNotes: ''
@@ -905,8 +1285,8 @@ export class AdminReportsComponent implements OnInit {
} }
} }
submitReview(): void { submitProfileReview(): void {
const report = this.reviewingReport(); const report = this.reviewingProfileReport();
if (!report) { if (!report) {
return; return;
} }
@@ -919,20 +1299,73 @@ export class AdminReportsComponent implements OnInit {
this.reviewForm.reviewNotes || undefined this.reviewForm.reviewNotes || undefined
).subscribe({ ).subscribe({
next: (updatedReport) => { next: (updatedReport) => {
this.allReports.update(reports => this.allProfileReports.update(reports =>
reports.map(r => r.id === updatedReport.id ? updatedReport : r) reports.map(r => r.id === updatedReport.id ? updatedReport : r)
); );
this.toastService.success('Report updated successfully'); this.toastService.success('Profile report updated successfully');
this.submitting.set(false); this.submitting.set(false);
this.closeReviewModal(); this.closeProfileReviewModal();
}, },
error: (err) => { error: (err) => {
this.toastService.error(err.message ?? 'Failed to update report'); this.toastService.error(err.message ?? 'Failed to update profile report');
this.submitting.set(false); this.submitting.set(false);
} }
}); });
} }
openCommentReviewModal(report: CommentReportWithDetails): void {
this.reviewingCommentReport.set(report);
this.reviewForm = {
status: report.status,
reviewNotes: report.reviewNotes || ''
};
}
closeCommentReviewModal(): void {
if (!this.submitting()) {
this.reviewingCommentReport.set(null);
this.reviewForm = {
status: ReportStatus.PENDING,
reviewNotes: ''
};
}
}
submitCommentReview(): void {
const report = this.reviewingCommentReport();
if (!report) {
return;
}
this.submitting.set(true);
this.commentReportService.updateReport(report.id, {
status: this.reviewForm.status,
reviewNotes: this.reviewForm.reviewNotes || undefined
}).subscribe({
next: (updatedReport) => {
this.allCommentReports.update(reports =>
reports.map(r => r.id === updatedReport.id ? updatedReport : r)
);
this.toastService.success('Comment report updated successfully');
this.submitting.set(false);
this.closeCommentReviewModal();
},
error: (err) => {
this.toastService.error(err.message ?? 'Failed to update comment report');
this.submitting.set(false);
}
});
}
truncateComment(content: string, maxLength = 200): string {
const stripped = content.replace(/<[^>]*>/g, '');
if (stripped.length <= maxLength) {
return stripped;
}
return stripped.substring(0, maxLength) + '...';
}
formatStatus(status: ReportStatus): string { formatStatus(status: ReportStatus): string {
const statusMap: Record<ReportStatus, string> = { const statusMap: Record<ReportStatus, string> = {
[ReportStatus.PENDING]: 'Pending', [ReportStatus.PENDING]: 'Pending',