Biz Next.js Chatbot OpenRouter — Installe un chatbot IA flottant sur un site en un clin d'œil
Cette compétence ajoute un widget chatbot IA flottant à un projet Next.js existant. Le widget se trouve dans le coin inférieur droit, est entièrement responsive sur les 3 écrans, utilise un LLM via OpenRouter avec une base de connaissances provenant de la FAQ + de la documentation du cours/produit, affiche le Markdown dans les bulles de l'assistant (gras pour les nombres/prix, listes à puces, liens cliquables, code inline) grâce à react-markdown + remark-gfm, et extrait et sauvegarde automatiquement les leads (nom + téléphone + email) dans KV (Upstash Redis / Vercel KV) lorsque l'utilisateur fournit les informations complètes dans le chat — consultable par l'admin via le tableau de bord /admin. Objectif : solopreneur / créateur de cours avec bot de support automatique 24/7 + capture passive de leads directement sur la page de vente du cours, sans perdre aucun client qui pose une question via le chat.
Esprit : Détecter automatiquement tout ce qui peut l'être (version Next.js, App/Pages Router, TS/JS, Tailwind/CSS), ne demander à l'utilisateur que 4 choses vraiment nécessaires : (1) le projet s'il y en a plusieurs, (2) la FAQ, (3) la documentation produit, (4) le ton/le nom de la marque. Le reste est du code → opérationnel en quelques minutes.
Quand l'utiliser
L'utilisateur souhaite :
- Ajouter un chatbot IA d'assistance client sur une page de vente de cours / service
- Réduire la charge de réponse aux questions répétitives (prix, programme, remboursement, public cible...)
- Disposer d'un widget de chat compact en bas à droite, sans masquer le contenu
- Avoir un chatbot qui comprend précisément le contenu de son cours / produit (piloté par FAQ)
- Posséder déjà un projet Next.js (venant de terminer
/biz-sales-page-layout, ou un projet existant)
Quand NE PAS l'utiliser
- Le projet n'est pas Next.js (React pur Vite, Vue, Svelte, HTML simple) → cette compétence est spécialisée Next.js. Une adaptation est possible mais non optimale.
- Il faut un chatbot RAG (retrieval-augmented) avec une base de connaissances de milliers de pages → nécessite une base vectorielle (Pinecone/Supabase pgvector). Cette compétence utilise un prompt système simple adapté à ≤ 20K tokens de connaissances.
- Il faut un chatbot vocal / multimodal (envoi d'images) → cette compétence est limitée au texte.
- Il faut un chatbot qui sauvegarde l'historique des conversations par utilisateur dans une base de données → cette compétence est sans état, session éphémère, possibilité d'extension ultérieure.
- Le backend n'est pas compatible Vercel (nécessite un long polling, WebSocket persistant) → le streaming OpenRouter fonctionne bien sur Vercel Edge, mais si un WS dédié est nécessaire, c'est différent.
Ce que l'utilisateur recevra
Une fois la compétence exécutée :
-
Nouveaux fichiers dans le projet Next.js de l'utilisateur :
components/Chatbot.tsx(ou.jsx) — interface du widget (affiche le Markdown viareact-markdown+remark-gfm)app/api/chat/route.ts(App Router) OUpages/api/chat.ts(Pages Router) — proxy API appelant OpenRouter + extraction de leads après le streamlib/chatbot-knowledge.ts(ou.js) — base de connaissances + prompt système (comprend un bloc FORMAT DE RÉPONSE + un bloc COLLECTE ACTIVE DE LEADS)lib/chat-lead.ts— CRÉÉ UNIQUEMENT si le projet a KV/Upstash — extracteur + sauvegarde, dédoublonnage par téléphone, TTL 90 jours, espace de nomschat-lead:{phone}- Styles Markdown
.chatbot-mdajoutés àapp/globals.css(à partir detemplates/chatbot-markdown-styles.css) .env.localcréé/amendé avecOPENROUTER_API_KEY=(placeholder).env.example(s'il n'existe pas) pour documenter les variables d'environnement- Montage de
<Chatbot />dansapp/layout.tsxoupages/_app.tsx
-
Paquets installés (via le gestionnaire de paquets détecté à partir du fichier de verrouillage) :
react-markdown— affichage sécurisé du Markdown (sansdangerouslySetInnerHTML)remark-gfm— GitHub Flavored Markdown (tableaux, autolink, barré)
-
Instructions claires en fin de session :
- 3 étapes que l'utilisateur doit effectuer lui-même : (a) obtenir une clé API OpenRouter, (b) la coller dans
.env.local, (c)npm run devpour tester - Comment changer de modèle entre Gemini et Sonnet
- Comment mettre à jour la base de connaissances ultérieurement
- 3 étapes que l'utilisateur doit effectuer lui-même : (a) obtenir une clé API OpenRouter, (b) la coller dans
Workflow
Étape 1 — Détecter le projet Next.js
Rechercher les candidats projets Next.js. Exécuter :
# Chercher package.json contenant "next" dans les dépendances, profondeur max 4
find /Users/tonyhoang/Documents/GitHub -maxdepth 4 -name "package.json" -not -path "*/node_modules/*" 2>/dev/null | xargs grep -l '"next"' 2>/dev/null | head -20
(Si l'utilisateur se trouve dans un répertoire de travail spécifique, privilégier la recherche à partir du cwd en premier, sinon chercher dans ~/Documents/GitHub si rien n'est trouvé.)
Branchement :
- 0 projet : En informer l'utilisateur et s'arrêter. Suggestion : "Avez-vous un projet Next.js ? Sinon, voulez-vous que j'en crée un nouveau avec
/biz-sales-page-layoutpuis revenir ?" - 1 projet : Confirmer le chemin avec l'utilisateur avant de continuer : "Je vais installer le chatbot dans [chemin]. Cela vous convient-il ?"
- ≥ 2 projets : Lister les chemins + noms (
namedu package.json) dans une liste numérotée, demander à l'utilisateur de choisir : "J'ai trouvé N projets Next.js. Dans lequel souhaitez-vous installer le chatbot ?"
Étape 2 — Analyser le contexte du projet
Après que l'utilisateur a confirmé le projet, lire :
-
package.json→ vérifier :- TypeScript (
typescriptdans les dépendances ?) - Tailwind (
tailwindcssdans les devDeps ?) - Gestionnaire de paquets (fichier de verrouillage :
pnpm-lock.yaml→ pnpm,yarn.lock→ yarn, par défautnpm) - Version de Next.js
- TypeScript (
-
Structure des répertoires → vérifier :
- Le répertoire
app/existe → App Router - Le répertoire
pages/existe (etapp/n'existe pas) → Pages Router - Les deux existent → privilégier App Router pour les nouveaux fichiers (Next.js migre vers celui-ci)
- Wrapper
src/→ ajuster le chemin (ex.src/app/,src/components/)
- Le répertoire
-
tsconfig.json→ vérifier les alias de chemin (@/*→./src/*ou./*) -
.env.local,.env.example→ vérifier s'ils existent déjà -
Disponibilité KV/Upstash (pour la capture automatique de leads) :
package.jsoncontient@upstash/redisOU@vercel/kv→ KV présent → GÉNÉRER chat-lead.ts + l'intégrer à la route- Le fichier
src/lib/kv.ts(oulib/kv.ts) exportekv→ utiliser l'assistant existant (motif camp) - KV absent → NE PAS GÉNÉRER chat-lead.ts, informer l'utilisateur : "Le projet n'a pas de KV — la capture automatique de leads n'est pas activée. Je peux exécuter
/biz-setup-sepay-paymentpour installer KV d'abord, puis revenir activer la capture, ou passer outre."
Enregistrer ces observations dans un petit tableau pour les utiliser aux étapes suivantes.
Étape 3 — Collecter la base de connaissances auprès de l'utilisateur
Poser 4 questions à l'utilisateur (regroupées dans un seul message pour qu'il réponde en une fois, éviter les allers-retours) :
Pour que le chatbot réponde avec précision, j'ai besoin de 4 choses :
1. **FAQ** — liste des questions fréquentes + réponses (prix, programme, remboursement, public cible, durée, support après le cours...).
Collez directement ici, OU indiquez le chemin du fichier (.md / .txt / .docx).
2. **Documentation produit / cours** — description détaillée du cours : nom, contenu de chaque module, résultats attendus, formateur, bonus, paliers de prix s'il y en a plusieurs.
Collez également ou indiquez le chemin du fichier.
3. **Nom de la marque + ton** — Nom de la marque/du cours pour que le chatbot se présente (ex. "assistant de AI Mastery"). Ton utilisé : amical (par défaut) / formel / décontracté.
4. **Modèle** — par défaut `google/gemini-3-flash-preview` (rapide, peu coûteux, suffisant pour le support). Si vous voulez utiliser `anthropic/claude-sonnet-4.6` (meilleure qualité, ~10x plus cher) parce que la qualité des réponses précoces est insuffisante, dites-le moi.
Pouvez-vous répondre en une seule fois ?
Quand l'utilisateur répond :
- S'il indique un chemin de fichier → lire ce fichier avec l'outil de lecture.
- S'il colle le contenu directement → utiliser le contenu collé.
- Si l'utilisateur dit "génère un placeholder pour moi" → créer un placeholder FAQ générique et le marquer
// TODO : à remplir par l'utilisateurpour qu'il sache qu'il doit le mettre à jour. - Pas de nom de marque → par défaut "Assistant IA" avec le ton "vous".
Étape 4 — Générer les fichiers dans le projet
Lire les templates dans templates/ de cette compétence et les adapter au projet. Les templates :
templates/chatbot-knowledge.ts— Module exportant la base de connaissances + systemPrompt. Injecter FAQ, informations produit, nom de marque, ton. Contient déjà le bloc FORMAT DE RÉPONSE (règles Markdown) + bloc COLLECTE ACTIVE DE LEADS (apprend au LLM à demander nom/téléphone/email lors d'un signal d'achat, à remercier + confirmer quand l'utilisateur fournit, NE PAS dire "sauvegardé en base de données").templates/Chatbot.tsx— Composant widget, priorité Tailwind. Importe déjàreact-markdown+remark-gfm, affiche le message de l'assistant avec<ReactMarkdown remarkPlugins={[remarkGfm]}>(assistant uniquement — les messages utilisateur restent enwhitespace-pre-wrap). Si le projet N'A PAS Tailwind → fallbacktemplates/Chatbot-noTailwind.tsx+templates/chatbot.module.css.templates/chat-route-app.ts— Route API pour App Router (app/api/chat/route.ts). Contient déjà le hookpersistLeadIfReadyexécuté en fire-and-forget après la fermeture du stream — ne bloque pas la réponse.templates/chat-route-pages.ts— Route API pour Pages Router (pages/api/chat.ts).templates/chat-lead.ts— GÉNÉRÉ UNIQUEMENT si le projet a KV/Upstash — extraction par regex du téléphone VN + email + heuristique de nom (modèles d'introduction "nom X" / "je suis X" / "nom: X"),saveChatLeaddédoublonné par téléphone, TTL 90 jours, espace de noms de cléchat-lead:{phone}.templates/chatbot-markdown-styles.css— Extrait CSS pour la classe.chatbot-md. DOIT être ajouté à la fin deapp/globals.css(ousrc/app/globals.css/styles/globals.css). Modifier la couleur d'accent (#fde68a/#f59e0b) selon la palette de marque du projet avant d'ajouter.templates/env-instructions.md— Extrait d'instructions pour obtenir la clé OpenRouter, utilisé à la dernière étape.
Règles d'adaptation des templates :
-
TS vs JS : si le projet a seulement du JS, changer l'extension
.ts/.tsx→.js/.jsxet supprimer les annotations de type (ne pas seulement renommer le fichier). Lire le fichier modèle, analyser, supprimer les types, écrire. -
Alias de chemin : si
tsconfigmappe@/*→./src/*, les imports dans le template doivent utiliser@/lib/chatbot-knowledgeet le fichier être placé danssrc/lib/. S'il n'y a pas d'alias, utiliser un chemin relatif. -
Modèle : remplacer le placeholder
__MODEL_ID__par le modèle choisi par l'utilisateur (google/gemini-3-flash-previewouanthropic/claude-sonnet-4.6). -
Marque : remplacer
__BRAND_NAME__,__BRAND_TONE__. -
Connaissances : construire
systemPromptà partir de la FAQ + des informations produit sous forme structurée claire (voir le template). Conserver le bloc FORMAT DE RÉPONSE dans systemPrompt — c'est ce qui apprend au LLM à produire un Markdown adapté à une petite bulle de chat (gras plutôt que titres, puces courtes sur une ligne). -
Installer les paquets de rendu Markdown (OBLIGATOIRE — si ignoré, le chatbot affichera du
**gras**brut au lieu du texte en gras dans les bulles) :# détecter le fichier de verrouillage → choisir le bon gestionnaire de paquets npm install react-markdown remark-gfm # ou : pnpm add react-markdown remark-gfm # ou : yarn add react-markdown remark-gfm -
Ajouter les styles Markdown : lire
templates/chatbot-markdown-styles.css, échanger la couleur d'accent selon la palette de marque du projet (vérifierglobals.csspour--color-primaryou équivalent), puis ajouter à la fin deapp/globals.css(ousrc/app/globals.css/styles/globals.css). -
Monter le widget :
- App Router : éditer
app/layout.tsx(ousrc/app/layout.tsx), ajouterimport Chatbot from '@/components/Chatbot'et<Chatbot />juste avant</body>. Lire le layout.tsx actuel avant de l'éditer pour conserver les métadonnées, polices, fournisseurs. - Pages Router : éditer
pages/_app.tsx, envelopper le retour :<><Component {...pageProps} /><Chatbot /></>. Lire_appavant, conserver les fournisseurs existants.
- App Router : éditer
-
Intégration de la capture de leads automatique (uniquement si KV a été détecté à l'étape 2) :
- Copier
templates/chat-lead.tsdanslib/chat-lead.ts(ousrc/lib/chat-lead.ts). - Si le projet N'A PAS encore
lib/kv.ts→ créer :import { Redis } from "@upstash/redis"; export const kv = Redis.fromEnv(); - Le gestionnaire de route importe :
import { extractLeadFromMessages, saveChatLead } from "@/lib/chat-lead". - Dans la boucle de stream : suivre
assistantText(accumuler les deltas), à la fermeture du stream → appelerpersistLeadIfReady(history, assistantText)(fire-and-forget, NE PAS attendre dans le chemin de réponse). - Prompt système : s'assurer que le bloc COLLECTE ACTIVE DE LEADS est présent dans
chatbot-knowledge.ts(le template le contient déjà). - Confirmer avec l'utilisateur : "J'ai activé la capture automatique de leads — lorsque le client colle nom/téléphone/email dans le chat, le système sauvegarde automatiquement dans l'espace de noms KV
chat-lead:{SĐT}. Consultez les leads via/admin(si/biz-admin-leads-dashboardest installé)."
- Copier
Étape 5 — Configurer .env.local + .gitignore
-
Vérifier si
.env.localexiste déjà :- Non : créer avec :
# Clé API OpenRouter — Obtenez-la sur https://openrouter.ai/keys OPENROUTER_API_KEY= # Facultatif : URL du site pour le tracking du referrer OpenRouter (améliore le rate limiting) NEXT_PUBLIC_SITE_URL=http://localhost:3000 - Oui : ajouter les 2 variables ci-dessus si elles n'existent pas, ne pas écraser les variables existantes.
- Non : créer avec :
-
Mettre à jour
.env.example(créer s'il n'existe pas) :OPENROUTER_API_KEY=sk-or-v1-xxxxx NEXT_PUBLIC_SITE_URL=https://your-domain.com -
Vérifier si
.gitignorecontient déjà.env.local? Si non → ajouter.env.localet.env*.local.
Étape 6 — Rapport à l'utilisateur
Afficher 1 message récapitulatif clair avec 4 parties :
✅ Chatbot installé avec succès dans [nom du projet]
📁 Fichiers créés :
- components/Chatbot.tsx (affiche le Markdown via react-markdown + remark-gfm)
- app/api/chat/route.ts
- lib/chatbot-knowledge.ts
- app/globals.css (styles Markdown .chatbot-md ajoutés)
- .env.local (placeholder)
📦 Paquets installés : react-markdown, remark-gfm
🔑 3 étapes à effectuer vous-même pour faire fonctionner le chatbot :
1. Obtenir une clé API OpenRouter :
- Allez sur https://openrouter.ai → Inscrivez-vous (possible avec Google)
- Allez sur https://openrouter.ai/keys → Créez une clé
- Ajoutez du crédit (https://openrouter.ai/credits) — Gemini 3 Flash est peu coûteux, 5$ suffisent pour des milliers de conversations
- Copiez la clé au format `sk-or-v1-xxxxx`
2. Collez-la dans le fichier `.env.local` :
OPENROUTER_API_KEY=sk-or-v1-xxxxx-collez-ici
3. Testez :
[npm/pnpm/yarn] run dev
Ouvrez http://localhost:3000 → cliquez sur la bulle en bas à droite
🔄 Changer de modèle entre Gemini ↔ Sonnet :
Ouvrez `app/api/chat/route.ts`, modifiez la variable `model:` en début de fichier.
- `google/gemini-3-flash-preview` (par défaut, 0,075$/1M entrée, rapide)
- `anthropic/claude-sonnet-4.6` (haut de gamme, ~3$/1M entrée, meilleure qualité)
📚 Mettre à jour la base de connaissances ultérieurement :
Éditez `lib/chatbot-knowledge.ts` — ajoutez/modifiez FAQ et productInfo, sauvegardez, rechargement à chaud.
Design responsive mobile-first
Le widget doit respecter 3 points de rupture :
| Point de rupture | Comportement |
|---|---|
| ≤ 640px (mobile) | Cliquer sur la bulle → ouvre une superposition plein écran (z-index 9999), l'en-tête a un bouton X pour fermer. L'input est ancré en bas avec safe-area-inset pour ne pas être masqué par le clavier. |
| 641–1023px (tablette) | Panneau 380px × 70vh, ancré en bas à droite avec une marge de 16px. |
| ≥ 1024px (desktop) | Panneau 400px × 600px, ancré en bas à droite avec une marge de 24px. Bulle ronde de 60px. |
**
…
[suite tronquée pour la traduction]