P PasteCode
Prompt

Prompt to Create an Admin Dashboard in Next.js

AI agent prompt to build a protected Next.js admin dashboard with data tables, server-side auth guards, and a sidebar layout using Tailwind.

CursorClaude CodeCodexWindsurf Next.jsPostgreSQLTypeScriptTailwind
.md .json Difficulty: Hard Updated Jun 8, 2026

Give this prompt to your agent to scaffold a role-gated admin area with a persistent sidebar, sortable data tables, and server-side auth checks — without it scaffolding a full new Next.js project or installing a bloated admin framework.

Main Prompt

Main Prompt
You are working in an existing Next.js App Router project with TypeScript, Tailwind CSS v4,
and PostgreSQL. Auth is already set up (assume a `getSession()` helper exists in `src/lib/get-session.ts`
that returns `{ user: { id, email, role } } | null`).
Task: add an admin dashboard at the `/admin` route group.
Requirements:
- Create a `src/app/(admin)/layout.tsx` that:
- Calls `getSession()` and redirects to `/login` if the session is null or `role !== 'admin'`.
- Renders a persistent sidebar with links: Dashboard, Users, Settings.
- Uses Tailwind for layout — a fixed-width sidebar and a scrollable main area.
- Create `src/app/(admin)/dashboard/page.tsx` with:
- Four KPI cards: Total Users, Active Today, Revenue (MRR), Churn Rate — all fetched from
PostgreSQL using parameterized queries via `postgres` npm package.
- A `<UsersTable>` server component that renders the 50 most recent users (id, email, role,
created_at) with sortable column headers (URL-based sort param).
- Create `src/components/admin/KpiCard.tsx` and `src/components/admin/UsersTable.tsx`.
- All DB queries must use parameterized placeholders — never string interpolation.
- Do not install Prisma, Drizzle, or any ORM. Use the `postgres` package directly.
- Do not use any pre-built admin UI library (Refine, AdminJS, etc.).
Stop and list all planned file changes before writing any code.

Implementation Notes

  • The (admin) route group parentheses keep /admin/dashboard out of the URL while sharing the layout — the agent sometimes forgets the parentheses and creates a literal /admin segment.
  • Sortable columns should use ?sort=email&dir=asc URL params read in the Server Component via searchParams prop — no client state needed.
  • KPI queries should be run in parallel with Promise.all to minimize DB round-trips.

Expected File Changes

src/app/(admin)/layout.tsx (new)
src/app/(admin)/dashboard/page.tsx (new)
src/components/admin/KpiCard.tsx (new)
src/components/admin/UsersTable.tsx (new)
src/lib/queries/admin-stats.ts (new)

Acceptance Criteria

  • Visiting /dashboard as a non-admin user redirects to /login.
  • KPI cards show real values from PostgreSQL.
  • Users table is sorted by the sort + dir URL params server-side.
  • bun run build exits 0 with no type errors.

Test Commands

Terminal window
bun run typecheck
bun run build
bun run dev
# log in as admin and visit /dashboard
# visit /dashboard without auth — confirm redirect to /login
# add ?sort=email&dir=asc to the URL and confirm table order

Common AI Mistakes

  • Creating src/app/admin/layout.tsx instead of src/app/(admin)/layout.tsx.
  • Performing the auth check client-side, which is bypassable.
  • Using string interpolation in SQL queries: `SELECT * FROM users WHERE role = '${role}'`.
  • Fetching KPI stats sequentially instead of using Promise.all.

Fix Prompt

Fix Prompt
The admin layout does not redirect unauthenticated users, or the route group folder name is wrong.
Fix in order:
1. Rename `src/app/admin/` to `src/app/(admin)/` — the parentheses are required for a route group.
2. In `layout.tsx`, add the auth check at the very top using `getSession()` and `redirect('/login')`.
3. Audit all SQL query strings and replace any interpolated variables with `$1`, `$2` placeholders.
Show the corrected diff only.