fix: fix ui reports

This commit is contained in:
Krispire Admin
2026-06-16 19:03:06 +05:30
parent 1cde0c0544
commit a63ef672bb
6 changed files with 36 additions and 12 deletions
+6 -1
View File
@@ -208,7 +208,12 @@ func (h *AdminHandler) ResolveReport(w http.ResponseWriter, r *http.Request) {
response.Error(w, http.StatusBadRequest, "type must be post, comment, or user")
return
}
resolved, err := h.svc.ResolveReport(r.Context(), reportType, id)
var in struct {
Note string `json:"note"`
}
// Body is optional/best-effort; an empty/missing note resolves with no note.
_ = json.NewDecoder(r.Body).Decode(&in)
resolved, err := h.svc.ResolveReport(r.Context(), reportType, id, strings.TrimSpace(in.Note))
if err != nil {
response.Error(w, http.StatusInternalServerError, "could not resolve report")
return
+7
View File
@@ -84,6 +84,13 @@ type AdminReport struct {
ReporterHandle *string `json:"reporter_handle"`
ReporterName *string `json:"reporter_name"`
// ResolutionNote is the admin's note left when resolving (nil while open).
ResolutionNote *string `json:"resolution_note"`
// TargetRemoved is true when the reported content is currently removed —
// a soft-deleted post/comment, or a banned account — so the UI can offer
// "Undo" instead of "Delete"/"Ban".
TargetRemoved bool `json:"target_removed"`
// Target of the report. TargetID is the post/comment/user id; TargetText
// is the post caption, comment body, or reported user's handle so the
// admin sees what was flagged without a second fetch. TargetAuthorHandle
+13 -9
View File
@@ -219,6 +219,7 @@ func (r *AdminRepo) ListReports(ctx context.Context, reportType, q string, limit
SELECT r.id, r.category, r.reason, r.created_at, r.resolved_at,
ru.handle, ru.name,
r.post_id, p.caption, pu.handle,
r.resolution_note, COALESCE(p.deleted_at IS NOT NULL, false),
COUNT(*) OVER() AS total
FROM post_reports r
LEFT JOIN users ru ON ru.id = r.reporter_id
@@ -234,6 +235,7 @@ func (r *AdminRepo) ListReports(ctx context.Context, reportType, q string, limit
SELECT r.id, r.category, r.reason, r.created_at, r.resolved_at,
ru.handle, ru.name,
r.comment_id, c.body, cu.handle,
r.resolution_note, COALESCE(c.deleted_at IS NOT NULL, false),
COUNT(*) OVER() AS total
FROM comment_reports r
LEFT JOIN users ru ON ru.id = r.reporter_id
@@ -248,8 +250,9 @@ func (r *AdminRepo) ListReports(ctx context.Context, reportType, q string, limit
sql = `
SELECT r.id, r.category, r.reason, r.created_at, r.resolved_at,
ru.handle, ru.name,
r.reported_id, tu.handle, tu.handle
, COUNT(*) OVER() AS total
r.reported_id, tu.handle, tu.handle,
r.resolution_note, COALESCE(tu.banned_at IS NOT NULL, false),
COUNT(*) OVER() AS total
FROM user_reports r
LEFT JOIN users ru ON ru.id = r.reporter_id
LEFT JOIN users tu ON tu.id = r.reported_id
@@ -274,7 +277,8 @@ func (r *AdminRepo) ListReports(ctx context.Context, reportType, q string, limit
rep := model.AdminReport{Type: reportType}
if err := rows.Scan(&rep.ID, &rep.Category, &rep.Reason, &rep.CreatedAt, &rep.ResolvedAt,
&rep.ReporterHandle, &rep.ReporterName,
&rep.TargetID, &rep.TargetText, &rep.TargetAuthorHandle, &total); err != nil {
&rep.TargetID, &rep.TargetText, &rep.TargetAuthorHandle,
&rep.ResolutionNote, &rep.TargetRemoved, &total); err != nil {
return nil, 0, fmt.Errorf("scan admin report: %w", err)
}
out = append(out, rep)
@@ -282,10 +286,10 @@ func (r *AdminRepo) ListReports(ctx context.Context, reportType, q string, limit
return out, total, rows.Err()
}
// ResolveReport stamps resolved_at=now() on a still-open report of the given
// type. Idempotent and one-way (the WHERE guard). Returns true when this call
// is the one that resolved it.
func (r *AdminRepo) ResolveReport(ctx context.Context, reportType, id string) (bool, error) {
// ResolveReport stamps resolved_at=now() and stores the admin's note on a
// still-open report of the given type. Idempotent and one-way (the WHERE
// guard). Returns true when this call is the one that resolved it.
func (r *AdminRepo) ResolveReport(ctx context.Context, reportType, id, note string) (bool, error) {
var table string
switch reportType {
case "post":
@@ -299,8 +303,8 @@ func (r *AdminRepo) ResolveReport(ctx context.Context, reportType, id string) (b
}
// table is from a fixed whitelist above — safe to interpolate.
tag, err := r.pg.Pool.Exec(ctx,
fmt.Sprintf(`UPDATE %s SET resolved_at = now() WHERE id = $1 AND resolved_at IS NULL`, table),
id,
fmt.Sprintf(`UPDATE %s SET resolved_at = now(), resolution_note = $2 WHERE id = $1 AND resolved_at IS NULL`, table),
id, note,
)
if err != nil {
return false, fmt.Errorf("admin resolve %s report: %w", reportType, err)
+2 -2
View File
@@ -121,8 +121,8 @@ func (s *AdminService) ReplyFeedback(ctx context.Context, id, message string) (f
// ResolveReport stamps a report resolved. found=true when this call resolved a
// previously-open report (idempotent: a no-op on an already-resolved/unknown id
// returns false).
func (s *AdminService) ResolveReport(ctx context.Context, reportType, id string) (bool, error) {
return s.repo.ResolveReport(ctx, reportType, id)
func (s *AdminService) ResolveReport(ctx context.Context, reportType, id, note string) (bool, error) {
return s.repo.ResolveReport(ctx, reportType, id, note)
}
// ─── Moderation actions ──────────────────────────────────────────────────────