CognxSafeTrack commited on
Commit
f1a06cd
·
1 Parent(s): c286b2d

docs: add audit_production.md + plan_implementation_prod.md; fix admin phone field

Browse files
apps/admin/src/App.tsx CHANGED
@@ -161,7 +161,7 @@ function Dashboard() {
161
  const headers = ['ID', 'User Phone', 'Track Title', 'Status', 'Current Day', 'Started At'];
162
  const rows = data.enrollments.map(env => [
163
  env.id,
164
- env.user?.whatsappId || 'Unknown',
165
  env.track?.title || 'Unknown',
166
  env.status,
167
  env.currentDay,
@@ -234,7 +234,7 @@ function Dashboard() {
234
  <tbody>
235
  {data.enrollments.map((env: any) => (
236
  <tr key={env.id} className="border-b border-slate-50 hover:bg-slate-50/50">
237
- <td className="px-6 py-4 font-medium text-slate-900">{env.user?.whatsappId || 'Unknown'}</td>
238
  <td className="px-6 py-4">{env.track?.title || 'Unknown Track'}</td>
239
  <td className="px-6 py-4">
240
  <span className={`px-2.5 py-1 py-0.5 rounded-full text-xs font-medium
 
161
  const headers = ['ID', 'User Phone', 'Track Title', 'Status', 'Current Day', 'Started At'];
162
  const rows = data.enrollments.map(env => [
163
  env.id,
164
+ env.user?.phone || 'Unknown',
165
  env.track?.title || 'Unknown',
166
  env.status,
167
  env.currentDay,
 
234
  <tbody>
235
  {data.enrollments.map((env: any) => (
236
  <tr key={env.id} className="border-b border-slate-50 hover:bg-slate-50/50">
237
+ <td className="px-6 py-4 font-medium text-slate-900">{env.user?.phone || 'Unknown'}</td>
238
  <td className="px-6 py-4">{env.track?.title || 'Unknown Track'}</td>
239
  <td className="px-6 py-4">
240
  <span className={`px-2.5 py-1 py-0.5 rounded-full text-xs font-medium
docs/Xamle_System_Spec_Deployment_Sheet.md ADDED
@@ -0,0 +1,237 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # XAMLÉ – System Spec & Deployment Sheet (HF + Railway Worker)
2
+
3
+ Ce document centralise toutes les informations techniques, de configuration et d'architecture de l'infrastructure XAMLÉ (SafeTrack EdTech). Il sert de référence absolue (Single Source of Truth) pour déployer et maintenir le backend scindé entre Hugging Face (API) et Railway (Worker).
4
+
5
+ ---
6
+
7
+ ## 1. Résumé Architecture (Actuelle / Cible)
8
+
9
+ Le système est construit sur un monorepo Turborepo (pnpm) composé de multiples applications qui se parlent via une base de données et un cache central.
10
+
11
+ * **Hugging Face Space (Public Inbound Endpoint) :**
12
+ * Héberge l'application `apps/api`.
13
+ * Rôle : Point d'entrée public pour le Webhook Meta (WhatsApp Inbound), les requêtes Stripe, et le back-office Admin.
14
+ * C'est ici que l'IA (OpenAI) génère le contenu et que l'utilisateur est enregistré en base. Aucune requête sortante vers Meta n'est émise d'ici à cause des restrictions DNS (ENOTFOUND).
15
+ * **Railway (Outbound Background Worker) :**
16
+ * Héberge **uniquement** l'application `apps/whatsapp-worker`.
17
+ * Rôle : Consommer les jobs BullMQ enregistrés dans Redis par Hugging Face, et envoyer physiquement les messages sortants vers l'API WhatsApp Cloud (graph.facebook.com). Contient le Cron (Scheduler) quotidien.
18
+ * **Neon Postgres :**
19
+ * Rôle : Base de données relationnelle centrale (Prisma) partagée entre HF et Railway (Tables `User`, `Track`, `Enrollment`, `TrackDay`).
20
+ * **Upstash Redis :**
21
+ * Rôle : Gestion de la file d'attente asynchrone BullMQ (queue `whatsappQueue`). Il agit comme le pont de communication en temps réel entre Hugging Face et Railway.
22
+ * **Cloudflare R2 :**
23
+ * Rôle : Stockage S3 public pour héberger les fichiers générés par l'IA (PDF, Audio, PPTX) afin que Meta puisse les télécharger via des URLs publiques et les envoyer aux utilisateurs WhatsApp.
24
+ * **OpenAI :**
25
+ * Rôle : LLM utilisé dans `apps/api` (sur Hugging Face) pour personnaliser les leçons et générer l'audio (Whisper STT / TTS).
26
+
27
+ ---
28
+
29
+ ## 2. URLs & Endpoints
30
+
31
+ **URL de base Hugging Face :** `https://safetrack-edtech.hf.space`
32
+
33
+ ### Webhook WhatsApp (Configuration Meta App Dashboard)
34
+ * **GET Verify (Public) :** `GET https://safetrack-edtech.hf.space/v1/whatsapp/webhook`
35
+ * Utilisé par Meta une seule fois lors de la configuration avec `<WHATSAPP_VERIFY_TOKEN>`.
36
+ * **POST Webhook (Public mais Sécurisé HMAC) :** `POST https://safetrack-edtech.hf.space/v1/whatsapp/webhook`
37
+ * L'endpoint où Meta poste les messages des utilisateurs.
38
+ * Sécurisé via validation de signature `X-Hub-Signature-256` en utilisant `<WHATSAPP_APP_SECRET>`.
39
+
40
+ ### Utilitaires & Healthchecks (Publics)
41
+ * `GET /` : Healthcheck basique (`{"ok": true}`).
42
+ * `GET /health` : Monitoring avec timestamp.
43
+ * `GET /privacy` : Politique de confidentialité servie en HTML (requise par Meta App Review).
44
+ * `GET /debug/net` : Teste la connectivité Google (diagnostique Egress).
45
+ * `GET /debug/graph` : Teste explicitement si la résolution DNS `graph.facebook.com` fonctionne (Diagnostic HF DNS Block).
46
+
47
+ ### Routes Privées (Protégées par `X-API-Key`)
48
+ Les routes suivantes exigent le header `Authorization: Bearer <ADMIN_API_KEY>` :
49
+ * `/v1/admin/*` : Endpoints pour le frontend Admin (Stats, Enrollments).
50
+ * `/v1/ai/*` : Génération à la demande de PDF/Audio.
51
+ * `/v1/payments/*` : Création de sessions Stripe.
52
+
53
+ ---
54
+
55
+ ## 3. Variables & Secrets (Environment)
56
+
57
+ | Name | Env | Type | Example / Placeholder | Used by | Notes |
58
+ | :--- | :--- | :--- | :--- | :--- | :--- |
59
+ | `WHATSAPP_VERIFY_TOKEN` | HF | Secret | `my_secure_verify_token_123` | `apps/api` | Vérifie le webhook initial de Meta. |
60
+ | `WHATSAPP_APP_SECRET` | HF | Secret | `c2a5...` (32 chars) | `apps/api` | Clé secrète de l'App Meta pour signer (`crypto.createHmac`) les payloads. |
61
+ | `WHATSAPP_ACCESS_TOKEN` | **Railway** & HF | Secret | `EAA...` | `whatsapp-worker` & `api` | Le jeton d'accès permanent System User. Worker (envoi message) / API (téléchargement audio STT). |
62
+ | `WHATSAPP_PHONE_NUMBER_ID` | **Railway** | Public | `5249583...` | `whatsapp-worker` | L'ID Meta du numéro expéditeur. |
63
+ | `DATABASE_URL` | Both | Secret | `postgresql://user:pass@ep-neon.tech/db?sslmode=require` | `api` & `worker` | DSN Neon (Pooler ou direct). |
64
+ | `REDIS_URL` | Both | Secret | `rediss://default:pass@upstash.io:6379` | `api` & `worker` | **Important:** Si Upstash TLS, utiliser `rediss://` (avec 2 's') et non `redis://`. |
65
+ | `ADMIN_API_KEY` | HF | Secret | `sk_admin_12345` | `apps/api` | Sécurise le dashboard et les routes privées. |
66
+ | `OPENAI_API_KEY` | HF | Secret | `sk-proj...` | `apps/api` | Provider IA. |
67
+ | `R2_ACCOUNT_ID` | HF | Public | `a1b2c3d4...` | `apps/api` | Cloudflare Storage. |
68
+ | `R2_BUCKET` | HF | Public | `xamle-edtech-assets` | `apps/api` | Nom du seau Cloudflare. |
69
+ | `R2_PUBLIC_URL` | Both | Public | `https://assets.xamle.com` | `api` & `worker` | URL de diffusion pour que l'App Meta puisse télécharger l'asset. |
70
+ | `R2_ACCESS_KEY_ID` | HF | Secret | `...` | `apps/api` | AWS S3 Compat layer credential. |
71
+ | `R2_SECRET_ACCESS_KEY` | HF | Secret | `...` | `apps/api` | AWS S3 Compat layer credential. |
72
+ | `STRIPE_SECRET_KEY` | HF | Secret | `sk_test_...` | `apps/api` | Paiements et abonnements Premium. |
73
+ | `STRIPE_WEBHOOK_SECRET` | HF | Secret | `whsec_...` | `apps/api` | Signature Webhook Stripe. |
74
+
75
+ ---
76
+
77
+ ## 4. BullMQ / Worker Logic
78
+
79
+ La file d'attente garantit la résilience.
80
+ * **Nom de la Queue :** `whatsappQueue`
81
+ * **Producer (Hugging Face) :**
82
+ Quand un webhook Meta arrive, l'API appelle `scheduleMessage(userId, text)` dans `apps/api/src/services/queue.ts`.
83
+ * **Consumer (Railway) :**
84
+ Le Worker écoute la queue dans `apps/whatsapp-worker/src/index.ts`. Le handler de la file est instancié avec `new Worker('whatsappQueue', async (job) => { ... })`.
85
+ * **Job Types (Payload JSON) :**
86
+ * Type `send-message` : `{ "userId": "uuid-1234", "text": "Bienvenue !" }`
87
+ * Type `send-content` : `{ "userId": "uuid-123", "trackDayId": "day-123" }` (déclenche génération IA et envois médias).
88
+ * **Logs Attendus sur Railway :**
89
+ `Worker: Processing job: send-message 123`
90
+ `[WhatsApp] ✅ Text message sent to 22177...`
91
+
92
+ ---
93
+
94
+ ## 5. WhatsApp Cloud API
95
+
96
+ Toutes les requêtes sortantes vers Meta sont centralisées dans `apps/whatsapp-worker/src/whatsapp-cloud.ts`.
97
+
98
+ * **URL Exacte :** `https://graph.facebook.com/v18.0/<WHATSAPP_PHONE_NUMBER_ID>/messages` (Forcée via Axios avec un agent HTTP configuré explicitement sur l'IPv4 `family: 4` pour anticiper tout problème de configuration DNS Docker).
99
+ * **Headers :**
100
+ ```json
101
+ {
102
+ "Content-Type": "application/json",
103
+ "Authorization": "Bearer <WHATSAPP_ACCESS_TOKEN>"
104
+ }
105
+ ```
106
+ * **Body Type "Text" :**
107
+ ```json
108
+ {
109
+ "messaging_product": "whatsapp",
110
+ "recipient_type": "individual",
111
+ "to": "221781476249",
112
+ "type": "text",
113
+ "text": { "preview_url": false, "body": "Hello World" }
114
+ }
115
+ ```
116
+
117
+ ---
118
+
119
+ ## 6. Sécurité
120
+
121
+ 1. **Validations Webhook HMAC**
122
+ La vérification cryptographique est active dans `apps/api/src/routes/whatsapp.ts`.
123
+ ```typescript
124
+ const expected = 'sha256=' + crypto.createHmac('sha256', secret).update(rawBody).digest('hex');
125
+ crypto.timingSafeEqual(Buffer.from(expected), Buffer.from(signature));
126
+ ```
127
+ 2. **Rate Limiting Global (Hugging Face / Fastify)**
128
+ Configuré dans `index.ts` avec `@fastify/rate-limit`. Le seuil actuel est de `300 requêtes / minute` par adresse IP (qui protégera l'API si le Webhook fuite mais n'impactera pas l'IP de Meta qui est distribuée).
129
+ 3. **API Key (Admin routes)**
130
+ Vérification stricte de `Bearer <ADMIN_API_KEY>` sur chaque route admin/backoffice.
131
+ 4. **CORS**
132
+ CORS est ouvert globalement via `@fastify/cors` pour permettre au dashboard frontal d'intéragir depuis n'importe où.
133
+
134
+ ---
135
+
136
+ ## 7. Repo & Build
137
+
138
+ Le projet est un monorepo géré avec **pnpm** et ses workspaces.
139
+
140
+ * `apps/api/` : Application Fastify (Node).
141
+ * `apps/whatsapp-worker/` : Processus BullMQ natif (Node).
142
+ * `packages/database/` : Couche Prisma partagée avec le schéma `schema.prisma`.
143
+
144
+ **Commandes critiques (Railway Build & Start) :**
145
+ * Root command (Installation) : `pnpm install`
146
+ * Génération client : `pnpm --filter @repo/database build` (ou `prisma generate`).
147
+ * Build du Worker : `pnpm --filter whatsapp-worker build`
148
+ * Start du Worker (Prod) : `pnpm --filter whatsapp-worker start`
149
+
150
+ _Note : Le script start de whatsapp-worker utilise actuellement `tsx src/index.ts` en développement. Pour la prod (Railway), il est conseillé de compiler avec `tsc` et lancer `node dist/index.js`._
151
+
152
+ ---
153
+
154
+ ## 8. Plan de Déploiement "Railway Worker Only"
155
+
156
+ Pour déployer le Worker sur Railway, nous voulons ignorer totalement l'API et le Frontend. La configuration Railway cible le dossier du worker :
157
+
158
+ 1. **Connect GitHub** : Ajouter le repository Edtech à Railway.
159
+ 2. **Configuration du Service (Settings)** :
160
+ * **Root Directory :** Laisser à `/` (car c'est un monorepo pnpm, Railway comprendra les workspaces).
161
+ * **Build Command :** `pnpm install && pnpm --filter @repo/database build && pnpm --filter whatsapp-worker build`
162
+ * **Start Command :** `pnpm --filter whatsapp-worker start`
163
+ 3. **Injector les Variables (Variables tab)** :
164
+ * Coller en Block Import les variables requises signalées en "Railway" ou "Both" dans la section [3].
165
+ * Spécialement `DATABASE_URL`, `REDIS_URL`, `WHATSAPP_PHONE_NUMBER_ID`, et `WHATSAPP_ACCESS_TOKEN`.
166
+ 4. **Checklist de validation finale :**
167
+ * Envoyer `INSCRIPTION` au numéro WhatsApp.
168
+ * Vérifier les logs sur le container de Hugging Face => on y voit la réception `[WEBHOOK] Received webhook event...`.
169
+ * Ouvrir les logs de Railway => on y voit le worker prendre la relève `Processing job: send-message`.
170
+ * Le téléphone reçoit le message instantanément.
171
+
172
+ ---
173
+
174
+ ## 9. Extraits de code de référence
175
+
176
+ ### A. Producer (Hugging Face) -> `services/queue.ts`
177
+ ```typescript
178
+ import { Queue } from 'bullmq';
179
+ import Redis from 'ioredis';
180
+
181
+ const redisConnection = new Redis(process.env.REDIS_URL!);
182
+ export const whatsappQueue = new Queue('whatsappQueue', { connection: redisConnection });
183
+
184
+ export async function scheduleMessage(userId: string, text: string, delayMs: number = 0) {
185
+ await whatsappQueue.add('send-message', { userId, text }, { delay: delayMs });
186
+ }
187
+ ```
188
+
189
+ ### B. Consumer (Railway Worker) -> `whatsapp-worker/src/index.ts`
190
+ ```typescript
191
+ import { Worker, Job } from 'bullmq';
192
+ import { sendTextMessage } from './whatsapp-cloud';
193
+
194
+ const worker = new Worker('whatsappQueue', async (job: Job) => {
195
+ if (job.name === 'send-message') {
196
+ const { userId, text } = job.data;
197
+ const user = await prisma.user.findUnique({ where: { id: userId } });
198
+ if (user?.phone) {
199
+ await sendTextMessage(user.phone, text); // Appelle Graph API
200
+ }
201
+ }
202
+ }, { connection: redisConnection });
203
+ ```
204
+
205
+ ### C. Bypass du blocage DNS (whatsapp-cloud.ts)
206
+ ```typescript
207
+ import axios from 'axios';
208
+ import https from 'https';
209
+ import dns from 'node:dns';
210
+
211
+ // Fix global ipv4first
212
+ dns.setDefaultResultOrder('ipv4first');
213
+
214
+ // Agent de secours
215
+ const httpsAgent = new https.Agent({ family: 4 });
216
+
217
+ export async function sendTextMessage(to: string, text: string): Promise<void> {
218
+ const url = `https://graph.facebook.com/v18.0/${process.env.WHATSAPP_PHONE_NUMBER_ID}/messages`;
219
+ await axios.post(url, body, {
220
+ headers: { Authorization: `Bearer ${process.env.WHATSAPP_ACCESS_TOKEN}` },
221
+ httpsAgent // Force le DNS à utiliser une route supportée
222
+ });
223
+ }
224
+ ```
225
+
226
+ ### D. Endpoints de Diagnostique DNS & Egress Hugging Face (`api/src/index.ts`)
227
+ ```typescript
228
+ server.get('/debug/net', async (_req, reply) => {
229
+ const res = await fetch('https://www.google.com', { method: 'GET' });
230
+ return reply.send({ ok: true, status: res.status }); // ✅ SUCCESS HF
231
+ });
232
+
233
+ server.get('/debug/graph', async (_req, reply) => {
234
+ const res = await fetch('https://graph.facebook.com/v18.0', { method: 'GET' });
235
+ return reply.send({ ok: true, status: res.status }); // ❌ ENOTFOUND HF
236
+ });
237
+ ```
docs/architecture_pedagogique_whatsapp.md ADDED
@@ -0,0 +1,208 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Audit & Fonctionnement — Architecture Pédagogique Interactive (WhatsApp & Voix)
2
+
3
+ Ce document est destiné à l'équipe technique et aux experts pédagogiques. Il détaille la transition de Xamlé d'un bot textuel "statique" vers **un moteur pédagogique interactif** centré sur la voix (`audioUrl` / TTS), les interactions rapides (`buttonsJson`), et le retour intelligent par l'IA (`UserProgress` / OpenAI).
4
+
5
+ ---
6
+
7
+ ## 1. Modèle de Données (Prisma)
8
+
9
+ Pour soutenir des leçons interactives qui ne se valident que sous l'action de l'étudiant, deux changements majeurs ont été effectués dans `packages/database/prisma/schema.prisma`.
10
+
11
+ ### A. Le Contenu de la Leçon (`TrackDay`)
12
+ L'ancien format texte brut a été enrichi pour supporter les audios et les interactions (boutons, vocaux).
13
+
14
+ ```prisma
15
+ model TrackDay {
16
+ id String @id @default(uuid())
17
+ trackId String
18
+ dayNumber Int
19
+ title String?
20
+ audioUrl String? // URL de l'audio pré-enregistré (sur R2) ou généré par TTS
21
+ lessonText String? // Texte de remplacement de l'audio
22
+ exerciseType ExerciseType @default(TEXT) // Type (TEXT, AUDIO, BUTTON)
23
+ exercisePrompt String? // Question de validation à envoyer en texte ou en bouton
24
+ validationKeyword String? // (Option) Si la validation nécessite un mot précis
25
+ buttonsJson Json? // Structure du bouton if ExerciseType == BUTTON
26
+ unlockCondition String? // Logique pour débloquer le jour suivant
27
+ createdAt DateTime @default(now())
28
+ updatedAt DateTime @updatedAt
29
+
30
+ track Track @relation(fields: [trackId], references: [id])
31
+ }
32
+
33
+ enum ExerciseType {
34
+ TEXT
35
+ AUDIO
36
+ BUTTON
37
+ }
38
+ ```
39
+
40
+ ### B. Le Suivi de Mémorisation et Statut (`UserProgress`)
41
+ Au lieu de passer l'enrollement au "jour suivant" immédiatement, nous utilisons désormais un modèle précis pour l'exercice en cours.
42
+
43
+ ```prisma
44
+ model UserProgress {
45
+ id String @id @default(uuid())
46
+ userId String
47
+ trackId String
48
+ currentDay Int @default(1)
49
+ score Int @default(0) // Permet la gamification
50
+ lastInteraction DateTime @default(now())
51
+ exerciseStatus ExerciseStatus @default(PENDING) // PENDING -> l'étudiant doit répondre
52
+ createdAt DateTime @default(now())
53
+ updatedAt DateTime @updatedAt
54
+
55
+ user User @relation(fields: [userId], references: [id])
56
+ track Track @relation(fields: [trackId], references: [id])
57
+
58
+ @@unique([userId, trackId])
59
+ }
60
+
61
+ enum ExerciseStatus {
62
+ PENDING
63
+ COMPLETED
64
+ }
65
+ ```
66
+
67
+ ---
68
+
69
+ ## 2. Le Moteur d'Envoi côté Worker (`pedagogy.ts`)
70
+
71
+ La logique d'envoi du contenu (déclenchée par l'inscription ou la validation d'un exercice) a été isolée dans `apps/whatsapp-worker/src/pedagogy.ts`.
72
+
73
+ La fonction principale, `sendLessonDay(...)`, structure l'expérience en trois temps :
74
+ 1. Envoyer la leçon audio ou texte.
75
+ 2. Envoyer la question interactive.
76
+ 3. Bloquer l'utilisateur au statut `PENDING`.
77
+
78
+ ```typescript
79
+ // Extrait de pedagogy.ts dans le worker Railway
80
+ export async function sendLessonDay(userId: string, trackId: string, dayNumber: number) {
81
+ // 1. Envoi Audio (Via R2 ou TTS par défaut)
82
+ if (finalAudioUrl) {
83
+ await sendAudioMessage(user.phone, finalAudioUrl);
84
+ } else if (lessonText) {
85
+ await sendTextMessage(user.phone, lessonText);
86
+ }
87
+
88
+ // 2. Envoi de l'exercice interactif
89
+ if (trackDay.exercisePrompt) {
90
+ if (trackDay.exerciseType === 'BUTTON' && trackDay.buttonsJson) {
91
+ // Appelle l'API Meta avec le format Interactive Button
92
+ const buttons = trackDay.buttonsJson as Array<{ id: string; title: string }>;
93
+ await sendInteractiveButtonMessage(user.phone, trackDay.exercisePrompt, buttons);
94
+ } else {
95
+ await sendTextMessage(user.phone, trackDay.exercisePrompt);
96
+ }
97
+ }
98
+
99
+ // 3. Mise en attente de la réponse de l'utilisateur
100
+ await prisma.userProgress.upsert({
101
+ where: { userId_trackId: { userId, trackId } },
102
+ update: { currentDay: dayNumber, exerciseStatus: 'PENDING', lastInteraction: new Date() },
103
+ create: { userId, trackId, currentDay: dayNumber, exerciseStatus: 'PENDING' }
104
+ });
105
+ }
106
+ ```
107
+
108
+ ---
109
+
110
+ ## 3. Le Routage Webhook (`WhatsAppMessageSchema` et Interception)
111
+
112
+ L'API (Hugging Face) écoute les réponses. Si une réponse est reçue avec le statut `PENDING`, elle l'envoie au coach IA.
113
+ De plus, le typage Zod de l'événement entrant a été corrigé pour intercepter les boutons WhatsApp :
114
+
115
+ ```typescript
116
+ // Extrait de apps/api/src/routes/whatsapp.ts
117
+ const WhatsAppMessageSchema = z.object({
118
+ from: z.string(),
119
+ type: z.enum(['text', 'audio', 'image', 'video', 'document', 'sticker', 'reaction', 'interactive']),
120
+ text: z.object({ body: z.string() }).optional(),
121
+ audio: z.object({ id: z.string(), mime_type: z.string().optional() }).optional(),
122
+ interactive: z.object({
123
+ type: z.enum(['button_reply', 'list_reply']),
124
+ button_reply: z.object({ id: z.string(), title: z.string() }).optional()
125
+ }).optional()
126
+ });
127
+
128
+ // Dans le parsing du payload, on convertit un clic bouton en "Text", ou un Audio via STT Whisper :
129
+ if (message.type === 'interactive' && message.interactive) {
130
+ if (message.interactive.type === 'button_reply' && message.interactive.button_reply) {
131
+ text = message.interactive.button_reply.id; // L'ID du bouton devient la réponse envoyée à l'IA
132
+ }
133
+ } else if (message.type === 'audio' && message.audio) {
134
+ // On télécharge l'audio de Meta et on utilise Whisper pour le transcrire
135
+ text = await aiService.transcribeAudio(buffer, 'message.ogg');
136
+ }
137
+ ```
138
+
139
+ ---
140
+
141
+ ## 4. La Boucle de Correction IA (`whatsapp.ts` et `ai.ts`)
142
+
143
+ Lorsqu'une réponse (texte ou bouton) est interceptée par l'API alors qu'un exercice est "PENDING", elle sollicite `generateFeedback`.
144
+
145
+ ```typescript
146
+ // Logique d'interception (app/api/src/services/whatsapp.ts)
147
+ const pendingProgress = await prisma.userProgress.findFirst({
148
+ where: { userId: user.id, exerciseStatus: 'PENDING' },
149
+ include: { track: true }
150
+ });
151
+
152
+ if (pendingProgress) {
153
+ await scheduleMessage(user.id, "⏳ Analyse de votre réponse...");
154
+
155
+ // Génération du feedback IA
156
+ const feedback = await aiService.generateFeedback(
157
+ text, // Ce que l'utilisateur a répondu ou cliqué
158
+ trackDay.exercisePrompt || '', // L'exercice attendu
159
+ trackDay.lessonText || '' // Le contexte de la leçon
160
+ );
161
+
162
+ await scheduleMessage(user.id, feedback);
163
+
164
+ // ✅ On débloque l'utilisateur
165
+ await prisma.userProgress.update({
166
+ where: { id: pendingProgress.id },
167
+ data: { exerciseStatus: 'COMPLETED', score: { increment: 1 } }
168
+ });
169
+ }
170
+ ```
171
+
172
+ ### Le Prompt Pédagogique (OpenAI GPT-4o-mini)
173
+ L'intelligence est formatée pour un coach bienveillant sénégalais du secteur informel (WhatsApp Native).
174
+
175
+ ```typescript
176
+ // apps/api/src/services/ai/index.ts
177
+ const prompt = `
178
+ Tu es un coach business bienveillant pour des entrepreneurs du secteur informel au Sénégal.
179
+ L'étudiant vient d'écouter cette leçon : "${lessonContent}"
180
+ L'exercice attendu était : "${expectedExercise}"
181
+ Voici la réponse ou action de l'étudiant : "${userInput}"
182
+
183
+ Ta mission :
184
+ Analyse sa réponse logiquement. Valide-la avec beaucoup d'enthousiasme, et ajoute UN conseil très pratique, précis, ou une mini correction bienveillante.
185
+ Contrainte stricte : MAXIMUM 5 LIGNES.
186
+ Format WhatsApp : Utilise un langage très simple, direct, des mots courants, une structure aérée avec du *gras* pour les mots clés importants, et des emojis adaptés.
187
+ `;
188
+ ```
189
+
190
+ ---
191
+
192
+ ## 5. Fonctionnalités Utiles Ajoutées pour le Déboguage
193
+
194
+ - **Le mot-clé `INSCRIPTION` (WhatsApp)** : Pour ne pas bloquer les utilisateurs en cours de test, le mot-clé supprime les données "ville" et "activité", et "enrollments" pour **REMETTRE L'ÉTUDIANT À ZÉRO** à la volée.
195
+ - **Le mot-clé `SUITE` (WhatsApp)** : Par défaut, la prochaine leçon (lorsque "COMPLETED" est atteint) n'arrive que le lendemain matin via le Scheduler de BullMQ. Si l'utilisateur envoie `SUITE`, nous forçons le passage immédiat au jour suivant sans attendre 24 heures.
196
+
197
+ ---
198
+
199
+ ## 6. Feedbacks Requis par l'Expert et Axes d'Amélioration (V2)
200
+
201
+ L'expert observant la codebase doit envisager ces améliorations :
202
+
203
+ 1. **Fallback si Feedback "Négatif"** : Actuellement, le système génère un feedback bienveillant et passe `UserProgress` à `COMPLETED` même si l'étudiant dit une erreur ou envoie un contenu inapproprié. Il faudrait un "score de validation IA" (`true/false`) dans le schéma Zod de l'IA. Si `false`, on laisse le statut à `PENDING`.
204
+ 2. **Audio Natifs R2** : Lors de la création d'un "TrackDay", le tableau de bord Admin (pas encore construit sur ce pan) doit pouvoir uploader de vrais MP3 professionnels vers R2, et enregistrer l'URL dans `TrackDay.audioUrl`. Sans quoi le système "fallback" sur la génération automatique (TTS) assez robotique.
205
+ 3. **Limites des Boutons WhatsApp** :
206
+ - Meta impose un Maximum de 3 boutons interactifs par message. S'il y a 4 questions, il faudra switcher vers l'événement `List_reply` (Menu interactif WhatsApp).
207
+ - Les titres des boutons sont limités à 20 caractères.
208
+ 4. **Vidéos Verticales** : Le schéma BDD ne gère pas encore de champ `videoUrl`. Quand le format Vlog entrera en lice, il suffira de rajouter ce champ dans `TrackDay` et la fonction `sendVideoMessage` au Wrapper API Cloud Meta.
docs/audit_production.md ADDED
@@ -0,0 +1,123 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Audit Complet — État de la Codebase
2
+ *Date : 2026-02-22 — Commit base : `c286b2d`*
3
+
4
+ ---
5
+
6
+ ## 1. Architecture globale
7
+
8
+ ```
9
+ apps/
10
+ api/ → Fastify REST API (Railway — port 3001)
11
+ whatsapp-worker/ → BullMQ Worker (Railway — même container)
12
+ admin/ → Dashboard React (Vite + Tailwind)
13
+ web/ → Landing page + portail étudiant (Vite + Tailwind)
14
+
15
+ packages/
16
+ database/ → Prisma schema (PostgreSQL) + migrations
17
+ shared-types/ → Types TypeScript partagés
18
+ tsconfig/ → Config TS commune
19
+ ui/ → (vide)
20
+ ```
21
+
22
+ ---
23
+
24
+ ## 2. Ce qui fonctionne ✅
25
+
26
+ | Composant | État |
27
+ |---|---|
28
+ | Webhook WhatsApp (GET verify + POST events) | ✅ Implémenté |
29
+ | Onboarding : INSCRIPTION → langue → secteur | ✅ |
30
+ | Menus interactifs LIST (boutons natifs) | ✅ Titre ≤ 24 chars corrigé |
31
+ | Download audio Meta Graph API (Railway) | ✅ |
32
+ | Transcription Whisper via `/v1/ai/transcribe` | ✅ |
33
+ | Feedback AI `generateFeedback()` + 429 fallback | ✅ |
34
+ | Timeout IA 10s (Promise.race) | ✅ |
35
+ | TTS leçon → audio R2 | ✅ |
36
+ | Personnalisation leçon par secteur | ✅ |
37
+ | SUITE / DAY_CONTINUE → jour suivant | ✅ Regex fixé |
38
+ | REPLAY → réécoute leçon | ✅ |
39
+ | EXERCISE → invite réponse | ✅ |
40
+ | Dock génération OnePager PDF + PitchDeck PPTX | ✅ |
41
+ | Paiements Stripe (checkout + webhook) | ✅ Implémenté |
42
+ | Admin dashboard (stats + enrollments + CSV export) | ✅ |
43
+ | Dockerfile combiné API + Worker (Railway) | ✅ |
44
+ | Scheduler cron leçons quotidiennes | ✅ (basique) |
45
+ | Stockage audio R2 via `/v1/ai/store-audio` | ✅ Ajouté |
46
+
47
+ ---
48
+
49
+ ## 3. Problèmes identifiés ❌
50
+
51
+ ### 3.1 Critiques (bloquants en prod)
52
+
53
+ | # | Fichier | Problème | Solution |
54
+ |---|---|---|---|
55
+ | C1 | `Railway vars` | `WHATSAPP_APP_SECRET` non défini → HMAC skip (warning) | Ajouter la variable |
56
+ | C2 | `Railway vars` | `DISABLE_WHATSAPP_SEND=true` encore présent → aucun envoi | Supprimer la variable |
57
+ | C3 | Base de données | **Aucun contenu (Track/TrackDay) en production** → enrollement impossible | Seed ou admin content manager |
58
+ | C4 | `api/src/services/stripe.ts` | STRIPE_SECRET_KEY et STRIPE_WEBHOOK_SECRET non vérifiés au démarrage | Ajouter à Railway vars |
59
+ | C5 | Meta webhook | URL pas encore validée si container 502 | Corriger après Railway restart |
60
+
61
+ ### 3.2 Importants (fonctionnalités incomplètes)
62
+
63
+ | # | Composant | Problème |
64
+ |---|---|---|
65
+ | I1 | `apps/admin` | **Pas de gestion de contenu (Tracks/TrackDays)** — impossible de créer des leçons via l'interface |
66
+ | I2 | `apps/admin` | `env.user?.whatsappId` utilisé mais le modèle User a `phone` (pas `whatsappId`) → colonne vide dans le tableau |
67
+ | I3 | `apps/web` | **Portail étudiant `/login` est factice** — `handleLogin` fait un fake `alert` avec `setTimeout`, n'appelle aucune API |
68
+ | I4 | `apps/web` | Pas de page de succès paiement Stripe (`/payment/success`) |
69
+ | I5 | `whatsapp-worker/src/scheduler.ts` | Cron envoie les leçons mais **sans logique de vérification du jour courant** — risque d'envoyer à des utilisateurs déjà complétés |
70
+ | I6 | `apps/api/routes/admin.ts` | Pas de route pour **créer/modifier des Tracks ou TrackDays** API-side |
71
+ | I7 | `packages/ui` | Package vide — prévu pour composants partagés mais non utilisé |
72
+
73
+ ### 3.3 Mineurs (dette technique)
74
+
75
+ | # | Problème |
76
+ |---|---|
77
+ | M1 | `Enrollment` et `UserProgress` sont deux tables qui trackent le même `currentDay` et `exerciseStatus` — duplication de données |
78
+ | M2 | `admin/src/App.tsx` ligne 108 : import `lucide-react` collé avec une déclaration d'interface (mauvais formatage) |
79
+ | M3 | `Message` modèle Prisma non utilisé nulle part dans le code (payload jamais stocké) |
80
+ | M4 | `web/src` n'a pas de `VITE_API_URL` configuré par défaut → fallback `localhost:3001` en prod |
81
+ | M5 | Pas de gestion du cas où un utilisateur envoie `INSCRIPTION` alors qu'il est déjà inscrit à une formation active |
82
+
83
+ ---
84
+
85
+ ## 4. Variables d'environnement — état complet
86
+
87
+ ### Railway (whatsapp-worker service)
88
+
89
+ | Variable | Statut | Action |
90
+ |---|---|---|
91
+ | `WHATSAPP_VERIFY_TOKEN` | ✅ | OK |
92
+ | `WHATSAPP_ACCESS_TOKEN` | ✅ | OK |
93
+ | `WHATSAPP_PHONE_NUMBER_ID` | ✅ | OK |
94
+ | `WHATSAPP_APP_SECRET` | ⚠️ Non défini | **Ajouter** (Meta App → Basic Settings → App Secret) |
95
+ | `ADMIN_API_KEY` | ✅ (confirmé) | OK |
96
+ | `DATABASE_URL` | ✅ | OK |
97
+ | `REDIS_URL` | ✅ | OK |
98
+ | `OPENAI_API_KEY` | ✅ | OK |
99
+ | `API_URL` | ✅ | OK (doit pointer vers URL Railway publique) |
100
+ | `DISABLE_WHATSAPP_SEND` | ❌ Présent | **Supprimer** |
101
+ | `STRIPE_SECRET_KEY` | ❓ Non confirmé | Ajouter si Stripe actif |
102
+ | `STRIPE_WEBHOOK_SECRET` | ❓ Non confirmé | Ajouter si Stripe actif |
103
+ | `NODE_ENV` | ✅ | OK |
104
+ | `R2_*` variables | ✅ | OK |
105
+
106
+ ### HuggingFace (api service — inbound webhook désactivé)
107
+
108
+ | Variable | Statut |
109
+ |---|---|
110
+ | `DISABLE_WHATSAPP_SEND` | ✅ `true` (correct) |
111
+ | `OPENAI_API_KEY` | ✅ |
112
+ | `DATABASE_URL` | ✅ |
113
+ | Autres | Même que Railway |
114
+
115
+ ---
116
+
117
+ ## 5. Déploiements
118
+
119
+ | Service | Plateforme | URL | État |
120
+ |---|---|---|---|
121
+ | API + Worker (webhook + BullMQ) | Railway | `whatsapp-worker-production-0bc0.up.railway.app` | 🟡 En restart |
122
+ | Landing + Portail | HF / Netlify ? | À confirmer | ❓ |
123
+ | Admin Dashboard | HF / Netlify ? | À confirmer | ❓ |
docs/flow utilisateurs et formation .md ADDED
@@ -0,0 +1,641 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # XAMLE – NOUVELLE ARCHITECTURE PÉDAGOGIQUE VOIX & WHATSAPP
2
+
3
+ > Objectif : transformer Xamle en plateforme EdTech mobile-first ultra intuitive pour le secteur informel, priorisant AUDIO + BOUTONS WHATSAPP + EXERCICES TERRAIN + IA.
4
+
5
+ ---
6
+
7
+ # 1️⃣ POSITIONNEMENT PÉDAGOGIQUE STRATÉGIQUE
8
+
9
+ Xamle ne doit pas être un simple bot qui envoie des messages.
10
+
11
+ Il doit devenir :
12
+
13
+ → Un coach business vocal interactif
14
+ → Un système d’apprentissage terrain progressif
15
+ → Un moteur IA qui transforme apprentissage → documents concrets
16
+
17
+ Cible : entrepreneurs secteur informel
18
+ Contraintes :
19
+ - Faible écriture
20
+ - Connexion instable
21
+ - Temps limité
22
+ - Préférence audio
23
+
24
+ Donc priorité :
25
+
26
+ ✅ Audio court
27
+ ✅ Boutons interactifs
28
+ ✅ Exercices terrain concrets
29
+ ✅ Réponses vocales possibles
30
+ ✅ Vidéo courte mobile verticale
31
+
32
+ ---
33
+
34
+ # 2️⃣ NOUVEAU FLOW UTILISATEUR APRÈS INSCRIPTION
35
+
36
+ ## Étape 1 – Choix langue
37
+
38
+ 1. Français 🇫🇷
39
+ 2. Wolof 🇸🇳
40
+
41
+ ## Étape 2 – Choix OBJECTIF PRINCIPAL (UX simplifiée)
42
+
43
+ 🎯 Trouver des clients
44
+ 💰 Mieux gagner de l’argent
45
+ 🧠 Comprendre son business
46
+ 🚀 Lever des fonds / Pitcher
47
+
48
+ ➡ IMPORTANT : Ces choix restent visibles côté utilisateur (langage simple et concret).
49
+
50
+ ⚙️ MAIS en backend, chaque objectif correspond à de vrais modules structurés :
51
+
52
+ - "Trouver des clients" → Module Marketing + Validation terrain (Design Thinking appliqué)
53
+ - "Mieux gagner de l’argent" → Module Pricing + Business Model
54
+ - "Comprendre son business" → Module Business Model Simplifié
55
+ - "Lever des fonds / Pitcher" → Module Pitch + Pitch Deck
56
+
57
+ 👉 Conclusion :
58
+ On garde les choix orientés PROBLÈME (UX simple)
59
+ Mais on structure les contenus avec de vrais modules entrepreneuriaux (Design Thinking, Business Model, Pitch).
60
+
61
+ ---
62
+
63
+ # 3️⃣ STRUCTURE DES MODULES D’APPRENTISSAGE (VERSION MVP SANS VIDÉO)
64
+
65
+ ⚠️ Décision :
66
+ Pour la V1, on n’intègre PAS encore les vidéos.
67
+ On se concentre sur : AUDIO + BOUTONS + EXERCICE + FEEDBACK IA.
68
+ Les vidéos seront ajoutées en V2.
69
+
70
+ Chaque module contient :
71
+
72
+ 1️⃣ Leçon audio (2–3 min max)
73
+ - langage simple
74
+ - voix naturelle locale
75
+ - 1 seul objectif par leçon
76
+ - stockée sur R2
77
+
78
+ 2️⃣ Exercice terrain obligatoire
79
+ Exemples :
80
+ - Parler à 3 clients
81
+ - Calculer son prix réel
82
+ - Observer un problème
83
+ - Faire un mini pitch audio
84
+
85
+ 3️⃣ Interaction WhatsApp
86
+ - Boutons interactifs
87
+ - Validation par chiffre possible
88
+ - Message vocal accepté
89
+
90
+ 4️⃣ Feedback IA
91
+ - Encouragement
92
+ - Correction simple
93
+ - Suggestion pratique
94
+ - Génération automatique si nécessaire
95
+
96
+ ---
97
+
98
+ Architecture logique d’une journée :
99
+
100
+ Étape 1 → sendLessonDay()
101
+ Étape 2 → envoi audio
102
+ Étape 3 → envoi boutons
103
+ Étape 4 → attente réponse utilisateur
104
+ Étape 5 → sauvegarde DB
105
+ Étape 6 → feedback IA
106
+ Étape 7 → déblocage jour suivant
107
+
108
+
109
+ ---
110
+
111
+ # 4️⃣ STRUCTURE PROGRESSIVE DES MODULES (STRUCTURE HYBRIDE RECOMMANDÉE)
112
+
113
+ ⚠️ Décision stratégique :
114
+
115
+ On ne doit PAS opposer :
116
+ - Objectifs simples (Trouver clients, Gagner argent…)
117
+ - Modules structurés (Design Thinking, Business Model, Pitch)
118
+
119
+ On combine les deux.
120
+
121
+ ---
122
+
123
+ ## NIVEAU 1 – MODULES VISIBLES (Langage simple UX)
124
+
125
+ Ce que l’utilisateur voit :
126
+
127
+ 1️⃣ Comprendre son business
128
+ 2️⃣ Trouver des clients
129
+ 3️⃣ Mieux gagner de l’argent
130
+ 4️⃣ Structurer son activité
131
+ 5️⃣ Pitcher son projet
132
+
133
+ ---
134
+
135
+ ## NIVEAU 2 – STRUCTURE ACADÉMIQUE INTERNE (Backend)
136
+
137
+ Ces modules sont en réalité construits sur :
138
+
139
+ ### MODULE A – Design Thinking Terrain
140
+ - Observer problème
141
+ - Tester solution
142
+ - Ajuster
143
+
144
+ ### MODULE B – Business Model Simplifié
145
+ - Client
146
+ - Proposition de valeur
147
+ - Revenus
148
+ - Coûts
149
+ - Partenaires
150
+
151
+ ### MODULE C – Pricing & Rentabilité
152
+ - Coût réel
153
+ - Marge
154
+ - Prix stratégique
155
+
156
+ ### MODULE D – Marketing Terrain
157
+ - Où trouver clients
158
+ - Message simple
159
+ - Test terrain
160
+
161
+ ### MODULE E – Pitch Simple
162
+ - Problème
163
+ - Solution
164
+ - Client
165
+ - Différence
166
+
167
+ ### MODULE F – Pitch Deck (avancé)
168
+ - Histoire
169
+ - Marché
170
+ - Traction
171
+ - Besoin financier
172
+
173
+ ---
174
+
175
+ ## Progression pédagogique recommandée
176
+
177
+ Phase 1 – Survie & Cash
178
+ → Comprendre business
179
+ → Trouver clients
180
+ → Fixer prix
181
+
182
+ Phase 2 – Structuration
183
+ → Business Model
184
+ → Marketing terrain
185
+
186
+ Phase 3 – Croissance
187
+ → Pitch
188
+ → Pitch deck
189
+
190
+ ---
191
+
192
+ 🎯 Conclusion stratégique :
193
+
194
+ UX = langage problème (simple)
195
+ Architecture = vrais frameworks entrepreneuriaux
196
+
197
+ C’est la meilleure combinaison entre accessibilité et crédibilité.
198
+
199
+ ---
200
+
201
+ # 5️⃣ UX WHATSAPP RECOMMANDÉE
202
+
203
+ Après chaque leçon :
204
+
205
+ 1️⃣ Réécouter 🎧
206
+ 2️⃣ Voir vidéo ▶️
207
+ 3️⃣ Faire exercice 📝
208
+ 4️⃣ Continuer ➡️
209
+
210
+ Réponses possibles :
211
+ - Boutons interactifs
212
+ - Message vocal
213
+ - Chiffre (1/2/3/4)
214
+
215
+ ---
216
+
217
+ # 6️⃣ ADAPTATION BASE DE DONNÉES RECOMMANDÉE
218
+
219
+ Nouvelle structure recommandée pour track_days :
220
+
221
+ - id
222
+ - track_id
223
+ - day_number
224
+ - title
225
+ - audio_url
226
+ - video_url
227
+ - lesson_text
228
+ - exercise_type (audio / texte / photo / bouton)
229
+ - exercise_prompt
230
+ - validation_keyword
231
+ - buttons_json
232
+
233
+ Ajouter :
234
+
235
+ - user_progress (score, last_activity)
236
+ - media_storage_url
237
+ - exercise_status
238
+
239
+ ---
240
+
241
+ # 7️⃣ FAISABILITÉ TECHNIQUE AVEC INFRA ACTUELLE
242
+
243
+ Infra actuelle :
244
+
245
+ ✅ Neon Postgres
246
+ ✅ Railway Worker
247
+ ✅ Redis Upstash
248
+ ✅ R2 Cloudflare
249
+ ✅ WhatsApp Cloud API
250
+
251
+ Conclusion :
252
+
253
+ ✔ Audio sortant = possible
254
+ ✔ Boutons interactifs = possible
255
+ ✔ Messages vocaux entrants = possible
256
+ ✔ Vidéos = possible
257
+ ✔ Stockage média = possible via R2
258
+
259
+ Aucun changement d’infrastructure nécessaire.
260
+
261
+ Le blocage n’est pas technique.
262
+ Il est pédagogique et UX.
263
+
264
+ ---
265
+
266
+ # 8️⃣ CE QUE L’HUMAIN DOIT DÉFINIR
267
+
268
+ 1️⃣ Scripts audio simples (langue locale validée)
269
+ 2️⃣ Exercices terrain réels
270
+ 3️⃣ Progression pédagogique cohérente
271
+ 4️⃣ Vocabulaire accessible secteur informel
272
+
273
+ ---
274
+
275
+ # 9️⃣ PROMPT À DONNER À ANTIGRAVITY POUR INTÉGRATION RAILWAY + BACKEND
276
+
277
+ Utilise le prompt ci-dessous :
278
+
279
+ ---
280
+
281
+ Tu es architecte senior Node.js + Prisma + WhatsApp Cloud API.
282
+
283
+ Contexte :
284
+ Nous transformons le WhatsApp Worker actuel en moteur pédagogique interactif vocal (sans vidéo pour la V1).
285
+
286
+ Stack existante :
287
+ - Node.js
288
+ - Prisma + Neon Postgres
289
+ - Redis Upstash
290
+ - Railway
291
+ - R2 Cloudflare
292
+ - WhatsApp Cloud API
293
+
294
+ ⚠️ Ne pas modifier l’architecture globale.
295
+ ⚠️ Ne pas supprimer le système actuel d’onboarding.
296
+ ⚠️ Ajouter uniquement la couche pédagogique.
297
+
298
+ ---
299
+
300
+ OBJECTIFS TECHNIQUES
301
+
302
+ 1️⃣ Adapter le modèle Prisma :
303
+
304
+ Model TrackDay {
305
+ id
306
+ trackId
307
+ dayNumber
308
+ title
309
+ audioUrl
310
+ lessonText
311
+ exerciseType
312
+ exercisePrompt
313
+ validationKeyword
314
+ buttonsJson
315
+ unlockCondition
316
+ }
317
+
318
+ Créer table UserProgress :
319
+
320
+ Model UserProgress {
321
+ id
322
+ userId
323
+ trackId
324
+ currentDay
325
+ score
326
+ lastInteraction
327
+ exerciseStatus
328
+ }
329
+
330
+ ---
331
+
332
+ 2️⃣ Créer fonction principale :
333
+
334
+ async function sendLessonDay(userId: string, dayNumber: number)
335
+
336
+ Elle doit :
337
+ - Charger TrackDay
338
+ - Envoyer audio WhatsApp
339
+ - Envoyer message interactif avec boutons
340
+ - Sauvegarder état progression
341
+
342
+ ---
343
+
344
+ 3️⃣ Modifier Webhook WhatsApp
345
+
346
+ Le webhook doit :
347
+ - Détecter message texte
348
+ - Détecter message vocal
349
+ - Détecter bouton interactif
350
+ - Mapper réponse vers exercice
351
+ - Sauvegarder réponse
352
+ - Déclencher feedback IA
353
+
354
+ ---
355
+
356
+ 4️⃣ Gestion des exercices
357
+
358
+ Si exerciseType = "audio"
359
+ → Télécharger media WhatsApp
360
+ → Stocker sur R2
361
+ → Sauvegarder URL
362
+
363
+ Si exerciseType = "text"
364
+ → Sauvegarder texte
365
+
366
+ Si exerciseType = "button"
367
+ → Mapper reply.id
368
+
369
+ ---
370
+
371
+ 5️⃣ Feedback IA
372
+
373
+ Créer fonction :
374
+
375
+ async function generateFeedback(userInput, context)
376
+
377
+ Utiliser OPENAI_API_KEY existante
378
+ Retourner feedback court (max 5 lignes)
379
+
380
+ ---
381
+
382
+ 6️⃣ Scoring
383
+
384
+ - +1 point par exercice validé
385
+ - Mise à jour UserProgress
386
+ - Si dernier jour → trigger génération document (pitch / onepager)
387
+
388
+ ---
389
+
390
+ Livrables attendus :
391
+
392
+ - Schéma Prisma complet
393
+ - Migration SQL
394
+ - Exemple JSON bouton WhatsApp
395
+ - Code complet sendLessonDay()
396
+ - Code webhook mis à jour
397
+ - Plan de migration base existante
398
+
399
+ ---
400
+
401
+ IMPORTANT :
402
+
403
+ Ne pas ajouter vidéo pour la V1.
404
+ Concentrer la logique sur audio + interaction + progression.
405
+
406
+ ---
407
+
408
+ Retourner le code prêt à intégrer sur Railway.
409
+
410
+ ---
411
+
412
+
413
+ ---
414
+
415
+ # 🔟 MON AVIS STRATÉGIQUE
416
+
417
+ Ce que vous avez fait jusqu’ici est faisable.
418
+
419
+ L’infrastructure est solide.
420
+
421
+ Le vrai chantier maintenant est :
422
+
423
+ ➡ UX pédagogique
424
+ ➡ Structuration des modules
425
+ ➡ Interaction intuitive
426
+
427
+ La proposition "Voix + Boutons + Terrain" est excellente.
428
+
429
+ C’est différenciant.
430
+ C’est adapté au secteur informel.
431
+ C’est scalable.
432
+
433
+ Et surtout : c’est réaliste techniquement avec votre stack actuelle.
434
+
435
+ ---
436
+
437
+ # 🚀 PROCHAINE ÉTAPE RECOMMANDÉE
438
+
439
+ Décision : programme de 1 mois (4 semaines) avec terrain.
440
+
441
+ Pour la V1 WhatsApp, on recommande :
442
+ - 3 leçons / semaine (lun-mer-ven) → 12 leçons / mois
443
+ - Chaque leçon : audio 2–3 min + 1 exercice terrain + 1 réponse simple
444
+ - Les jours "OFF" : l’apprenant applique sur le terrain (sans surcharge)
445
+
446
+ ✅ Objectif : apprentissage réel + action terrain + progression mesurable.
447
+
448
+ ---
449
+
450
+ # 11️⃣ MODULE 1 (1 MOIS) – COMPRENDRE SON BUSINESS (SECTEUR INFORMEL)
451
+
452
+ ## 11.1 Résultat attendu à la fin du mois
453
+
454
+ À la fin du Module 1, l’apprenant doit pouvoir :
455
+ - Décrire son activité en 1 phrase (simple)
456
+ - Identifier 1 client principal + où le trouver
457
+ - Expliquer le problème client qu’il résout
458
+ - Énoncer son offre (produit/service) clairement
459
+ - Donner un prix de base cohérent (premier test)
460
+ - Construire un mini pitch vocal de 30–45 secondes
461
+
462
+ ⚠️ Ce module n’est pas théorique : chaque semaine impose des tests terrain.
463
+
464
+ ---
465
+
466
+ ## 11.2 Règles UX WhatsApp (V1)
467
+
468
+ - Réponses autorisées :
469
+ - 1/2/3/4 (chiffres)
470
+ - audio (recommandé)
471
+ - texte très court
472
+ - Longs formulaires interdits.
473
+ - Toujours proposer des choix via boutons quand possible.
474
+
475
+ ### Boutons standard (après chaque leçon)
476
+ 1️⃣ Réécouter 🎧
477
+ 2️⃣ Faire l’exercice 📝
478
+ 3️⃣ Envoyer ma réponse 🎙️
479
+ 4️⃣ Continuer ➡️
480
+
481
+ ---
482
+
483
+ ## 11.3 Structure d’une leçon (TrackDay)
484
+
485
+ Chaque TrackDay doit contenir :
486
+ - title
487
+ - audioUrl (R2)
488
+ - lessonText (résumé très court)
489
+ - exercisePrompt (terrain)
490
+ - exerciseType (audio|text|button|photo)
491
+ - buttonsJson (interactive)
492
+ - validationKeyword (ex: FAIT, OK, DONE) + acceptation audio
493
+ - unlockCondition (complétion jour)
494
+
495
+ ---
496
+
497
+ ## 11.4 Calendrier – 4 semaines / 12 leçons
498
+
499
+ > Note : l’apprenant reçoit la leçon le matin (ex: 08:00) mais peut répondre quand il veut.
500
+
501
+ ### Semaine 1 – Clarifier l’activité (ce que je fais vraiment)
502
+
503
+ **Jour 1 (Lun) – "Mon activité en 1 phrase"**
504
+ - Objectif : clarifier ce que tu vends (sans jargon)
505
+ - Audio (script FR – simple) :
506
+ - "Dis-moi simplement : tu aides QUI à faire QUOI, et comment tu gagnes de l’argent. Exemple : Je vends du jus bissap aux étudiants devant l’université."
507
+ - Exercice terrain : écrire ou dire une phrase :
508
+ - "Je vends [offre] à [client] à [lieu]" (ou audio)
509
+ - Réponse attendue : audio 10–20s (ou texte 1 ligne)
510
+ - Feedback IA : reformule en phrase plus claire
511
+
512
+ **Jour 2 (Mer) – "Ce que le client achète vraiment"**
513
+ - Objectif : différencier produit vs bénéfice
514
+ - Audio :
515
+ - "Le client n’achète pas un produit, il achète un résultat. Exemple : il n’achète pas du savon, il achète la propreté."
516
+ - Exercice terrain : demander à 2 clients :
517
+ - "Pourquoi tu achètes ça ?" → noter 2 réponses
518
+ - Réponse : 1 audio (20s) avec les 2 raisons
519
+ - Feedback IA : extrait 1 bénéfice principal
520
+
521
+ **Jour 3 (Ven) – "Mon client principal"**
522
+ - Objectif : choisir un client prioritaire (pas "tout le monde")
523
+ - Audio :
524
+ - "Si tu essaies de vendre à tout le monde, tu vends à personne. On choisit 1 client principal."
525
+ - Exercice : choisir 1 client principal parmi 3 options (boutons)
526
+ - A) Jeunes
527
+ - B) Femmes
528
+ - C) Commerçants
529
+ - D) Autre (texte court)
530
+ - Réponse : bouton + (si Autre) texte
531
+ - Feedback IA : confirme le persona simple
532
+
533
+ ### Semaine 2 – Problème client (ce que je résous)
534
+
535
+ **Jour 4 (Lun) – "Le problème n°1"**
536
+ - Objectif : identifier 1 douleur forte
537
+ - Audio : "Quel problème ton client veut éviter ?"
538
+ - Exercice terrain : parler à 3 personnes du client choisi et demander :
539
+ - "C’est quoi ton plus gros problème sur ça ?"
540
+ - Réponse : audio 30s résumant les 3 réponses
541
+ - Feedback IA : synthèse en 1 problème
542
+
543
+ **Jour 5 (Mer) – "Quand le problème arrive"**
544
+ - Objectif : comprendre contexte et moment
545
+ - Audio : "À quel moment ton client a le problème ?"
546
+ - Exercice : choisir un moment (boutons)
547
+ - Matin / Midi / Soir / Tout le temps
548
+ - Réponse : bouton + 1 phrase (option)
549
+ - Feedback IA : propose une phrase de positionnement
550
+
551
+ **Jour 6 (Ven) – "Comment ils le résolvent aujourd’hui"**
552
+ - Objectif : concurrence = solutions actuelles
553
+ - Audio : "Avant toi, le client fait comment ?"
554
+ - Exercice : écrire 2 solutions actuelles (texte court) ou audio
555
+ - Feedback IA : clarifie ton avantage
556
+
557
+ ### Semaine 3 – Offre (solution) & preuve terrain
558
+
559
+ **Jour 7 (Lun) – "Mon offre simple"**
560
+ - Objectif : décrire l’offre en mots simples
561
+ - Audio : "Dis ta solution en une phrase."
562
+ - Exercice : phrase offre + 1 exemple réel
563
+ - Réponse : audio 20s
564
+ - Feedback IA : reformule en offre claire
565
+
566
+ **Jour 8 (Mer) – "Promesse et limite"**
567
+ - Objectif : éviter promesses irréalistes
568
+ - Audio : "Promets petit, livre grand."
569
+ - Exercice : choisir 1 promesse parmi 3 (boutons)
570
+ - Rapide / Moins cher / Plus fiable
571
+ - Feedback IA : propose promesse adaptée au contexte
572
+
573
+ **Jour 9 (Ven) – "Test terrain 1"**
574
+ - Objectif : valider l’intérêt
575
+ - Audio : "Parle à 5 personnes et propose ton offre en 10 secondes."
576
+ - Exercice :
577
+ - Dire à 5 personnes : "Je fais X pour Y"
578
+ - Noter combien disent OUI/NON
579
+ - Réponse : chiffre (ex: OUI=2, NON=3)
580
+ - Feedback IA : conseil pratique (améliorer phrase)
581
+
582
+ ### Semaine 4 – Argent (premier prix) & mini pitch
583
+
584
+ **Jour 10 (Lun) – "Prix de base"**
585
+ - Objectif : fixer un premier prix cohérent
586
+ - Audio : "Ton prix doit couvrir tes coûts + marge."
587
+ - Exercice :
588
+ - écrire 2 coûts principaux + prix actuel
589
+ - Réponse : texte court (ou audio)
590
+ - Feedback IA : suggère marge minimale
591
+
592
+ **Jour 11 (Mer) – "Ton avantage"**
593
+ - Objectif : différenciation simple
594
+ - Audio : "Pourquoi toi plutôt qu’un autre ?"
595
+ - Exercice : choisir 1 avantage (boutons)
596
+ - Qualité / Rapidité / Confiance / Proximité
597
+ - Feedback IA : phrase d’avantage
598
+
599
+ **Jour 12 (Ven) – "Mini pitch audio"**
600
+ - Objectif : sortir un pitch 30–45s
601
+ - Audio : guide pitch :
602
+ 1) Je suis…
603
+ 2) J’aide…
604
+ 3) Parce que…
605
+ 4) Je vends… à …
606
+ - Exercice : envoyer pitch vocal 30–45s
607
+ - Réponse : audio obligatoire
608
+ - Feedback IA : 3 conseils max + version texte du pitch
609
+
610
+ ---
611
+
612
+ ## 11.5 Sorties IA après Module 1 (automatiques)
613
+
614
+ Après Jour 12 complété :
615
+ - Générer :
616
+ - Pitch texte 30s
617
+ - One-liner activité
618
+ - Mini fiche (1 page) "Mon activité" (option)
619
+
620
+ ⚠️ Pitch deck complet = V2 (ou Module Pitch dédié).
621
+
622
+ ---
623
+
624
+ ## 11.6 Notes d’implémentation (pour Antigravity)
625
+
626
+ - Créer Track "MODULE1_COMPRENDRE_BUSINESS" (FR + WO)
627
+ - Seeder : insérer 12 TrackDays
628
+ - sendLessonDay() :
629
+ - envoyer audio (media)
630
+ - envoyer interactive buttons
631
+ - Webhook :
632
+ - accepter chiffres + boutons + audio
633
+ - Exercice terrain :
634
+ - validationKeyword = {"FAIT","OK","DONE"} ou audio reçu
635
+ - Progression :
636
+ - currentDay++ après validation
637
+
638
+ ---
639
+
640
+ Fin du document.
641
+
docs/plan technique solution .md ADDED
@@ -0,0 +1,592 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 12. PLAN COMPLET DE LA SOLUTION (TECHNIQUE)
2
+
3
+ > Objectif : développer une plateforme EdTech **mobile-first** centrée sur WhatsApp + contenus sociaux, avec une brique IA pour générer des documents (pitch, pitch deck, one-pager, mini business plan, logo basique).
4
+
5
+ ## 12.1 Produits livrables (MVP → V1)
6
+
7
+ ### MVP (6–8 semaines)
8
+
9
+ - WhatsApp Learning Track (7–14 jours) : inscription, séquençage, exercices, suivi de complétion
10
+ - Admin dashboard : cohortes, progression, export CSV, tags, statistiques de base
11
+ - Générateur IA v0 : **Pitch (30s + 2min)** + **One-pager PDF** (1 page)
12
+ - Landing page : inscription, FAQ, contact
13
+
14
+ ### V1 (8–16 semaines)
15
+
16
+ - Générateur IA v1 : Pitch deck (8–10 slides PPTX) + mini business plan (2–4 pages)
17
+ - Bibliothèque de templates (deck / one-pager) + styles
18
+ - Scoring pédagogique (auto-évaluation) : maturité business (non-financier complexe)
19
+ - Paiement (Wave / Orange Money) pour cohortes présentiel
20
+
21
+ ### V2 (16–24 semaines)
22
+
23
+ - Personnalisation par filière (livreur, artisanat, commerce, transformation)
24
+ - Multi-langue (Wolof + Français)
25
+ - Data products (anonymisés) : tableaux de bord d’insights pour partenaires
26
+
27
+ ---
28
+
29
+ # 13. ARCHITECTURE (SIMPLE, SCALABLE)
30
+
31
+ ## 13.1 Vue d’ensemble
32
+
33
+ **Canaux**
34
+
35
+ - TikTok / Instagram : acquisition (contenus + CTA)
36
+ - WhatsApp : parcours structuré (learning track)
37
+ - Web : inscription + paiement + téléchargement documents
38
+
39
+ **Back-end**
40
+
41
+ - API principale (auth, learning, admin)
42
+ - Service WhatsApp (webhooks + envoi messages)
43
+ - Service IA (génération texte + génération PDF/PPTX)
44
+
45
+ **Données**
46
+
47
+ - Base Postgres (users, tracks, réponses, documents)
48
+ - Stockage fichiers (PDF/PPTX) : S3 compatible (ex: Cloudflare R2 / AWS S3 / OVH Object Storage)
49
+
50
+ ## 13.2 Recommandation repo Git
51
+
52
+ ✅ Démarrage conseillé : **Monorepo** (plus simple au début)
53
+
54
+ Structure type :
55
+
56
+ - `/apps/api` : API (Node/TS ou Python)
57
+ - `/apps/whatsapp-worker` : worker WhatsApp (queues + scheduling)
58
+ - `/apps/admin` : dashboard admin (Next.js)
59
+ - `/apps/web` : landing + paiement + portail apprenant
60
+ - `/packages/shared` : types, templates, prompts, contenu pédagogique
61
+
62
+ ➡️ Séparer en plusieurs repos uniquement si l’IA devient un produit autonome.
63
+
64
+ ---
65
+
66
+ # 14. STACK TECH RECOMMANDÉ
67
+
68
+ ## 14.1 Front-end
69
+
70
+ - **Next.js** (React) : admin + web portal
71
+ - UI : TailwindCSS + component library (shadcn/ui)
72
+
73
+ ## 14.2 Back-end
74
+
75
+ Option A (très simple, robuste) : **Node.js + TypeScript**
76
+
77
+ - Framework : Fastify ou NestJS
78
+ - Validation : Zod
79
+
80
+ Option B (si équipe plus Python) : **Python + FastAPI**
81
+
82
+ ## 14.3 Base de données
83
+
84
+ - **PostgreSQL** (Neon / Supabase / OVH / RDS)
85
+ - ORM : Prisma (Node) ou SQLModel (Python)
86
+
87
+ ## 14.4 Messaging / Jobs
88
+
89
+ - Queue : **BullMQ (Redis)** ou **Cloud Tasks**
90
+ - Objectif : envoi WhatsApp programmé, génération documents asynchrone
91
+
92
+ ## 14.5 WhatsApp
93
+
94
+ - Recommandé : **WhatsApp Cloud API (Meta)**
95
+ - Alternative : Twilio (plus simple mais coûts + dépendances)
96
+
97
+ ## 14.6 IA / Document generation
98
+
99
+ - Génération texte : via LLM (prompts + garde-fous)
100
+ - Génération PDF : HTML → PDF (Playwright) ou ReportLab
101
+ - Génération PPTX : PptxGenJS (templates) ou python-pptx
102
+ - Logos : au MVP, générer **logo basique** (icône + typographie) ou proposer 3 styles (pas de promesse “brand pro”)
103
+
104
+ ---
105
+
106
+ # 15. CHARTE GRAPHIQUE – DESIGN SYSTEM PROPOSÉ
107
+
108
+ Objectif : créer une identité visuelle moderne, crédible, inclusive et technologique, adaptée au secteur informel africain mais suffisamment institutionnelle pour parler à des bailleurs et partenaires.
109
+
110
+ ## 15.1 Positionnement visuel
111
+
112
+ Le design doit transmettre :
113
+
114
+ - Inclusion
115
+ - Modernité
116
+ - Technologie accessible
117
+ - Confiance financière
118
+ - Éducation structurée
119
+
120
+ Le style est :
121
+
122
+ - Mobile-first
123
+ - Clair et lisible
124
+ - Peu chargé
125
+ - Orienté impact
126
+
127
+ ---
128
+
129
+ ## 15.2 Palette Couleurs (proposition stratégique)
130
+
131
+ ### 🎯 Couleur primaire – Vert Structuration
132
+
133
+ `#1C7C54`
134
+
135
+ Signification : croissance, stabilité, transformation progressive. Utilisation : boutons principaux, titres, éléments forts, KPI.
136
+
137
+ ---
138
+
139
+ ### 🎯 Couleur secondaire – Bleu Profondeur
140
+
141
+ `#1B3A57`
142
+
143
+ Signification : crédibilité, sérieux institutionnel, tech. Utilisation : header, footer, dashboard admin, slide de couverture.
144
+
145
+ ---
146
+
147
+ ### 🎯 Accent – Orange Activation
148
+
149
+ `#F4A261`
150
+
151
+ Signification : énergie, entrepreneuriat, action. Utilisation : CTA, highlights, icônes actives, progression.
152
+
153
+ ---
154
+
155
+ ### 🎯 Gris neutres
156
+
157
+ - Gris clair fond : `#F5F7F9`
158
+ - Gris texte secondaire : `#6B7280`
159
+ - Noir texte principal : `#111827`
160
+
161
+ ---
162
+
163
+ ## 15.3 Typographie
164
+
165
+ ### Titres
166
+
167
+ **Montserrat Bold / SemiBold**
168
+
169
+ - Moderne
170
+ - Lisible
171
+ - Impact visuel fort
172
+
173
+ ### Texte courant
174
+
175
+ **Inter / Poppins Regular**
176
+
177
+ - Optimisé mobile
178
+ - Lecture fluide
179
+ - Compatible PDF & PPT
180
+
181
+ Fallback system :
182
+
183
+ - Arial / Helvetica
184
+
185
+ ---
186
+
187
+ ## 15.4 Tokens Design (à utiliser dans le code)
188
+
189
+ ```ts
190
+ export const theme = {
191
+ colors: {
192
+ primary: '#1C7C54',
193
+ secondary: '#1B3A57',
194
+ accent: '#F4A261',
195
+ background: '#F5F7F9',
196
+ textPrimary: '#111827',
197
+ textSecondary: '#6B7280'
198
+ },
199
+ radius: {
200
+ card: '12px',
201
+ button: '8px'
202
+ },
203
+ shadow: {
204
+ soft: '0 4px 12px rgba(0,0,0,0.08)'
205
+ }
206
+ }
207
+ ```
208
+
209
+ ---
210
+
211
+ ## 15.5 UI Components Standards
212
+
213
+ ### Boutons
214
+
215
+ - Primary : fond vert `#1C7C54`, texte blanc
216
+ - Secondary : fond bleu `#1B3A57`, texte blanc
217
+ - Accent : fond orange `#F4A261`, texte noir
218
+
219
+ Hover : légère augmentation luminosité + shadow soft.
220
+
221
+ ---
222
+
223
+ ### Cards
224
+
225
+ - Fond blanc
226
+ - Border radius 12px
227
+ - Ombre douce
228
+ - Padding généreux (16–24px)
229
+
230
+ ---
231
+
232
+ ### Dashboard Admin
233
+
234
+ - Sidebar bleu foncé
235
+ - KPI cards vertes
236
+ - Alerts orange
237
+ - Graphiques simples (bar / line)
238
+
239
+ ---
240
+
241
+ ## 15.6 Style Documents Générés (PDF / PPT)
242
+
243
+ One-pager :
244
+
245
+ - Header bleu profond
246
+ - Titres verts
247
+ - Accent orange pour chiffres clés
248
+ - Icônes simples outline
249
+
250
+ Pitch deck :
251
+
252
+ - Slide couverture : fond bleu + titre blanc
253
+ - Slides contenu : fond blanc + titres verts
254
+ - Graphiques en vert + orange
255
+
256
+ ---
257
+
258
+ ## 15.7 Ton visuel global
259
+
260
+ Pas trop “startup Silicon Valley”. Pas trop “ONG traditionnelle”.
261
+
262
+ → Tech africaine inclusive. → Sérieuse mais accessible.
263
+
264
+ ---
265
+
266
+ # 16. WHATSAPP LEARNING TRACK – COMMENT LE CODER
267
+
268
+ WHATSAPP LEARNING TRACK – COMMENT LE CODER
269
+
270
+ ## 16.1 Logique pédagogique
271
+
272
+ - Track = 7 ou 14 jours
273
+ - Chaque jour :
274
+ - 1 audio (1–3 min)
275
+ - 1 visuel (option)
276
+ - 1 exercice (réponse texte/voice)
277
+ - 1 validation (mot-clé “OK”, “SUIVANT”, bouton)
278
+
279
+ ## 16.2 Parcours utilisateur
280
+
281
+ 1. L’utilisateur envoie “INSCRIPTION”
282
+ 2. Le bot demande : prénom, activité, ville, langue
283
+ 3. Inscription au Track
284
+ 4. Envoi Jour 1
285
+ 5. Sauvegarde réponses + progression
286
+ 6. Relance automatique si inactif (J+1)
287
+ 7. Checkpoints (J7/J14) : mini pitch + données business
288
+
289
+ ## 16.3 Modèle de données (minimum viable)
290
+
291
+ Tables :
292
+
293
+ - `users(id, phone, name, language, city, created_at)`
294
+ - `tracks(id, name, duration_days, language)`
295
+ - `track_days(id, track_id, day_number, content_type, content_url, prompt_text)`
296
+ - `enrollments(id, user_id, track_id, status, current_day, started_at, completed_at)`
297
+ - `responses(id, enrollment_id, day_number, text, media_url, created_at)`
298
+ - `messages(id, user_id, channel, direction, payload_json, created_at)`
299
+
300
+ ## 16.4 Endpoints API (exemple)
301
+
302
+ - `POST /webhooks/whatsapp` : réception messages
303
+ - `POST /enroll` : créer inscription
304
+ - `POST /send/day` : envoyer contenu du jour N
305
+ - `GET /admin/metrics` : stats
306
+
307
+ ## 16.5 Exemple (Node/TS) – Webhook WhatsApp (simplifié)
308
+
309
+ ```ts
310
+ import Fastify from 'fastify'
311
+
312
+ const app = Fastify()
313
+
314
+ app.post('/webhooks/whatsapp', async (req, reply) => {
315
+ const payload: any = req.body
316
+ // 1) extraire phone + message
317
+ const phone = payload?.entry?.[0]?.changes?.[0]?.value?.messages?.[0]?.from
318
+ const text = payload?.entry?.[0]?.changes?.[0]?.value?.messages?.[0]?.text?.body?.trim()?.toUpperCase()
319
+
320
+ if (!phone) return reply.code(200).send({ ok: true })
321
+
322
+ // 2) router simple
323
+ if (text === 'INSCRIPTION') {
324
+ // TODO: créer user + enrollment
325
+ // TODO: répondre avec questions profil
326
+ }
327
+
328
+ // TODO: progression track
329
+ return reply.code(200).send({ ok: true })
330
+ })
331
+
332
+ app.listen({ port: 3000, host: '0.0.0.0' })
333
+ ```
334
+
335
+ ---
336
+
337
+ # 17. IA – GÉNÉRATION PITCH / ONE-PAGER / DECK
338
+
339
+ ## 17.1 Par quoi commencer (recommandation)
340
+
341
+ 1. **Pitch (30s + 2min)**
342
+ 2. **One-pager PDF (1 page)** ➡️ Ensuite seulement : Pitch deck PPTX
343
+
344
+ ## 17.2 Inputs (collectés via WhatsApp + formulaire web)
345
+
346
+ - Activité
347
+ - Client cible
348
+ - Problème
349
+ - Solution
350
+ - Prix / revenus
351
+ - Coûts principaux
352
+ - Preuves (photos, témoignages, traction)
353
+ - Besoin (montant, équipement, stock)
354
+
355
+ ## 17.3 Sorties (MVP)
356
+
357
+ - Pitch 30 secondes (wolof/fr)
358
+ - Pitch 2 minutes (wolof/fr)
359
+ - One-pager PDF : 6 blocs (problème, solution, marché, modèle, traction, besoin)
360
+
361
+ ## 17.4 Templates & garde-fous
362
+
363
+ - Templates de contenu (structure fixe)
364
+ - Pas de promesses irréalistes
365
+ - Toujours demander 1 preuve (même simple)
366
+ - Mentionner types de financement informel (microcrédit, crédit équipement, etc.)
367
+
368
+ ## 17.5 Exemple (pseudo-code) – endpoint docgen
369
+
370
+ ```ts
371
+ app.post('/ai/onepager', async (req, reply) => {
372
+ const input = req.body // validated
373
+ // 1) appeler modèle texte → sections
374
+ const sections = await generateSectionsWithLLM(input)
375
+ // 2) rendre HTML template
376
+ const html = renderOnePagerHTML(sections)
377
+ // 3) convertir en PDF
378
+ const pdfUrl = await htmlToPdfAndUpload(html)
379
+ return reply.send({ pdfUrl })
380
+ })
381
+ ```
382
+
383
+ ---
384
+
385
+ ---
386
+
387
+ ## 17.6 AI SLIDES ENGINE – VERSION “GENSPARK-LIKE”
388
+
389
+ Objectif : ne pas seulement générer du texte, mais produire un **pitch deck complet, designé, prêt à envoyer (PPTX/PDF)** avec :
390
+
391
+ - Couleurs appliquées automatiquement (charte interne)
392
+ - Icônes cohérentes
393
+ - Hiérarchie visuelle
394
+ - Mise en page intelligente
395
+
396
+ ### Architecture recommandée
397
+
398
+ Le système ne repose pas uniquement sur un prompt texte. Il repose sur 3 couches :
399
+
400
+ ### 1️⃣ LLM → Structure JSON avancée
401
+
402
+ Le modèle doit produire une structure riche, par exemple :
403
+
404
+ ```json
405
+ [
406
+ {
407
+ "type": "cover",
408
+ "title": "Nom du projet",
409
+ "subtitle": "Pitch court",
410
+ "themeVariant": "primary"
411
+ },
412
+ {
413
+ "type": "problem",
414
+ "title": "Problème",
415
+ "bullets": ["...", "..."],
416
+ "icon": "alert-circle",
417
+ "highlightStat": "80% des clients..."
418
+ }
419
+ ]
420
+ ```
421
+
422
+ Le LLM ne génère pas seulement du texte. Il génère une **intention de layout**.
423
+
424
+ ---
425
+
426
+ ### 2️⃣ Slide Renderer Engine
427
+
428
+ Un moteur interne transforme cette structure en slides réelles :
429
+
430
+ - Mapping automatique des types (`cover`, `problem`, `solution`, `market`, etc.)
431
+ - Application des couleurs selon le `themeVariant`
432
+ - Injection des icônes (Lucide / Heroicons / custom SVG)
433
+ - Placement automatique des blocs (grille 2 colonnes, full width, etc.)
434
+
435
+ Avec **PptxGenJS** :
436
+
437
+ - Création slide
438
+ - Ajout titre
439
+ - Ajout shape colorée
440
+ - Ajout icône SVG
441
+ - Ajout bullets stylisées
442
+
443
+ ---
444
+
445
+ ### 3️⃣ Design System Binding
446
+
447
+ Le moteur doit utiliser les tokens définis section 15 :
448
+
449
+ - Primary → #1C7C54
450
+ - Secondary → #1B3A57
451
+ - Accent → #F4A261
452
+
453
+ Chaque type de slide a une règle :
454
+
455
+ - Cover → fond bleu profond
456
+ - Problem → bande accent orange
457
+ - Solution → titre vert
458
+ - Financial → chiffres en accent
459
+
460
+ Ainsi, le rendu est cohérent automatiquement.
461
+
462
+ ---
463
+
464
+ ### 4️⃣ Auto-Layout Rules
465
+
466
+ Le moteur doit :
467
+
468
+ - Limiter à 5 bullets max
469
+ - Couper les phrases trop longues
470
+ - Ajuster taille police selon longueur
471
+ - Éviter slide surchargée
472
+
473
+ Ce n’est pas seulement du texte. C’est une logique produit.
474
+
475
+ ---
476
+
477
+ ### 5️⃣ Pipeline complet
478
+
479
+ 1. Collect inputs utilisateur
480
+ 2. Générer JSON structuré via LLM
481
+ 3. Valider JSON (Zod schema)
482
+ 4. Mapper vers template engine
483
+ 5. Générer PPTX
484
+ 6. Stocker fichier (S3)
485
+ 7. Retourner lien WhatsApp
486
+
487
+ ---
488
+
489
+ ### Important
490
+
491
+ Pour obtenir un résultat niveau Genspark :
492
+
493
+ - Toujours utiliser output JSON structuré (pas texte libre)
494
+ - Toujours passer par un renderer interne
495
+ - Toujours appliquer un design system cohérent
496
+
497
+ Le rendu final doit être :
498
+
499
+ "Document prêt à envoyer à un financeur"
500
+
501
+ et non
502
+
503
+ "Texte copié-collé dans PowerPoint"
504
+
505
+ ---
506
+
507
+ # 18. ADMIN DASHBOARD (IGNITE.E / ÉQUIPE)
508
+
509
+ Fonctionnalités admin MVP :
510
+
511
+ - Liste des inscrits (WhatsApp)
512
+ - Progression par track
513
+ - Taux de complétion J1/J7/J14
514
+ - Export CSV
515
+ - Gestion des contenus (track\_days)
516
+ - Gestion des cohortes présentiel
517
+ - Paiements (V1)
518
+
519
+ ---
520
+
521
+ # 19. SÉCURITÉ, DONNÉES & CONFORMITÉ
522
+
523
+ - Consentement explicite à l’inscription (WhatsApp) : utilisation pédagogique + données anonymisées
524
+ - Pseudonymisation : séparer identité (phone) des analytics
525
+ - Stockage chiffré des médias (si possible)
526
+ - Règles d’accès : admin role-based
527
+ - Journalisation (logs) + monitoring
528
+
529
+ ---
530
+
531
+ # 20. PLAN D’EXÉCUTION (PHASÉ)
532
+
533
+ ## Phase 0 – Préparation (1 semaine)
534
+
535
+ - Finaliser 2 tracks (Business Model 7j + Pitch 7j)
536
+ - Écrire scripts audios wolof + exercices
537
+ - Définir templates one-pager
538
+
539
+ ## Phase 1 – MVP WhatsApp Track (2–3 semaines)
540
+
541
+ - Webhook WhatsApp + parsing
542
+ - Enrollment + progression
543
+ - Scheduling envoi jour N
544
+ - Dashboard metrics minimal
545
+ - Pilote 50–100 utilisateurs
546
+
547
+ ## Phase 2 – IA v0 (2–3 semaines)
548
+
549
+ - Collect inputs
550
+ - Générer pitch + one-pager PDF
551
+ - Retour WhatsApp (lien)
552
+
553
+ ## Phase 3 – V1 (4–8 semaines)
554
+
555
+ - Paiement cohorte présentiel
556
+ - Pitch deck PPTX
557
+ - Auto-évaluation (scoring pédagogique)
558
+
559
+ ---
560
+
561
+ # 21. LISTE DES FONCTIONNALITÉS (CHECKLIST)
562
+
563
+ ## Apprenants
564
+
565
+ - Inscription WhatsApp
566
+ - Parcours 7/14 jours
567
+ - Exercices + feedback
568
+ - Documents générés (liens)
569
+
570
+ ## Admin
571
+
572
+ - Gestion contenus
573
+ - Stats complétion
574
+ - Exports
575
+ - Cohortes + paiements (V1)
576
+
577
+ ## IA
578
+
579
+ - Pitch 30s / 2min
580
+ - One-pager PDF
581
+ - Pitch deck PPTX (V1)
582
+ - Logo basique (V1/V2)
583
+
584
+ ---
585
+
586
+ # 22. NOTES IMPORTANTES
587
+
588
+ - Commencer simple : WhatsApp Track d’abord, puis IA.
589
+ - Le “logo IA” est optionnel au MVP.
590
+ - Mettre l’accent sur la qualité pédagogique + tracking.
591
+ - Les documents générés doivent rester réalistes et adaptés au financement informel.
592
+
docs/plan_implementation_prod.md ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Plan d'Implémentation Production
2
+ *Priorité : du plus bloquant au moins urgent*
3
+
4
+ ---
5
+
6
+ ## Phase 1 — Déploiement Railway opérationnel (immediat)
7
+
8
+ > Objectif : Railway répond, webhook Meta validé, premiers messages reçoivent une réponse.
9
+
10
+ ### Étape 1.1 — Variables Railway (manuel, 5 min)
11
+ 1. **Supprimer** `DISABLE_WHATSAPP_SEND`
12
+ 2. **Ajouter** `WHATSAPP_APP_SECRET` (Meta App → Basic Settings → App Secret)
13
+ 3. Vérifier que `API_URL` = `https://whatsapp-worker-production-0bc0.up.railway.app`
14
+
15
+ ### Étape 1.2 — Valider le webhook Meta (manuel, 2 min)
16
+ 1. Attendre fin du redéploiement Railway (logs : `Server listening on http://0.0.0.0:3001`)
17
+ 2. Tester : `curl https://whatsapp-worker-production-0bc0.up.railway.app/` → `{"ok":true}`
18
+ 3. Meta Developers → WhatsApp → Configuration → Webhook → Retenter la validation
19
+
20
+ ### Étape 1.3 — Créer le contenu pédagogique (base de données)
21
+ Le flow complet nécessite au moins **1 Track avec 3+ TrackDays**. À insérer via l'endpoint seed existant (`SEED_DATA`) ou directement en base :
22
+ - Envoyer `SEED_DATA` depuis WhatsApp (déclencheur existant dans `whatsapp.ts`)
23
+
24
+ ---
25
+
26
+ ## Phase 2 — Corriger les bugs d'interface (1-2 jours)
27
+
28
+ ### Étape 2.1 — Fix Admin App : champ `whatsappId` → `phone`
29
+ **Fichier** : `apps/admin/src/App.tsx` lignes 164, 237
30
+
31
+ ```diff
32
+ - env.user?.whatsappId || 'Unknown'
33
+ + env.user?.phone || 'Unknown'
34
+ ```
35
+
36
+ ### Étape 2.2 — Fix Web App : portail étudiant factice
37
+ **Fichier** : `apps/web/src/App.tsx` — fonction `handleLogin`
38
+
39
+ Implémenter l'authentification réelle :
40
+ - Appel API avec le numéro de téléphone
41
+ - Retour des documents générés (OnePager, PitchDeck) via liens R2
42
+
43
+ ### Étape 2.3 — Page de succès Stripe
44
+ Créer une page `/payment/success` dans `apps/web` qui :
45
+ - Confirme le paiement à l'utilisateur
46
+ - Affiche un lien vers WhatsApp pour commencer
47
+
48
+ ---
49
+
50
+ ## Phase 3 — Gestion de contenu admin (3-5 jours)
51
+
52
+ ### Étape 3.1 — API backend : routes Track/TrackDay
53
+ **Fichier** : `apps/api/src/routes/admin.ts`
54
+
55
+ Ajouter :
56
+ ```typescript
57
+ POST /v1/admin/tracks // Créer un track
58
+ PUT /v1/admin/tracks/:id // Modifier
59
+ POST /v1/admin/tracks/:id/days // Ajouter un TrackDay
60
+ PUT /v1/admin/tracks/:id/days/:dayId
61
+ DELETE /v1/admin/tracks/:id
62
+ ```
63
+
64
+ ### Étape 3.2 — Interface admin : Content Manager
65
+ **Fichier** : `apps/admin/src/App.tsx`
66
+
67
+ Ajouter une vue "Content" dans le sidebar :
68
+ - Liste des Tracks avec bouton Créer/Modifier
69
+ - Pour chaque Track : liste des jours avec éditeur de texte + upload audio
70
+
71
+ ---
72
+
73
+ ## Phase 4 — Améliorations qualité (optionnel)
74
+
75
+ ### Étape 4.1 — Unifier Enrollment / UserProgress
76
+ Les deux tables doublonnent `currentDay`. Migrer vers `UserProgress` uniquement et retirer `currentDay` de `Enrollment`.
77
+
78
+ ### Étape 4.2 — Logger les messages entrants (modèle Message)
79
+ Le modèle `Message` en base n'est jamais rempli. Ajouter un `prisma.message.create()` dans `handleIncomingMessage`.
80
+
81
+ ### Étape 4.3 — Scheduler : vérification état avant envoi
82
+ Dans `scheduler.ts`, avant d'envoyer la leçon du jour, vérifier que l'enrollment est `ACTIVE` et que le `currentDay` correspond.
83
+
84
+ ### Étape 4.4 — Variables VITE_API_URL dans les apps frontend
85
+ S'assurer que `VITE_API_URL` est configuré dans les `.env` de production de `apps/admin` et `apps/web`.
86
+
87
+ ---
88
+
89
+ ## Résumé des priorités
90
+
91
+ ```
92
+ ✅ FAIT → API + Worker Railway, todos les bugs WhatsApp fixés
93
+ 🔴 CRITIQUE → Supprimer DISABLE_WHATSAPP_SEND, ajouter WHATSAPP_APP_SECRET
94
+ 🟠 IMPORTANT → Valider webhook Meta, créer contenu (SEED_DATA)
95
+ 🟡 SOON → Fix admin phone field, portail étudiant réel
96
+ 🟢 OPTIONNEL → Stripe live, content manager admin, Message logging
97
+ ```