Skip to main content

Admin Panel

The /admin dashboard — everything admins can do.

The admin panel lives at /admin. Logged-in admins see a new link in the header; everyone else gets redirected.

Becoming an admin

In Supabase → Table Editorusers → set both is_admin = true and role = 'admin' on your row. See Authentication for the full note on why there are two fields.

Dashboard overview

/admin dashboard with stats and charts

The landing page shows at-a-glance stats: submissions this week, pending approvals, top categories, revenue, etc. Charts are powered by Recharts.

Pages

RouteWhat you manage
/adminOverview dashboard with charts
/admin/projectsApprove / reject / edit submissions
/admin/categoriesCategories and spheres (drag-and-drop)
/admin/usersUser roles, admin flag, bans
/admin/sponsorsSponsor placements
/admin/promotionsPaid promo placements
/admin/changelogPost updates that appear on /changelog
/admin/themeCustomize site theme (applies to everyone)
/admin/settingsGlobal site settings
/admin/analyticsEnhanced analytics view (traffic, countries, …)

Projects

/admin/projects table

Filter by status (pending / live / rejected), search by name, bulk-approve. Clicking a row opens the full project detail with edit fields.

Also supports CSV import — dump existing listings from a spreadsheet, map columns, import in one go.

Categories (drag-and-drop)

/admin/categories with drag-and-drop

Uses @dnd-kit/*. Drag categories to reorder within a sphere or move them between spheres. Save auto-persists the new order.

Promotions & sponsors

/admin/promotions page

Approve/reject purchases, set start dates, manually extend or revoke placements.

Site settings

/admin/settings global config page

DB-backed global knobs (separate from the code-based config/ files). Good for things you want a non-developer admin to flip without deploying.

Access control

Admin routes are protected both client-side and server-side:

  • Server components call requireAuth() and isAdmin() → redirect if not admin
  • RLS policies on sensitive tables use role = 'admin'
  • Middleware does not enforce admin — individual pages do

See also