# Prompt para criar um Dashboard Administrativo em Next.js

> Prompt para agente de IA construir um dashboard administrativo protegido em Next.js com tabelas de dados, guards de autenticação no servidor e layout com barra lateral usando Tailwind.

**Type:** Prompt  
**Tools:** Cursor, Claude Code, Codex, Windsurf  
**Stack:** Next.js, PostgreSQL, TypeScript, Tailwind  
**Difficulty:** hard  
**Updated:** 2026-06-08

---

Entregue este prompt ao seu agente para esboçar uma área administrativa com controle de funções, barra lateral persistente, tabelas de dados classificáveis e verificações de autenticação no servidor — sem que ele tenha que criar um projeto Next.js completo ou instalar um framework administrativo inchado.

## Main Prompt

```txt title="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

- Os parênteses do grupo de rotas `(admin)` mantêm `/admin/dashboard` fora da URL enquanto compartilham o layout — o agente às vezes esquece os parênteses e cria um segmento literal `/admin`.
- Colunas classificáveis devem usar parâmetros de URL `?sort=email&dir=asc` lidos no Componente Servidor via prop `searchParams` — sem necessidade de estado do cliente.
- Consultas de KPI devem ser executadas em paralelo com `Promise.all` para minimizar viagens de ida e volta ao banco de dados.

## Expected File Changes

```txt
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

- Visitar `/dashboard` como um usuário não administrador redireciona para `/login`.
- Cartões de KPI mostram valores reais do PostgreSQL.
- A tabela de usuários é classificada pelos parâmetros de URL `sort` + `dir` no servidor.
- `bun run build` encerra com código 0 e sem erros de tipo.

## Test Commands

```bash
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

- Criar `src/app/admin/layout.tsx` ao invés de `src/app/(admin)/layout.tsx`.
- Realizar a verificação de autenticação no lado do cliente, o que pode ser contornado.
- Usar interpolação de strings em consultas SQL: `` `SELECT * FROM users WHERE role = '${role}'` ``.
- Buscar estatísticas de KPI sequencialmente ao invés de usar `Promise.all`.

## Fix Prompt

```txt title="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.
```