CognxSafeTrack commited on
Commit
b5dcbdb
·
1 Parent(s): 338c360

fix(super-admin): guard null wabaId, org.id, amount in profiles/templates/billing/AI pages

Browse files
apps/admin/src/pages/super-admin/AIInsights.tsx CHANGED
@@ -43,7 +43,7 @@ export default function AIInsights() {
43
  if (res.status === 'pending_confirmation') {
44
  setMessages(prev => [...prev, {
45
  role: 'assistant',
46
- content: res.summary,
47
  pending: { action: res.action, params: res.params, summary: res.summary },
48
  }]);
49
  } else {
 
43
  if (res.status === 'pending_confirmation') {
44
  setMessages(prev => [...prev, {
45
  role: 'assistant',
46
+ content: res.summary ?? '',
47
  pending: { action: res.action, params: res.params, summary: res.summary },
48
  }]);
49
  } else {
apps/admin/src/pages/super-admin/BillingManager.tsx CHANGED
@@ -102,7 +102,7 @@ export default function BillingManager() {
102
  </td>
103
  <td className="px-4 py-3 text-xs text-slate-400 max-w-xs truncate">{t.description || '—'}</td>
104
  <td className={`px-4 py-3 text-right font-medium text-sm ${t.amount > 0 ? 'text-emerald-400' : 'text-red-400'}`}>
105
- {t.amount > 0 ? '+' : ''}{t.amount.toLocaleString()}
106
  </td>
107
  <td className="px-4 py-3 text-right text-slate-300">{t.balanceAfter?.toLocaleString()}</td>
108
  </tr>
 
102
  </td>
103
  <td className="px-4 py-3 text-xs text-slate-400 max-w-xs truncate">{t.description || '—'}</td>
104
  <td className={`px-4 py-3 text-right font-medium text-sm ${t.amount > 0 ? 'text-emerald-400' : 'text-red-400'}`}>
105
+ {(t.amount ?? 0) > 0 ? '+' : ''}{(t.amount ?? 0).toLocaleString()}
106
  </td>
107
  <td className="px-4 py-3 text-right text-slate-300">{t.balanceAfter?.toLocaleString()}</td>
108
  </tr>
apps/admin/src/pages/super-admin/OrganizationsManager.tsx CHANGED
@@ -154,7 +154,7 @@ export default function OrganizationsManager() {
154
  <tr key={org.id} className="hover:bg-slate-800/50 transition-colors">
155
  <td className="px-4 py-3">
156
  <div className="font-medium text-white">{org.name}</div>
157
- <div className="text-xs text-slate-500 font-mono">{org.id.slice(0, 8)}…</div>
158
  </td>
159
  <td className="px-4 py-3">
160
  <span className={`text-xs px-2 py-0.5 rounded-full ${PLAN_COLORS[org.subscriptionPlan] ?? 'bg-slate-700 text-slate-300'}`}>
 
154
  <tr key={org.id} className="hover:bg-slate-800/50 transition-colors">
155
  <td className="px-4 py-3">
156
  <div className="font-medium text-white">{org.name}</div>
157
+ <div className="text-xs text-slate-500 font-mono">{(org.id ?? '').slice(0, 8)}…</div>
158
  </td>
159
  <td className="px-4 py-3">
160
  <span className={`text-xs px-2 py-0.5 rounded-full ${PLAN_COLORS[org.subscriptionPlan] ?? 'bg-slate-700 text-slate-300'}`}>
apps/admin/src/pages/super-admin/WhatsAppProfiles.tsx CHANGED
@@ -12,7 +12,7 @@ interface BrandingData {
12
  interface Profile {
13
  id: string;
14
  name: string;
15
- wabaId: string;
16
  brandingData: BrandingData | null;
17
  subscriptionPlan: string;
18
  subscriptionStatus: string;
@@ -152,7 +152,9 @@ function ProfileCard({
152
 
153
  const planClass = PLAN_BADGE[profile.subscriptionPlan] ?? 'bg-slate-700 text-slate-300';
154
  const statusClass = STATUS_BADGE[profile.subscriptionStatus] ?? 'bg-slate-700 text-slate-300';
155
- const wabaShort = profile.wabaId.length > 14 ? profile.wabaId.slice(0, 14) + '…' : profile.wabaId;
 
 
156
 
157
  return (
158
  <div className="bg-slate-900 border border-slate-800 rounded-xl p-5 space-y-4 flex flex-col">
 
12
  interface Profile {
13
  id: string;
14
  name: string;
15
+ wabaId: string | null;
16
  brandingData: BrandingData | null;
17
  subscriptionPlan: string;
18
  subscriptionStatus: string;
 
152
 
153
  const planClass = PLAN_BADGE[profile.subscriptionPlan] ?? 'bg-slate-700 text-slate-300';
154
  const statusClass = STATUS_BADGE[profile.subscriptionStatus] ?? 'bg-slate-700 text-slate-300';
155
+ const wabaShort = profile.wabaId
156
+ ? profile.wabaId.length > 14 ? profile.wabaId.slice(0, 14) + '…' : profile.wabaId
157
+ : '—';
158
 
159
  return (
160
  <div className="bg-slate-900 border border-slate-800 rounded-xl p-5 space-y-4 flex flex-col">
apps/admin/src/pages/super-admin/WhatsAppTemplates.tsx CHANGED
@@ -190,7 +190,7 @@ export default function WhatsAppTemplates() {
190
  }
191
 
192
  const filtered = orgs.filter(o =>
193
- o.name.toLowerCase().includes(search.toLowerCase()) ||
194
  o.wabaId?.toLowerCase().includes(search.toLowerCase())
195
  );
196
 
@@ -258,7 +258,7 @@ export default function WhatsAppTemplates() {
258
  <tr key={org.id} className="hover:bg-slate-800/50 transition-colors">
259
  <td className="px-4 py-3">
260
  <div className="font-medium text-white">{org.name}</div>
261
- <div className="text-xs text-slate-500 font-mono">{org.id.slice(0, 8)}…</div>
262
  </td>
263
  <td className="px-4 py-3">
264
  <span className={`text-xs px-2 py-0.5 rounded-full ${PLAN_COLORS[org.subscriptionPlan] ?? 'bg-slate-700 text-slate-300'}`}>
 
190
  }
191
 
192
  const filtered = orgs.filter(o =>
193
+ (o.name ?? '').toLowerCase().includes(search.toLowerCase()) ||
194
  o.wabaId?.toLowerCase().includes(search.toLowerCase())
195
  );
196
 
 
258
  <tr key={org.id} className="hover:bg-slate-800/50 transition-colors">
259
  <td className="px-4 py-3">
260
  <div className="font-medium text-white">{org.name}</div>
261
+ <div className="text-xs text-slate-500 font-mono">{(org.id ?? '').slice(0, 8)}…</div>
262
  </td>
263
  <td className="px-4 py-3">
264
  <span className={`text-xs px-2 py-0.5 rounded-full ${PLAN_COLORS[org.subscriptionPlan] ?? 'bg-slate-700 text-slate-300'}`}>
tasks/todo.md CHANGED
@@ -1,16 +1,43 @@
1
  # 📋 État des Tâches : XAMLÉ AI
2
 
3
- ## 🛡️ Stabilité & Infrastructure
4
- - [x] **Bug Timeout Gateway** : Implémentation de l'Architecture Async-First (Réponse 200 OK immédiate).
5
- - [x] **Instabilité Jour 1** : Idempotence par verrous Redis (300s) et Inversion de Logique (DB avant MSG).
6
- - [x] **[MEDIA] Non-bloquant** : FFMPEG (conversion MP3) doit tourner dans un sous-processus asynchrone pour ne pas saturer l'event loop du worker.
7
- - **[VISUAL] Team Collection** : Sur le Jour 11, le deep-dive doit obligatoirement valider l'existence d'une image pour extraire le `photoUrl` et l'associer au `name/role` dans `teamMembers`.
8
-
9
- ## 🎓 Pédagogie & Collecte Data
10
- - [x] **Collecte d'Équipe Visuelle (Jour 11)** : Audit terminé. Schéma Prisma, prompts d'extraction AI et persistence worker validés.
11
- - [x] **Audit de Persistance Final** : Les `BusinessProfile` survivent aux redémarrages (Prisma verified).
12
-
13
- ## 🚀 Prochaines Actions
14
- 1. Vérifier l'intégration du schéma `teamMembers` (Prisma).
15
- 2. Tester le flow "Recrutement Visuel" (Image + Texte).
16
- 3. Monitorer les logs Railway pour confirmer l'absence de jobs orphelins.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  # 📋 État des Tâches : XAMLÉ AI
2
 
3
+ ## Complété
4
+
5
+ ### Stabilité & Infrastructure
6
+ - [x] Bug Timeout Gateway : Architecture Async-First (200 OK immédiat)
7
+ - [x] Instabilité Jour 1 : Idempotence Redis (300s) + DB avant MSG
8
+ - [x] FFMPEG non-bloquant : subprocess asynchrone
9
+ - [x] Docker-compose : services api + whatsapp-worker avec healthchecks
10
+
11
+ ### Sécurité
12
+ - [x] SQL Injection analytics : whitelist SELECT uniquement
13
+ - [x] JWT secret fallback supprimé : crash hard si absent
14
+ - [x] Admin routes : role guard SUPER_ADMIN ajouté
15
+
16
+ ### Pédagogie
17
+ - [x] Collecte Équipe Visuelle Jour 11 : enforcement photo obligatoire, extraction AI teamMembers
18
+ - [x] BusinessProfile persistance validée
19
+ - [x] Rapport mensuel par email (cron 1er du mois, 07h30 UTC)
20
+ - [x] Filtre alertes quota par plan (GROWTH/SCALE/ENTERPRISE uniquement)
21
+
22
+ ### Super-Admin Interface (10 pages)
23
+ - [x] PlatformDashboard, OrganizationsManager, UsersManager
24
+ - [x] WhatsAppNumbers avec flow OTP 2 étapes (register → verify)
25
+ - [x] WhatsAppTemplates avec création complète + preview live
26
+ - [x] WhatsAppProfiles, MonitoringAlerts, BillingManager
27
+ - [x] AuditLogs, AIInsights (commandes NL → actions exécutées)
28
+ - [x] 20+ routes backend super-admin (stats, CRUD orgs/users, WA, billing, audit, monitoring, AI)
29
+
30
+ ### Features Admin
31
+ - [x] KB Auto-Generation : description métier → FAQ GPT → embeddings → indexation
32
+ - [x] CSV export transactions billing
33
+ - [x] Reset password utilisateur (email avec lien)
34
+ - [x] Org delete soft, suspend/réactiver
35
+ - [x] Impersonation org (JWT court-terme ORG_ADMIN)
36
+
37
+ ### Tests
38
+ - [x] 31 tests routes API : validation schema, SQL injection guards, auth
39
+
40
+ ## 🔜 Dépend de l'environnement externe
41
+ - [ ] Test OTP WhatsApp en prod (nécessite numéro validé Meta)
42
+ - [ ] Test création template (nécessite WABA approuvé Meta)
43
+ - [ ] Déploiement Railway avec les nouveaux services docker-compose