Admin UI for offer review, user management, and platform oversight
The Admin Dashboard is a set of pages under /admin/* in zooly-app that gives administrators the ability to:
Offers are no longer moderated before the talent sees them — a submitted offer goes straight to the talent. See Offers Overview.
All admin pages require the admin role. Unauthorized users see a 403 page.
Layout: apps/zooly-app/app/(pages)/admin/layout.tsx
The admin layout wraps all /admin/* pages and provides:
GET /api/admin/session — if the response is not 403, the user is authorized. Shows a loading state while checking.The admin role itself is verified server-side by assertAdmin() from @zooly/util-srv, which resolves the user from the auth cookie and checks for the admin role. All admin API endpoints use assertAdmin().
All admin pages are client components that fetch data on mount and provide interactive controls.
| Page | Route | File |
|---|---|---|
| All Offers | /admin/offers | apps/zooly-app/app/(pages)/admin/offers/page.tsx |
| User Management | /admin/users | apps/zooly-app/app/(pages)/admin/users/page.tsx |
Displays a paginated table of all offers on the platform.
Data source: GET /api/offers/list?perspective=admin (existing endpoint)
Features:
Search, list, and block/unblock user accounts.
Data source: GET /api/admin/users and POST /api/admin/users
Features:
?search= query param)POST /api/admin/users with { accountId, action: "block", reason }POST /api/admin/users with { accountId, action: "unblock" }Account blocking is implemented via the brand_limits table (not a column on account):
blockAccount(accountId, reason, adminAccountId) — upserts into brand_limits setting isBlocked = trueunblockAccount(accountId, adminAccountId) — sets isBlocked = falsepackages/db/src/access/brandLimits.tscheckBrandCanCreateOffer)The listAccounts(limit, offset, search?) function in packages/db/src/access/account.ts supports:
search parameter that filters by displayName or slug using case-insensitive ilike{ accounts: Account[], total: number } for paginationTo add a new admin page:
apps/zooly-app/app/(pages)/admin/<section>/page.tsxNAV_ITEMS in apps/zooly-app/app/(pages)/admin/layout.tsxassertAdmin(cookieHeader) from @zooly/util-srv in your API route