Appearance
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/dismissedback tounder_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).
- Default visible columns:
- Actions menu (per row):
- View entity (listing/user)
- Start review (when
pending) - Mark resolved / Dismiss (when
pendingorunder_review) - Reopen (when
resolvedordismissedβ setsunder_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.reportswith 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_statustopublic.report_statusand delegates toadmin_update_report_status.
- SECURITY DEFINER, validates admin via
public.admin_update_report_status(p_report_id uuid, p_status public.report_status) RETURNS SETOF public.reports- SECURITY DEFINER; updates
statusand stampsresolved_at/resolved_byfor terminal states.
- SECURITY DEFINER; updates
Triggers β
trg_report_status_notificationβpublic.create_report_status_notification()inserts a safe notification tonotificationsfor 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_statusor cast within wrapper. - Ambiguous function overloads: drop old
textoverload, keep wrapper name distinct (admin_set_report_status). - Notification trigger NOT NULL: ensure fallback message in trigger function.
- 403 on direct PATCH to
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.