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 +1 -1
- apps/admin/src/pages/super-admin/BillingManager.tsx +1 -1
- apps/admin/src/pages/super-admin/OrganizationsManager.tsx +1 -1
- apps/admin/src/pages/super-admin/WhatsAppProfiles.tsx +4 -2
- apps/admin/src/pages/super-admin/WhatsAppTemplates.tsx +2 -2
- tasks/todo.md +41 -14
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
|
|
|
|
|
|
|
| 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 |
-
##
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
- [x]
|
| 7 |
-
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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
|