{
  "id": "create-an-admin-dashboard",
  "type": "prompts",
  "category": "prompts",
  "locale": "fr",
  "url": "/fr/prompts/create-an-admin-dashboard",
  "title": "Prompt pour créer un tableau de bord admin dans Next.js",
  "description": "Prompt d'agent IA pour construire un tableau de bord admin protégé dans Next.js avec des tableaux de données, des gardes d'authentification côté serveur et une disposition de barre latérale utilisant Tailwind.",
  "tools": [
    "Cursor",
    "Claude Code",
    "Codex",
    "Windsurf"
  ],
  "stack": [
    "Next.js",
    "PostgreSQL",
    "TypeScript",
    "Tailwind"
  ],
  "tags": [
    "nextjs",
    "tailwind",
    "typescript",
    "auth",
    "postgres"
  ],
  "difficulty": "hard",
  "updated": "2026-06-08",
  "markdown": "Donnez ce prompt à votre agent pour échafauder une zone admin avec restriction de rôle, avec une barre latérale persistante, des tableaux de données triables et des vérifications d'authentification côté serveur — sans qu'il échafaude un nouveau projet Next.js complet ou installe un framework admin lourd.\n\n## Prompt Principal\n\n```txt title=\"Main Prompt\"\nYou are working in an existing Next.js App Router project with TypeScript, Tailwind CSS v4,\nand PostgreSQL. Auth is already set up (assume a `getSession()` helper exists in `src/lib/get-session.ts`\nthat returns `{ user: { id, email, role } } | null`).\n\nTask: add an admin dashboard at the `/admin` route group.\n\nRequirements:\n- Create a `src/app/(admin)/layout.tsx` that:\n  - Calls `getSession()` and redirects to `/login` if the session is null or `role !== 'admin'`.\n  - Renders a persistent sidebar with links: Dashboard, Users, Settings.\n  - Uses Tailwind for layout — a fixed-width sidebar and a scrollable main area.\n- Create `src/app/(admin)/dashboard/page.tsx` with:\n  - Four KPI cards: Total Users, Active Today, Revenue (MRR), Churn Rate — all fetched from\n    PostgreSQL using parameterized queries via `postgres` npm package.\n  - A `<UsersTable>` server component that renders the 50 most recent users (id, email, role,\n    created_at) with sortable column headers (URL-based sort param).\n- Create `src/components/admin/KpiCard.tsx` and `src/components/admin/UsersTable.tsx`.\n- All DB queries must use parameterized placeholders — never string interpolation.\n- Do not install Prisma, Drizzle, or any ORM. Use the `postgres` package directly.\n- Do not use any pre-built admin UI library (Refine, AdminJS, etc.).\n\nStop and list all planned file changes before writing any code.\n```\n\n## Notes d'implémentation\n\n- Les parenthèses du groupe de routes `(admin)` gardent `/admin/dashboard` hors de l'URL tout en partageant la mise en page — l'agent oublie parfois les parenthèses et crée un segment littéral `/admin`.\n- Les colonnes triables doivent utiliser les paramètres d'URL `?sort=email&dir=asc` lus dans le composant serveur via la prop `searchParams` — aucun état client n'est nécessaire.\n- Les requêtes KPI doivent être exécutées en parallèle avec `Promise.all` pour minimiser les allers-retours à la base de données.\n\n## Modifications de fichiers attendues\n\n```txt\nsrc/app/(admin)/layout.tsx                  (new)\nsrc/app/(admin)/dashboard/page.tsx          (new)\nsrc/components/admin/KpiCard.tsx            (new)\nsrc/components/admin/UsersTable.tsx         (new)\nsrc/lib/queries/admin-stats.ts             (new)\n```\n\n## Critères d'acceptation\n\n- Visiter `/dashboard` en tant qu'utilisateur non admin redirige vers `/login`.\n- Les cartes KPI affichent des valeurs réelles provenant de PostgreSQL.\n- La table des utilisateurs est triée par les paramètres d'URL `sort` et `dir` côté serveur.\n- `bun run build` se termine avec le code 0 et sans erreurs de type.\n\n## Commandes de test\n\n```bash\nbun run typecheck\nbun run build\nbun run dev\n# log in as admin and visit /dashboard\n# visit /dashboard without auth — confirm redirect to /login\n# add ?sort=email&dir=asc to the URL and confirm table order\n```\n\n## Erreurs courantes de l'IA\n\n- Créer `src/app/admin/layout.tsx` au lieu de `src/app/(admin)/layout.tsx`.\n- Effectuer la vérification d'authentification côté client, ce qui est contournable.\n- Utiliser l'interpolation de chaîne dans les requêtes SQL : `` `SELECT * FROM users WHERE role = '${role}'` ``.\n- Récupérer les statistiques KPI de manière séquentielle au lieu d'utiliser `Promise.all`.\n\n## Prompt de correction\n\n```txt title=\"Fix Prompt\"\nThe admin layout does not redirect unauthenticated users, or the route group folder name is wrong.\nFix in order:\n1. Rename `src/app/admin/` to `src/app/(admin)/` — the parentheses are required for a route group.\n2. In `layout.tsx`, add the auth check at the very top using `getSession()` and `redirect('/login')`.\n3. Audit all SQL query strings and replace any interpolated variables with `$1`, `$2` placeholders.\nShow the corrected diff only.\n```"
}