FileAttente
SaaS de gestion de liste d'attente. 7 Ă©tapes, dans l'ordre. Tu exĂ©cutes chaque action toi-mĂȘme, dans ton terminal.
Réutilisable vs spécifique : les étapes réutilisable forment un template valable pour n'importe quel futur SaaS de ta stack. Les étapes spécifique sont propres au modÚle de données de FileAttente (
Queue/Entry) â Ă chaque nouveau projet, seule cette partie change vraiment.
Initialiser le projet Next.js
Un projet Next.js qui dĂ©marre en local, avec Tailwind et shadcn/ui prĂȘts Ă l'emploi.
Prérequis : Node.js installé. Terminal ouvert dans le dossier du projet.
-
Installe pnpm si besoin
npm install -g pnpm
pnpm est le gestionnaire de paquets choisi pour ce projet â plus rapide et plus Ă©conome en espace disque que npm.đ Comment voir le rĂ©sultat : tapepnpm -v. Un numĂ©ro de version s'affiche (ex.9.12.0) â sinon l'installation a Ă©chouĂ©. -
Crée le projet Next.js
pnpm create next-app@latest . --typescript --tailwind --eslint --app --src-dir=false --import-alias "@/*"
Le nom du dossier ne doit pas contenir de majuscules (contrainte npm). S'il en contient, crĂ©e le projet dans un dossier temporaire au nom valide puis dĂ©place tout son contenu dans ton dossier de travail.đ Comment voir le rĂ©sultat : tapels. Tu dois voir apparaĂźtreapp/,package.json,tailwind.config.tsâ la commande a rempli ton dossier de fichiers. -
Initialise shadcn/ui
pnpm dlx shadcn@latest init
Ăa crĂ©ecomponents.jsonetlib/utils.ts, et prĂ©pare Tailwind pour recevoir des composants shadcn/ui prĂȘts Ă copier.đ Comment voir le rĂ©sultat : tapels components.json lib/utils.tsâ les deux fichiers doivent exister, sans message d'erreur « No such file ». -
Crée l'arborescence du projet
mkdir -p lib/auth lib/db lib/validations prisma testsmkdir lib/auth, lib/db, lib/validations, prisma, tests
On sĂ©pare dĂšs le dĂ©part : authentification, accĂšs base de donnĂ©es, schĂ©mas de validation. Ăa Ă©vite le fichier fourre-tout de 2000 lignes plus tard.đ Comment voir le rĂ©sultat : tapels libâ les dossiersauth,db,validationsdoivent apparaĂźtre. -
Initialise Git et fais le premier commit
git init git add -A git commit -m "chore: init projet Next.js"
đ Comment voir le rĂ©sultat : tapegit log --onelineâ une ligne avec ton message de commit doit s'afficher. -
Lance le serveur et vérifie
pnpm dev
Ouvrehttp://localhost:3000. Si tu as une erreur 500 mentionnant un module introuvable (ex.Can't resolve 'xxx') alors qu'il est bien danspackage.json: supprimenode_modules,pnpm-lock.yamlet.next, relancepnpm installpuispnpm dev. C'est presque toujours une installation incomplĂšte, pas un bug de code.đ Comment voir le rĂ©sultat : dans ton navigateur, ouvrelocalhost:3000. Tu dois voir la page d'accueil par dĂ©faut de Next.js. Le terminal afficheReadyet n'affiche pas d'erreur rouge.
Base de donnĂ©es â Prisma + Supabase
Une base PostgreSQL en ligne, avec deux tables (Queue, Entry) créées via Prisma.
Prérequis : étape 1 validée.
-
CrĂ©e un projet sur SupabaseVa sur supabase.com â New project. Choisis un nom, une rĂ©gion proche, et un mot de passe de base de donnĂ©es â note-le quelque part.đ Comment voir le rĂ©sultat : le tableau de bord Supabase affiche ton projet avec un statut « Active » (aprĂšs ~1-2 minutes).
-
RĂ©cupĂšre l'URL de connexionDans le projet Supabase : Project Settings â Database â Connection string, mode URI. Copie l'URL complĂšte (mot de passe inclus).
-
Installe Prisma
pnpm add prisma --save-dev pnpm add @prisma/client pnpm dlx prisma init
CrĂ©eprisma/schema.prismaet un fichier.envavec une variableDATABASE_URLvide.đ Comment voir le rĂ©sultat : tapels prisma/schema.prismaâ le fichier doit exister. -
Colle l'URL dans
.env# .env DATABASE_URL="postgresql://postgres:[MOT-DE-PASSE]@[HOST]:5432/postgres"
Ce fichier ne doit jamais ĂȘtre commit (dĂ©jĂ dans.gitignorepar dĂ©faut) â il contient un mot de passe. -
Ăcris le schĂ©maDans
prisma/schema.prisma, sous les blocsgenerator/datasourcedéjà présents, ajoute :model Queue { id String @id @default(cuid()) name String ownerId String createdAt DateTime @default(now()) entries Entry[] } model Entry { id String @id @default(cuid()) queueId String queue Queue @relation(fields: [queueId], references: [id]) name String note String? status String @default("WAITING") createdAt DateTime @default(now()) } -
Applique la migration et vérifie
pnpm dlx prisma migrate dev --name init pnpm dlx prisma studio
migrate devcrĂ©e les tables et gĂ©nĂšre le client Prisma typĂ©.studioouvre une interface web locale pour voir/Ă©diter les donnĂ©es Ă la main.đ Comment voir le rĂ©sultat :prisma studioouvrelocalhost:5555â les tablesQueueetEntrydoivent apparaĂźtre dans la liste de gauche.
PiĂšge frĂ©quent : connexion refusĂ©e â vĂ©rifie qu'aucun caractĂšre spĂ©cial du mot de passe (@, #...) n'est mal encodĂ© dans l'URL. Supabase propose un bouton pour copier l'URL dĂ©jĂ encodĂ©e.
Authentification â Better Auth
S'inscrire, se connecter, et protéger /dashboard des visiteurs non connectés.
Prérequis : étape 2 validée (schéma Prisma en place).
-
Installe Better Auth
pnpm add better-auth
đ Comment voir le rĂ©sultat :better-authapparaĂźt danspackage.json. -
Crée le fichier du client Prisma
touch lib/db/client.tsNew-Item -ItemType File -Force -Path lib/db/client.ts
đ Comment voir le rĂ©sultat : tapels lib/dbâclient.tsdoit apparaĂźtre. -
Colle le contenu du client Prisma partagé
// lib/db/client.ts import { PrismaClient } from "@prisma/client"; const globalForPrisma = globalThis as unknown as { prisma: PrismaClient }; export const prisma = globalForPrisma.prisma ?? new PrismaClient(); if (process.env.NODE_ENV !== "production") globalForPrisma.prisma = prisma;Une instance unique dePrismaClient, rĂ©utilisĂ©e partout viaimport { prisma } from "@/lib/db/client"â Ă©vite d'en recrĂ©er une Ă chaque appel (et les erreurs « too many connections » que ça provoque avec le rechargement Ă chaud de Next.js en dĂ©veloppement). -
Crée le fichier de config serveur
touch lib/auth/auth.tsNew-Item -ItemType File -Force -Path lib/auth/auth.ts
-
Colle le contenu de la config serveur
// lib/auth/auth.ts import { betterAuth } from "better-auth"; import { prismaAdapter } from "better-auth/adapters/prisma"; import { prisma } from "@/lib/db/client"; export const auth = betterAuth({ database: prismaAdapter(prisma, { provider: "postgresql" }), emailAndPassword: { enabled: true }, }); -
Ajoute les tables auth au schéma
pnpm dlx @better-auth/cli generate pnpm dlx prisma migrate dev --name add_auth
Better Auth gĂ©nĂšre automatiquement les modĂšlesUser,Session,Accountdansschema.prisma.đ Comment voir le rĂ©sultat : ouvreprisma studioâ les tablesUser,Session,Accountdoivent apparaĂźtre. -
Ajoute le secret dans
.env# .env BETTER_AUTH_SECRET="génÚre une chaßne aléatoire longue" BETTER_AUTH_URL="http://localhost:3000"
-
Crée le dossier et le fichier de la route API
mkdir -p "app/api/auth/[...all]" && touch "app/api/auth/[...all]/route.ts"New-Item -ItemType Directory -Force -Path "app/api/auth/[...all]"; New-Item -ItemType File -Force -Path "app/api/auth/[...all]/route.ts"
Le nom de dossier entre crochets[...all]est la syntaxe Next.js pour une route qui capture toutes les sous-routes â indispensable pour que Better Auth gĂšre toutes ses URLs (/api/auth/sign-in,/api/auth/sign-up, etc.) avec un seul fichier. -
Colle le contenu de la route API
// app/api/auth/[...all]/route.ts import { auth } from "@/lib/auth/auth"; import { toNextJsHandler } from "better-auth/next-js"; export const { GET, POST } = toNextJsHandler(auth);đ Comment voir le rĂ©sultat : relancepnpm devet ouvre un endpoint Better Auth (ex.localhost:3000/api/auth/ok) â tu dois obtenir une rĂ©ponse JSON, pas une erreur 404. -
Crée le fichier du client auth
touch lib/auth/client.tsNew-Item -ItemType File -Force -Path lib/auth/client.ts
// lib/auth/client.ts import { createAuthClient } from "better-auth/react"; export const authClient = createAuthClient(); -
Crée les dossiers et fichiers des pages
/signupet/loginmkdir -p app/signup app/login && touch app/signup/page.tsx app/login/page.tsxmkdir app/signup, app/login; New-Item -ItemType File -Force -Path app/signup/page.tsx, app/login/page.tsx
-
Colle le contenu de la page
/signup// app/signup/page.tsx "use client"; import { useState } from "react"; import { useRouter } from "next/navigation"; import { authClient } from "@/lib/auth/client"; export default function SignupPage() { const [email, setEmail] = useState(""); const [password, setPassword] = useState(""); const [error, setError] = useState(""); const router = useRouter(); async function handleSubmit(e: React.FormEvent) { e.preventDefault(); const { error } = await authClient.signUp.email({ email, password, name: email }); if (error) return setError(error.message ?? "Inscription refusĂ©e"); router.push("/dashboard"); } return ( <form onSubmit={handleSubmit} className="max-w-sm mx-auto p-8 flex flex-col gap-3"> <input type="email" placeholder="Email" value={email} onChange={(e) => setEmail(e.target.value)} className="border p-2 rounded" /> <input type="password" placeholder="Mot de passe" value={password} onChange={(e) => setPassword(e.target.value)} className="border p-2 rounded" /> {error && <p className="text-red-500 text-sm">{error}</p>} <button type="submit" className="bg-black text-white p-2 rounded">CrĂ©er un compte</button> </form> ); }Duplique la mĂȘme structure pourapp/login/page.tsx(fonctionLoginPage) en remplaçantauthClient.signUp.email(...)parauthClient.signIn.email({ email, password }).đ Comment voir le rĂ©sultat : crĂ©e un compte test surlocalhost:3000/signup, puis vĂ©rifie dans Prisma Studio qu'une ligne apparaĂźt dans la tableUser. Reconnecte-toi ensuite surlocalhost:3000/loginavec les mĂȘmes identifiants. -
Crée le fichier
middleware.tsĂ la racine du projettouch middleware.tsNew-Item -ItemType File -Force -Path middleware.ts
« Ă la racine » veut dire au mĂȘme niveau que le dossierapp/, pas dedans. -
Colle le contenu du
middleware.ts// middleware.ts import { NextRequest, NextResponse } from "next/server"; import { getSessionCookie } from "better-auth/cookies"; export function middleware(request: NextRequest) { const sessionCookie = getSessionCookie(request); if (!sessionCookie) { return NextResponse.redirect(new URL("/login", request.url)); } return NextResponse.next(); } export const config = { matcher: ["/dashboard/:path*"], };VĂ©rifie seulement la prĂ©sence du cookie de session (rapide, pas d'appel base de donnĂ©es) â assez pour bloquer l'accĂšs Ă la mise en page, la vraie vĂ©rification de session a dĂ©jĂ lieu dans les Server Actions et les pages.đ Comment voir le rĂ©sultat : en navigation privĂ©e (non connectĂ©), va surlocalhost:3000/dashboardâ tu dois ĂȘtre redirigĂ© vers/login.
PiĂšge frĂ©quent : si la doc officielle de Better Auth diffĂšre de ce qui est Ă©crit ici, suis la doc â c'est une librairie qui Ă©volue vite. Dis-le moi si un nom de fonction a changĂ©, on ajuste.
CRUD Files d'attente
Créer, lister et supprimer une file depuis /dashboard.
Prérequis : étape 3 validée (session utilisateur disponible).
-
Ajoute des composants shadcn/ui
pnpm dlx shadcn@latest add button input card
đ Comment voir le rĂ©sultat :ls components/uiâbutton.tsx,input.tsx,card.tsxdoivent apparaĂźtre. -
Crée le dossier
app/dashboardet le fichier des Server Actionsmkdir -p app/dashboard && touch app/dashboard/actions.tsNew-Item -ItemType Directory -Force -Path app/dashboard; New-Item -ItemType File -Force -Path app/dashboard/actions.ts
-
Crée le fichier de validation Zod
touch lib/validations/queue.tsNew-Item -ItemType File -Force -Path lib/validations/queue.ts
// lib/validations/queue.ts import { z } from "zod"; export const createQueueSchema = z.object({ name: z.string().min(1).max(80), });Ăcris ce fichier avant les Server Actions ci-dessous â elles l'importent. -
Colle le contenu des Server Actions
// app/dashboard/actions.ts "use server"; import { prisma } from "@/lib/db/client"; import { auth } from "@/lib/auth/auth"; import { headers } from "next/headers"; import { createQueueSchema } from "@/lib/validations/queue"; import { revalidatePath } from "next/cache"; export async function createQueue(formData: FormData) { const session = await auth.api.getSession({ headers: await headers() }); if (!session) throw new Error("Non connectĂ©"); const parsed = createQueueSchema.safeParse({ name: formData.get("name") }); if (!parsed.success) throw new Error("Nom invalide"); await prisma.queue.create({ data: { name: parsed.data.name, ownerId: session.user.id }, }); revalidatePath("/dashboard"); } export async function deleteQueue(id: string) { const session = await auth.api.getSession({ headers: await headers() }); if (!session) throw new Error("Non connectĂ©"); const queue = await prisma.queue.findUnique({ where: { id } }); if (queue?.ownerId !== session.user.id) throw new Error("Non autorisĂ©"); await prisma.queue.delete({ where: { id } }); revalidatePath("/dashboard"); }La vĂ©rificationownerId === session.user.idcĂŽtĂ© serveur est obligatoire â sans elle, n'importe quel utilisateur connectĂ© pourrait supprimer les files d'un autre. -
Crée le fichier de la page
/dashboardtouch app/dashboard/page.tsxNew-Item -ItemType File -Force -Path app/dashboard/page.tsx
-
Colle le contenu de la page
/dashboard// app/dashboard/page.tsx import { prisma } from "@/lib/db/client"; import { auth } from "@/lib/auth/auth"; import { headers } from "next/headers"; import { createQueue, deleteQueue } from "./actions"; export default async function DashboardPage() { const session = await auth.api.getSession({ headers: await headers() }); const queues = await prisma.queue.findMany({ where: { ownerId: session!.user.id } }); return ( <main className="p-8"> <h1 className="text-2xl font-bold mb-4">Mes files d'attente</h1> <form action={createQueue} className="flex gap-2 mb-6"> <input name="name" placeholder="Nom de la file" className="border p-2 rounded" required /> <button type="submit" className="bg-black text-white px-4 rounded">CrĂ©er</button> </form> <ul className="flex flex-col gap-2"> {queues.map((q) => ( <li key={q.id} className="flex justify-between border p-3 rounded"> <a href={`/dashboard/queues/${q.id}`}>{q.name}</a> <form action={deleteQueue.bind(null, q.id)}> <button type="submit" className="text-red-500">Supprimer</button> </form> </li> ))} </ul> </main> ); }đ Comment voir le rĂ©sultat : connectĂ© surlocalhost:3000/dashboard, crĂ©e une file test via le formulaire â elle doit apparaĂźtre immĂ©diatement dans la liste, et une nouvelle ligne doit exister dans Prisma Studio (tableQueue).
CRUD Entrées
Ajouter une personne Ă une file, la marquer servie ou la retirer.
Prérequis : étape 4 validée.
-
Crée le dossier et le fichier des actions
mkdir -p "app/dashboard/queues/[id]" && touch "app/dashboard/queues/[id]/actions.ts"New-Item -ItemType Directory -Force -Path "app/dashboard/queues/[id]"; New-Item -ItemType File -Force -Path "app/dashboard/queues/[id]/actions.ts"
Le dossier entre crochets[id]est la syntaxe Next.js pour une route dynamique âiddevient accessible dans la page viaparams.id. -
Colle le contenu des actions
// app/dashboard/queues/[id]/actions.ts "use server"; import { prisma } from "@/lib/db/client"; import { auth } from "@/lib/auth/auth"; import { headers } from "next/headers"; import { revalidatePath } from "next/cache"; async function assertOwnership(queueId: string) { const session = await auth.api.getSession({ headers: await headers() }); const queue = await prisma.queue.findUnique({ where: { id: queueId } }); if (!session || queue?.ownerId !== session.user.id) throw new Error("Non autorisĂ©"); return queue; } export async function addEntry(queueId: string, formData: FormData) { await assertOwnership(queueId); const name = formData.get("name") as string; await prisma.entry.create({ data: { queueId, name, status: "WAITING" } }); revalidatePath(`/dashboard/queues/${queueId}`); } export async function markServed(entryId: string) { const entry = await prisma.entry.findUniqueOrThrow({ where: { id: entryId } }); await assertOwnership(entry.queueId); await prisma.entry.update({ where: { id: entryId }, data: { status: "SERVED" } }); revalidatePath(`/dashboard/queues/${entry.queueId}`); } export async function removeEntry(entryId: string) { const entry = await prisma.entry.findUniqueOrThrow({ where: { id: entryId } }); await assertOwnership(entry.queueId); await prisma.entry.delete({ where: { id: entryId } }); revalidatePath(`/dashboard/queues/${entry.queueId}`); }Ăcris ce fichier avant la page ci-dessous â elle l'importe.assertOwnershipvĂ©rifie que la file appartient bien Ă l'utilisateur connectĂ© avant d'agir â sans ça, n'importe qui pourrait modifier les entrĂ©es d'une file qui n'est pas la sienne. -
Crée le fichier de la page de détail
touch "app/dashboard/queues/[id]/page.tsx"New-Item -ItemType File -Force -Path "app/dashboard/queues/[id]/page.tsx"
-
Colle le contenu de la page de détail
// app/dashboard/queues/[id]/page.tsx import { prisma } from "@/lib/db/client"; import { auth } from "@/lib/auth/auth"; import { headers } from "next/headers"; import { notFound } from "next/navigation"; import { addEntry, markServed, removeEntry } from "./actions"; export default async function QueueDetailPage({ params }: { params: { id: string } }) { const session = await auth.api.getSession({ headers: await headers() }); const queue = await prisma.queue.findUnique({ where: { id: params.id }, include: { entries: { orderBy: { createdAt: "asc" } } }, }); if (!queue || queue.ownerId !== session?.user.id) notFound(); return ( <main className="p-8"> <h1 className="text-2xl font-bold mb-4">{queue.name}</h1> <form action={addEntry.bind(null, queue.id)} className="flex gap-2 mb-6"> <input name="name" placeholder="Nom de la personne" className="border p-2 rounded" required /> <button type="submit" className="bg-black text-white px-4 rounded">Ajouter</button> </form> <ul className="flex flex-col gap-2"> {queue.entries.map((e) => ( <li key={e.id} className="flex justify-between border p-3 rounded"> <span>{e.name} â {e.status}</span> <div className="flex gap-2"> <form action={markServed.bind(null, e.id)}><button type="submit">Marquer servi</button></form> <form action={removeEntry.bind(null, e.id)}><button type="submit" className="text-red-500">Retirer</button></form> </div> </li> ))} </ul> </main> ); }Les entrĂ©es sont triĂ©es parcreatedAtcroissant (premier arrivĂ©, premier affichĂ©) directement dans la requĂȘte Prisma.đ Comment voir le rĂ©sultat : clique sur une de tes files depuis/dashboardâ tu dois arriver sur une page dĂ©diĂ©e avec son nom affichĂ©. Ajoute 2-3 entrĂ©es test via le formulaire â elles doivent apparaĂźtre dans l'ordre d'ajout avec le statut « WAITING ». Clique sur « Marquer servi » : le statut affichĂ© doit passer à « SERVED » sans recharger la page manuellement.
Habillage â Tailwind & shadcn/ui
Un rendu visuel cohérent sur tout le site, et une méthode simple pour le modifier plus tard.
Prérequis : étape 5 validée.
-
Comprends oĂč vit le styleIl n'y a pas un fichier
.csspar page. Deux niveaux seulement : les variables de thĂšme globales dansapp/globals.css(couleurs, arrondi â gĂ©nĂ©rĂ©es parshadcn initĂ l'Ă©tape 1, chargĂ©es une seule fois parapp/layout.tsx), et les classes Tailwind Ă©crites directement dans leclassNamede chaque composant. -
Personnalise les couleurs du thĂšmeOuvre
app/globals.cssâ repĂšre le bloc:root { ... }gĂ©nĂ©rĂ© parshadcn init, qui contient des variables comme--primaryou--background. Change leurs valeurs directement dans ce fichier.đ Comment voir le rĂ©sultat : modifie la valeur de--primary, enregistre, rechargelocalhost:3000/dashboardâ n'importe quel composant shadcn/ui affichĂ© sur le dashboard doit changer de couleur automatiquement, sans toucher au code des pages. -
Crée une mise en page cohérente, réutilisée sur chaque page
touch components/container.tsxNew-Item -ItemType File -Force -Path components/container.tsx
// components/container.tsx export default function Container({ children }: { children: React.ReactNode }) { return ( <div className="max-w-5xl mx-auto px-4 py-12"> {children} </div> ); }Sans ça, tu rĂ©pĂštes les mĂȘmes classes d'espacement (max-w-5xl mx-auto px-4 py-12) dans chaque page du dashboard â un seul endroit Ă modifier si tu changes la largeur ou les marges du site.đ Comment voir le rĂ©sultat : enveloppe le contenu retournĂ© parapp/dashboard/page.tsxetapp/dashboard/queues/[id]/page.tsxavec<Container>...</Container>â chaque page doit s'afficher centrĂ©e, avec la mĂȘme marge, une fois rechargĂ©e. -
Remplace le HTML brut par les composants shadcn/ui déjà installés
// avant â dans app/dashboard/queues/[id]/page.tsx <button type="submit" className="bg-black text-white px-4 rounded">Ajouter</button> // aprĂšs import { Button } from "@/components/ui/button"; <Button type="submit">Ajouter</Button>
Le composantButtonhĂ©rite automatiquement des variables de thĂšme dĂ©finies plus haut â un<button>HTML brut, non. Change--primaryet seul le premier ne suivra pas. -
Pour modifier le style plus tardGarde
pnpm devlancĂ© en continu â le rechargement Ă chaud applique tes changements dans le navigateur dĂšs que tu sauvegardes, sans redĂ©marrer le serveur. Ouvre le fichier de la page ou du composant concernĂ©, change les classes dansclassName, enregistre. Pour ajouter un nouveau composant shadcn/ui :pnpm dlx shadcn@latest add <nom>(liste sur ui.shadcn.com). Pour savoir ce que fait une classe (p-4,rounded-lg,text-xl...), consulte le lexique ou tailwindcss.com/docs.
Tests â Vitest & Playwright
Automatiser la vérification du parcours critique.
Prérequis : étapes 4 et 5 validées.
-
Installe et configure Vitest
pnpm add -D vitest
Ăcris un test danstests/queue.validation.test.tsqui vĂ©rifie quecreateQueueSchemarejette un nom vide et accepte un nom valide. -
Installe et configure Playwright
pnpm create playwright
RĂ©pond aux questions de l'installateur (TypeScript, dossiertests-e2e). Ăcris un test qui : crĂ©e un compte â se connecte â crĂ©e une file â ajoute une entrĂ©e â la marque servie. -
Lance les tests
pnpm exec vitest run pnpm exec playwright test
đ Comment voir le rĂ©sultat : le terminal affiche un rĂ©sumĂ© avec un nombre de tests « passed » en vert. Playwright peut ouvrir un rapport HTML (pnpm exec playwright show-report) qui rejoue le parcours Ă©tape par Ă©tape, avec captures d'Ă©cran si un test Ă©choue.
DĂ©ploiement â Vercel
Une URL publique et fonctionnelle.
Prérequis : étape 7 validée.
-
Ajoute la génération Prisma au build
// package.json "scripts": { "postinstall": "prisma generate" }Sans ça, le build Vercel plante â le client Prisma doit ĂȘtre rĂ©gĂ©nĂ©rĂ© aprĂšs chaque install en production. -
Pousse le code sur GitHub
git remote add origin URL-DE-TON-DEPOT git push -u origin masterđ Comment voir le rĂ©sultat : rafraĂźchis la page du dĂ©pĂŽt sur GitHub â tes fichiers doivent y apparaĂźtre. -
Importe le projet sur VercelSur vercel.com â Add New â Project â sĂ©lectionne le dĂ©pĂŽt GitHub.đ Comment voir le rĂ©sultat : le tableau de bord Vercel passe de « Building » à « Ready », avec un lien de prĂ©visualisation cliquable.
-
Renseigne les variables d'environnementCopie
DATABASE_URL,BETTER_AUTH_SECRET,BETTER_AUTH_URL(mets l'URL Vercel finale) dans Settings â Environment Variables.đ Comment voir le rĂ©sultat : pas d'effet visible immĂ©diatement â redĂ©ploie (« Redeploy ») puis teste Ă l'Ă©tape suivante. -
DĂ©ploie et vĂ©rifie en ligneOuvre l'URL fournie par Vercel, refais le parcours critique Ă la main : inscription â crĂ©er une file â ajouter une entrĂ©e â marquer servie.đ Comment voir le rĂ©sultat : sur l'URL
*.vercel.app, le parcours complet doit fonctionner exactement comme en local â crĂ©ation de compte, file, entrĂ©e, tout doit persister en base (vĂ©rifiable dans Supabase).