link,[object Object]
Skip to content

Reports (Admin) ​

Purpose: Document the admin reports management feature, UI behavior, status flow, and backend contracts. Audience: Admin, Developer Prerequisites: Supabase project linked; proper RLS and RPC deployed.

Overview ​

  • Route: /admin/reports
  • Purpose: Review and manage user-submitted reports about listings or users.
  • Key actions: Start review, Resolve, Dismiss, Reopen; quick navigation to listing/user.

Status Flow ​

Enum: report_status

  • pending β†’ under_review β†’ resolved | dismissed
  • Reopen: from resolved/dismissed back to under_review (admin only)

UI ​

  • Table with advanced controls (sorting, pagination, column visibility, row selection):
    • Default visible columns: Date, Reporter, Type, Reason, Message, Status, Actions.
    • Message column shows the first 20 chars with ... (tooltip contains full text).
    • Bulk actions: Export selected (CSV). Archive/Delete placeholders (TBD).
  • Actions menu (per row):
    • View entity (listing/user)
    • Start review (when pending)
    • Mark resolved / Dismiss (when pending or under_review)
    • Reopen (when resolved or dismissed β†’ sets under_review)
  • Details panel (below the table, shows for the selected row):
    • ID, Status, Type, Created at, Resolved at/by
    • Reason, Entity ID, Listing title
    • Reporter name/email
    • Message (full, formatted)
    • Quick links: View listing, View reporter
    • Toggle to show/hide raw JSON payload (hidden by default)

Backend ​

Table ​

  • public.reports with RLS enabled.
  • RLS: Reporter can view own; Admin can view/update all (via SECURITY DEFINER RPC).

RPC (Admin-only) ​

  • public.admin_set_report_status(p_report_id uuid, p_status text) RETURNS SETOF public.reports
    • SECURITY DEFINER, validates admin via public.is_admin_user() inside underlying function.
    • Internally casts p_status to public.report_status and delegates to admin_update_report_status.
  • public.admin_update_report_status(p_report_id uuid, p_status public.report_status) RETURNS SETOF public.reports
    • SECURITY DEFINER; updates status and stamps resolved_at/resolved_by for terminal states.

Triggers ​

  • trg_report_status_notification β†’ public.create_report_status_notification() inserts a safe notification to notifications for the reporter when status changes (message has fallbacks to avoid NOT NULL issues).

Frontend Integration ​

  • Update status:
ts
const { data, error } = await supabase.rpc('admin_set_report_status', {
  p_report_id: reportId,
  p_status: nextStatus, // 'under_review' | 'resolved' | 'dismissed'
})
  • Query:
    • Select includes reporter profile and optional listing titles for context.
    • Invalidate ['admin-reports'] after mutations.

Export ​

  • β€œExport selected” generates a CSV client-side with: id, created_at, status, reason, description, type, reporter_name, reporter_email.

Monitoring & Troubleshooting ​

  • Common errors and fixes:
    • 403 on direct PATCH to reports: use SECURITY DEFINER RPC instead of table updates.
    • 400 on RPC due to enum mismatch: ensure parameter is public.report_status or cast within wrapper.
    • Ambiguous function overloads: drop old text overload, keep wrapper name distinct (admin_set_report_status).
    • Notification trigger NOT NULL: ensure fallback message in trigger function.

Security Notes ​

  • Admin actions must be performed via SECURITY DEFINER RPCs (admin_set_report_status), never direct table writes from the client.
  • Validate role in UI and enforce in DB.

Next Steps ​

  • Optional: bulk resolve/dismiss; audit log table for status transitions.