SaaS de gestion de cabinet médical : agent IA conversationnel de prise de rendez-vous, dossier patient avec codage WHO ICD-11, téléconsultation, site vitrine no-code et paiements hybrides (Stripe + USDC).
- Framework : Next.js 15 (App Router) · React 19 · TypeScript 5
- UI : Tailwind 3 + Radix (shadcn) ·
next-themes(clair/sombre) ·next-intl(fr / en) - Base de données : Supabase (Postgres + Auth + RLS) — migrations SQL dans
supabase/migrations/ - IA : multi-fournisseurs — Claude, Gemini, OpenAI, Ollama (
lib/ai/router.ts) - Paiements : Stripe (carte) + USDC sur Polygon via ethers (
REQUIRED_CHAIN_ID = 0x89) - Intégrations : Twilio (SMS), Resend/nodemailer (email), Upstash (rate limit), Sentry, serveur MCP
- Tests : Vitest (environnement
node) · CI :.github/workflows/ci.yml(Node 24)
app/
(app)/app/… Espace praticien authentifié (dashboard, agenda, patients, diagnostics…)
(admin)/admin/… Super-admin multi-cliniques
(auth)/… login, signup
api/v1/… API REST publique (clés API + HMAC)
api/mcp/… Serveur MCP pour Claude Desktop
widget/[slug] Widget de réservation embarquable (iframe, hors middleware auth)
clinique/[slug] Site vitrine publié par le website builder
portail/… Portail patient
actions/ Server Actions — TOUTE mutation applicative passe par ici
components/ Par domaine (appointments, diagnostics, landing, widget…), pas par type
lib/ Logique métier pure et clients externes (ai/, stripe/, sms/, who-*.ts, rbac.ts)
messages/ fr.json + en.json — les deux locales doivent rester à parité
supabase/migrations/ Migrations SQL numérotées, jamais modifiées après merge
Règles structurantes :
- Mutations = Server Actions dans
actions/, pas de route API interne.app/api/est réservé aux webhooks, à l'API publiquev1, au MCP et aux proxys externes (WHO, OpenFDA). - Composants Server par défaut ;
"use client"uniquement si état, effet ou événement navigateur. - Isolation multi-tenant par RLS Supabase sur 100 % des tables, doublée par
lib/rbac.ts(super_admin>owner> staff). middleware.tsrafraîchit la session Supabase et pose l'en-têtex-user-authenticated.
- Toujours
git checkout main && git pull origin mainavant de créer une branche - Branches :
Feature/<code>-descriptionouFix/<code>-description(ex.Feature/061-sections-landing) - Commits :
<code>-titre-court(ex.061-ajoute-la-section-tarifs) - Minimum 30 commits atomiques par tâche, jamais de commit vide ou artificiel
- Ne jamais publier de mention d'outil IA dans les commits, PR ou commentaires GitHub
- Fichiers en
kebab-case(pricing-preview-section.tsx), composants enPascalCase - Jamais d'abréviation :
previousTotalpasprevTot,AdminOrderUpdateViewpasAdminOrdUpdView - Commentaires et messages utilisateur en français
- Immutabilité : créer un nouvel objet, ne jamais muter (
{ ...previous, field }) - Fichiers ≤ 800 lignes, fonctions ≤ 50 lignes — extraire plutôt que gonfler
- Validation aux frontières avec Zod (
lib/validations.ts), jamais faire confiance à une entrée externe - Pas de
any(règle ESLint active), pas deconsole.logen production
- Couleurs uniquement via les tokens de
app/globals.css(bg-card,text-muted-foreground,bg-primary/10) — aucune couleur sombre ou hexadécimale codée en dur, sinon le thème clair casse - Accent produit : teal (
--primary: 173 80% 30%) - Typographie :
font-cormorant(titres),font-sans= DM Sans (corps),font-mono= IBM Plex Mono (micro-libellés) - Tout texte visible passe par
next-intl— ajouter la clé dansfr.jsoneten.json
- Un composant = une responsabilité ; découper dès qu'un fichier dépasse ~250 lignes
getTranslationscôté serveur,useTranslationscôté client- État partagé : Zustand (
lib/store/) ou React Query — pas de prop drilling - Pas de valeur en dur : constantes en tête de fichier ou config partagée (
lib/subscription/pricing-plans.ts)
npm run dev # serveur de développement
npm run build # build de production (à lancer avant toute PR)
npm run lint # ESLint (dette préexistante : ne pas aggraver)
npm run test:run # Vitest une passe
npm run test:coverage # couverture (seuils : 80 lignes / 80 fonctions / 70 branches)
npm run db:migrate # supabase db push
npm run db:generate # régénère types/supabase.ts
npm run seed # données de démonstration- Tests dans
__tests__/lib/, environnementnode— pas de DOM, donc pas de rendu de composants - Tester en priorité : logique métier
lib/, sécurité/isolation, parité i18n, configuration partagée - Écrire le test avant le correctif sur un bug : reproduire (RED), corriger (GREEN), refactorer
- Avant toute PR :
npx tsc --noEmit,npm run test:runetnpm run builddoivent passer
- Lire le code existant et identifier le pattern en place — ne jamais supposer, inférer depuis la codebase
- Vérifier si une fonctionnalité existe déjà (
lib/,actions/) avant d'en écrire une nouvelle
- Toute tâche à 3+ étapes : planifier d'abord (fichiers affectés, risques, rollback)
- Si le plan dévie en cours de route : stop et re-planification
- Le code tourne sans erreur, les logs sont propres, les edge cases sont gérés
- Les fonctionnalités existantes ne sont pas cassées
- Vérifier le rendu en thème clair et sombre pour tout changement d'interface
- Reproduire · 2. Lire les logs · 3. Localiser la cause racine · 4. Corriger à la source, pas le symptôme
- Muter un objet ou un tableau existant
- Contourner la RLS avec la clé
service_rolesans contrôle d'autorisation explicite - Écrire une route API interne pour une mutation qui devrait être une Server Action
- Coder une couleur en dur (
bg-[#14b8a6],bg-white,text-white) au lieu d'un token de thème - Ajouter une chaîne visible sans sa traduction dans les deux locales
- Modifier une migration déjà mergée — en créer une nouvelle
- Dupliquer prix, quotas ou limites : source unique dans
lib/subscription/ - Avaler une erreur silencieusement (
catch {}sans retour utilisateur ni log) - Ignorer un échec de test ou de build « parce que ça marche en local »