Commit History
-
fix(admin): replace vague Meta hints with direct clickable links in Direct Setup modal
CognxSafeTrack- WABA ID → business.facebook.com/settings/whatsapp-business-accounts - Meta Business ID → business.facebook.com/settings/info - Token → business.facebook.com/settings/system-users (new hint added) - Updated i18n labels in en/fr/pt295c327 -
feat(admin): add Direct Setup button for super admins on orgs without phone number
CognxSafeTrack- Add "Direct Setup" button (super admin only) on /clients for orgs without a WhatsApp number - Previously the setup modal was only reachable via the Reconfigure button (orgs with a phone) - Add i18n keys: clients.actions.direct_setup (en/fr/pt) - Add scratch scripts for org configuration and DB queries - Add doc: configurer-token-nouvelle-org.md — how to configure a Meta token on a new orga3df350 -
fix(clients): resolve file import dialog error and SSE 401
CognxSafeTrackFile import (Bug 1): - CrmAIAssistant had its own <input type="file"> that called onFileUpload onChange, which then called .click() on #crm-file-upload — a second programmatic file dialog triggered from an onChange handler. Chrome 126+ blocks this as it lacks a direct "user activation". - Fix: replace the embedded <input> with <label htmlFor="crm-file-upload"> so the click routes directly to FileImporter's input. Remove the now-unused onFileUpload prop entirely. SSE 401 (Bug 2): - EventSource (browser API) cannot send custom headers — the JWT token never reached the server, causing a 401 on every SSE connection. - Fix: pass the token as ?token= query param in the EventSource URL (MainLayout + CrmConversationalDashboard). - Backend: verifyJwt now injects the query param token into the Authorization header before calling jwtVerify(), so no route changes are needed.87dcd87 -
feat(i18n): complete super-admin i18n — all 11 pages fully translated
CognxSafeTrackAll 11 super-admin pages now use useTranslation for every visible string. Adds rel_today/rel_yesterday/rel_days_ago keys for WhatsAppProfiles relativeDate, tpl_cat_*_desc keys for template category descriptions, and fixes the SuperAdminLayout NAV_ITEMS destructuring (labelKey vs label).b92ea37 -
feat(i18n): complete admin app internationalization across all pages
CognxSafeTrackReplace all hardcoded French strings with t() calls on 14 pages and sync 130+ new keys across fr/en/pt locale files. Language switching now works everywhere in the admin interface. Pages covered: TrackDaysPage, TrackFormPage, TrackListPage, UserListPage, KnowledgeBasePage, SettingsPage, AnalyticsPage, AIAgentSetup, OnboardingWizard, ResetPasswordPage, ClientsManagementView, ContactsPage, TemplatesPage, TrainingLab.d80fec4
-
fix: restore Limite Quotidienne and complete admin i18n
CognxSafeTrackFix messaging_limit_tier disappearing in /clients page: - fetchMetaStatus was querying WhatsAppPhoneNumber separately, which is tenant-filtered by Prisma extension — returns null when called from another org's JWT context (super-admin viewing all orgs) - Fix: include phoneNumbers in the Organization query (excluded from tenant filtering) and use org.phoneNumbers[0].id directly i18n — pages fully translated (fr/en/pt): - LoginPage: org ID, email, password, subtitles, button, footer - ResetPasswordPage: all 4 states (request/sent/new/success) - BillingPage: 62 keys — wallet, transactions, chart, AI assistant, credit packs, QUICK_QUESTIONS, TX_LABELS, FEATURE_LABELS - ClientsManagementView (/clients): 20 key groups — status badges, WABA labels, tier/quality info, all modals (billing, credits, create org, personality, WA setup), validation errors, toasts Fix TemplatesPage status badges showing raw key strings: - Add 5 missing keys to all 3 locale files: status_approved, sta…4f90920 -
fix(audit): resolve all remaining technical debt issues
CognxSafeTrackC2 - analytics SQL injection: UNION detection, ALLOWED_TABLES allowlist, remove SQL from error responses to prevent schema leakage H1/H2 - override-feedback: move organizationId validation to top of handler before any DB mutation; scope enrollment.findFirst to org H6 - UserListPage: add pagination controls (Précédent/Suivant, 20 per page) L4 - TrackDaysPage: await load() before closing editor to prevent stale data race L5 - admin.ts: replace getOrganizationId() with req.organizationId at 3 call sites, remove now-unused import M1 - EntityResolver: replace any[] types with proper Prisma payload types (ResolvedUser/Contact/Organization/Enrollment with exact include shapes) L8 - AI command fuzzy org match: return pending_confirmation when org found by partial name match, inject orgId for unambiguous confirmation retrya888244 -
fix(security): resolve critical and high technical debt from audit
CognxSafeTrackCRITICAL: - Exclude passwordHash from enrollment list response (admin.ts) - Add sanitizeOrg() to strip API keys/tokens from all org GET responses - Fix CORS wildcard on SSE stream; use request origin instead - Restrict super-admin routes to SUPER_ADMIN role only (was also allowing ADMIN) - Replace all Zod .parse() with .safeParse() → proper 400s instead of 500s (ai.ts, admin.ts) HIGH: - Increase ExerciseHandler Redis dedup lock from 30s to 300s - Add take:500 to unbounded trainingData.findMany (OOM risk) - Add take limits to message/contact findMany queries - Add Redis lock in EnrollHandler to prevent duplicate enrollments (TOCTOU) - Wrap override-feedback in try/catch; return 404 on P2025 - Use atomic increment for wallet credits (was TOCTOU race condition) - Scope messages/send to requesting org (was cross-org accessible) MEDIUM: - decryptSecrets returns new object instead of mutating argument in-place - GeminiProvider.generateStructuredData now validates response with Zod schema -…a8e18d6 -
fix(super-admin): guard null wabaId, org.id, amount in profiles/templates/billing/AI pages
CognxSafeTrackb5dcbdb -
feat(super-admin): WhatsApp OTP registration and template creation
CognxSafeTrack- WhatsApp service: add registerPhoneNumber() and verifyPhoneNumber() methods - Super-admin routes: POST /whatsapp/numbers/register and /verify for Meta OTP flow - Super-admin routes: POST /whatsapp/templates to submit templates to Meta for approval - WhatsAppNumbers page: 2-step modal (orgId + phoneNumberId + PIN → OTP code) - WhatsAppTemplates page: full creation modal with category cards, char counters, live preview bubble170eb5d -
feat(super-admin): org delete, user password reset, complete CRUD coverage
CognxSafeTrackBackend: - DELETE /v1/super-admin/organizations/:id — soft-delete via subscriptionStatus='DELETED' + isHardStopped=true - POST /v1/super-admin/users/:id/reset-password — generates 1h JWT reset token, emails link via scheduleEmail (same flow as /forgot-password) Frontend: - OrganizationsManager: red trash icon per row calls DELETE; confirmation dialog before destructive action; suspend icon color changed to amber - UsersManager: key icon per row calls reset-password endpoint; guards against users with no email addressb8629ec -
feat: complete super-admin interface + enforce Day 11 photo requirement
CognxSafeTrackSuper-admin (3 missing pages + 3 new backend routes): - AuditLogs page: date-range / action filter, paginated table with inline JSON detail expansion - WhatsAppTemplates page: org directory with lazy per-org template fetch; category/status badges; expandable template panel per row - WhatsAppProfiles page: card grid showing org branding (logo, color, WABA ID, token age); inline edit form with live logo preview and color picker; PATCH /v1/super-admin/whatsapp/profiles/:id - Backend: GET /audit-logs, GET/PATCH /whatsapp/profiles, GET /whatsapp/templates added to super-admin.ts - SuperAdminLayout: 10 nav items (added Templates WA, Profils WA, Audit Logs); SuperAdminRouter wired to all new routes Day 11 photo enforcement (tasks/todo.md item resolved): - FeedbackHandler: if currentDay === 11 and no imageUrl, send multilingual photo prompt and return early — exercise cannot be validated without a team member photo8f2286f -
feat: CSV export, monthly report, and quota alert plan filter
CognxSafeTrack- BillingPage: add "CSV" download button that exports all wallet transactions (date, type, description, amount, balance) as UTF-8 CSV - scheduler.ts: add startMonthlyReportScheduler() — cron on 1st of month 07:30 UTC; sends per-org recap of AI calls, WhatsApp messages, top-3 features, credits recharged/spent, and current balance - index.ts: register startMonthlyReportScheduler() alongside existing crons - usage-tracker.ts: quota alert (85% threshold) now only fires for GROWTH, SCALE, and ENTERPRISE plans; STARTER alerts were high-noise / low-valuea24fb7f -
feat(settings): expose branding logoUrl and primaryColor fields
CognxSafeTrackAdds a Branding section to SettingsPage that reads/writes org.brandingData.logoUrl and org.brandingData.primaryColor. Includes a live logo preview and a color picker + hex input. test(security): add 15 SQL injection and auth tests Pure unit checks for the DANGEROUS_PATTERNS set used in the text-to-SQL guard (UNION, DELETE, DROP, comments, stacked queries). Integration auth checks for super-admin JWT enforcement.ec1b111 -
feat: add XAMLÉ Platform Admin super-admin interface
CognxSafeTrackWhen selectedOrgId === 'xamle-admin-org', the admin switches to a full platform management dashboard instead of the normal org layout. Backend (apps/api): - New /v1/super-admin/* routes (SUPER_ADMIN/ADMIN role required): platform stats, organizations CRUD + suspend + impersonate, cross-org users + role change, WhatsApp numbers, billing transactions + manual credits, monitoring health, AI agentic command - Fixed SQL injection in analytics text-to-SQL (UNION/DROP/comment blocking) - Removed JWT secret fallback — crashes on startup if JWT_SECRET is missing - Added ORG_ADMIN role guard to all /v1/admin/* routes Frontend (apps/admin): - SuperAdminRouter: nested routes under /platform/* - SuperAdminLayout: dark sidebar (slate-950/violet), 7 nav items - PlatformDashboard: 6 KPI cards, system health, active alerts - OrganizationsManager: searchable table, suspend/edit drawer, create modal - UsersManager: cross-org table with inline role-change - WhatsAppNumbers: all registered numbers …6282d86 -
feat(whatsapp-setup): make token optional when org already has one stored
CognxSafeTrackIf an org already has a systemUserToken, the /whatsapp-setup route now reuses it automatically. Admins can reconfigure WABA ID + Phone Number ID without re-entering the token — useful for adding a new phone number to an existing connected account.de46926 -
feat(settings): add WhatsApp reconfiguration form to SettingsPage
CognxSafeTrackBusiness owners can now connect or reconnect their WhatsApp account directly from Settings without going through the onboarding wizard. Form accepts WABA ID, System User Token, Phone Number ID, and phone number.ec8f103 -
fix(crm-inbox): unwrap paginated messages response to restore conversation list
CognxSafeTrackThe GET /messages endpoint was changed to return {data, total, limit, skip} but the frontend still consumed it as an array, causing setInboxMessages to receive an object and the conversation list to always show empty.0da3feb -
fix(audit): resolve remaining minor technical debt issues
CognxSafeTrack +1: Claude Sonnet 4.6AI/BYOK: - generateText now respects tenant BYOK keys via getProvidersForTenant() (was using global registry) - generateText now applies exponential backoff retry via AIService.withRetry() Backend: - notifications.ts: return 503 instead of 200+undefined when VAPID_PUBLIC_KEY absent - BillingPage: support WA number now reads from VITE_SUPPORT_WA_NUMBER env var (was hardcoded fake number) - KnowledgeBasePage: show toast.error on delete failure (was silently swallowed) DB: - Add @@index([organizationId, exerciseStatus]) on UserProgress for live-feed query performance - Add @@index([createdAt]) on AuditLog for time-range admin queries - Migration 20260513000005 applied to production DB i18n: - Add knowledge.delete_error translation key to all 4 locales (fr, en, es, pt) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>d74c1b3
-
fix(audit): resolve 15 critical and major technical debt issues
CognxSafeTrack +1: Claude Sonnet 4.6Security: - campaigns.ts: prevent cross-tenant write by using req.organizationId instead of URL param - billing.ts: restrict org mode change to SUPER_ADMIN only (was incorrectly allowing ADMIN) - CommandHandler: guard SEED command with NODE_ENV !== 'production' - search.ts: return [] instead of fabricated mock data when SERPER_API_KEY absent - scheduler: filter out hard-stopped organizations from nudge and lesson delivery - payments.ts: HMAC signature verification for Wave and Orange Money webhooks + fix track.priceAmount Data integrity: - schema: TrackDay.dayNumber Float → Int, Enrollment.currentDay Float → Int - schema: add @@unique([userId, trackId]) to Enrollment to prevent race condition double-enrollment - Migration 20260513000004 applied to production DB Performance: - admin.ts /live-feed: fix N+1 queries with batch findMany + in-memory map - organizations.ts /:id/messages: add pagination (was unbounded, OOM risk) - crm-service.ts listMessages: add skip/limit/total support Co…97f541f -
feat: gold standards tests (15 days + STT + vision) & botName editable in AI setup
CognxSafeTrack +1: Claude Sonnet 4.6- ai-gold-standards: 40 test cases covering all 15 lesson days, STT smoke, vision (day 11 imageUrl), CRM broadcast/conversation, curriculum (FR+EN), pitch deck EN, business profile extraction days 1-6 with quality assertions - AIAgentSetup: botName field now editable (was hardcoded 'Agent IA'); loaded from personality API on mount, saved with PATCH; falls back to 'Agent IA' if blank - i18n: add bot_name_label/placeholder/hint keys to FR, EN, ES, PT locales Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>1c602ea -
chore: remove Stripe — replaced by Orange Money / Wave payment gateway
CognxSafeTrack +1: Claude Sonnet 4.6Delete stripe.ts route file, remove import and /v1/stripe registration from app.ts, drop stripe npm dependency, remove deprecated stripeWebhookRoute stub from payments.ts, and remove stripeCustomerId from admin Organization interface. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>641e72b -
feat: complete agentic audit roadmap — retry, KB generate, real costs, traceId, rate-limit
CognxSafeTrack +1: Claude Sonnet 4.6- ai-sdk: exponential backoff retry (3 attempts, 1s→2s) in callWithFailover; skips 4xx and QuotaExceededError - api/whatsapp: traceId (crypto.randomUUID) propagated to worker bridge via x-trace-id header - api/whatsapp: route-level rate limit 500/min on POST /webhook, returns 200 on rejection - api/organizations: POST /:id/kb/generate — GPT-4o-mini JSON mode generates FAQ pairs, embeds via IndexingService - api/indexing-service: new service batching OpenAI text-embedding-3-small + pgvector INSERT - admin/AnalyticsPage: real cost from UsageEvent aggregate + byFeature breakdown table - admin/KnowledgeBasePage: KB auto-generation panel (description textarea, generate button, FAQ preview) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>b438786 -
feat: agentic platform — text-to-sql, pedagogy advisor, security hardening & performance
CognxSafeTrack +1: Claude Sonnet 4.6- Analytics: POST /v1/analytics/query — natural language → GPT-4o-mini → SELECT → results (Text-to-SQL) - Analytics UI: example chips, SQL results table, 'Voir SQL' toggle - Billing: role guard on change_organization_mode (ADMIN+ only) - Nav: /ai-setup accessible to all org members (not just super admin) - Scheduler: startPedagogyAdvisor() — Wednesday 08:30 UTC cron, at-risk nudges (5 languages) + email report - S1 security: organizationId always from JWT for non-super-admin (never from header) - A6: InboundHandler passes user.language to Whisper transcription (Wolof hint: 'fr') - A2: Redis 6h cache for personalized lessons keyed by (trackDayId, activity, language) - P1: sendLessonDay() parallelized from 4 sequential DB queries to 2 (user+trackDay batch, userProgress parallel) - D2: Redis 30s dedup lock on exercise submission to prevent double-submit Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>5b8761d -
fix(admin): migrate raw fetch() to api client in ClientsManagementView
CognxSafeTrack +1: Claude Sonnet 4.6Removes the last manual fetch() call outside lib/api.ts — billing/admin/allocate now goes through api.post() which injects Authorization and Content-Type automatically. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>60c3938 -
feat: Claude Sonnet 4.6 provider + Stripe billing automation
CognxSafeTrack +1: Claude Sonnet 4.6AI: - packages/ai-sdk: ClaudeProvider implements LLMProvider via tool_use structured outputs + zod-to-json-schema - Claude registered at priority 120 (TEXT only), supersedes Gemini for feedback/lessons/chat — -40% cost, better French - ANTHROPIC_API_KEY added to API and worker env validation Stripe: - apps/api/src/routes/stripe.ts: POST /v1/stripe/webhook handles checkout.session.completed, subscription.updated/deleted, invoice.payment_failed/succeeded - Schema: stripeCustomerId + stripeSubscriptionId added to Organization - tenant.ts: 402 gate for isHardStopped orgs (x-api-key calls always pass) - ClientsManagementView: SUSPENDU badge on hard-stopped orgs - STRIPE_SECRET_KEY + STRIPE_WEBHOOK_SECRET added to config Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>4a0c3ba -
feat(crm-ux): tag management, unread badge, handoff release UI
CognxSafeTrack +1: Claude Sonnet 4.6- ContactsPage: tag filter chips, per-contact tag editing with inline input, add/remove tags via PATCH API, colSpan fix for new Tags column - MainLayout: SSE-powered unread message badge on conversations nav item, resets on navigation - UserListPage: soft-delete button, handoff status fetch + "Libérer l'IA" button in conversation modal - admin routes: DELETE /users/:id (soft-delete), GET/DELETE /users/:id/handoff endpoints Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>42a2598 -
fix(org-resolve): support legacy non-UUID custom IDs (e.g. default-org-id) + analytics orgId header
CognxSafeTrack +1: Claude Sonnet 4.6- resolveOrgId now uses findFirst with OR [slug, id] so literal custom IDs like "default-org-id" (the seed org XAMLÉ Global) are resolved correctly instead of returning 404 - ContactsPage.tsx: pass selectedOrgId to analytics/usage call — was missing the x-organization-id header, causing systematic 400 on the contacts page Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>2cc44c6 -
feat(agentic-week2-4): 5 major features — AI content generator, handoff, tags, soft-delete, SSE
CognxSafeTrack +1: Claude Sonnet 4.6## Agent Créateur de Contenu (Semaine 3) - AIService.generateCurriculum() — structured GPT-4o prompt generates full Track + TrackDays - POST /v1/organizations/:id/content/ai-generate — persists curriculum directly to DB - TrackListPage: "Générer avec IA" button + modal (description, numDays, language, audience) - Auto-redirects to the generated program days after creation ## Human Handoff Detection (Semaine 4) - AIAgentHandler: detects 15 multilingual handoff keywords (FR/EN/ES) - Sets Redis flag handoff:{userId}:{orgId} (24h) — pauses AI responses - Emails org admin with contact info + link to admin UI - Handles subsequent messages while in handoff mode gracefully ## Contact Segmentation by Tags (Semaine 2) - Contact.tags String[] added to Prisma schema (migration + db:push applied) - GET /contacts/tags — list all unique tags in org - PATCH /contacts/:id/tags — set tags on a contact - GET /contacts?tags=tag1,tag2 — filter contacts by tags (hasSome) ## SSE Real-time Updates (Semaine…99f6bca -
fix(conversations): resolve /conversations blank page + slug→UUID KB routes + WABA banner
CognxSafeTrack +1: Claude Sonnet 4.6- Add missing /conversations route to App.tsx (was nav-linked but unregistered) - Apply resolveOrgId() to all 5 KB routes so slug-based org IDs (e.g. xamle-admin-org) no longer cause 404/400 - TemplatesPage: detect WABA-not-configured 400 and show amber banner instead of silently failing - KnowledgeBasePage: show toast on index-kb 400 (no KB URL) with actionable message Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>9f72dd7 -
feat(admin): UX non-tech complète + i18n 4 langues
CognxSafeTrack +1: Claude Sonnet 4.6- Phases 1-5 : masquage JSON avancé, chat IA global (AdminChat), onboarding WhatsApp guidé, templates assistés IA, agent IA setup guidé - i18n exhaustif : ~70 nouvelles clés en FR/EN/ES/PT pour tous les composants ajoutés (AdminChat, AIAgentSetup, SettingsPage, TemplatesPage, OnboardingWizard) - Chat IA multilingue : l'assistant répond désormais dans la langue de l'utilisateur (FR/EN/ES/PT) sur toutes les pages Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>66ff7a1 -
feat(billing): refonte UX non-tech + enrichissement contexte IA
CognxSafeTrack +1: Claude Sonnet 4.6Frontend (BillingPage): - Renommage "Wallet" → "Crédits disponibles" (langage non-technique) - Badge projection dynamique "À ce rythme : encore ~X jours" calculé depuis les transactions des 7 derniers jours - Questions suggérées cliquables (quick-reply chips) avant le 1er message - Suppression "Tokens entrants/sortants" (jargon incompréhensible) - Transactions filtrées : BYOK masqué, layout liste simple vs tableau - Section "Où vont vos crédits IA ?" remplace le jargon feature/type - Indicateur de frappe animé (3 points) dans le chat - Packs recharge avec explication "≈ N messages IA" et badge populaire - Texte WhatsApp clarifié : "Gratuit pour vous — Meta facture directement" Backend (billing.ts /chat): - Wallet balance + isHardStopped ajoutés dans le contexte IA → l'assistant peut maintenant répondre "combien de crédits me reste-t-il" - Burn rate (débit moyen 7j) calculé et injecté : "À ce rythme, votre solde durera encore X jours" - Statut wallet (SUSPENDU / bas / actif) expl…f5ea14d -
feat(billing): close 3 remaining wallet gaps from implementation plan
CognxSafeTrack +1: Claude Sonnet 4.6⑫ sendTextMessage() wallet debit coverage: - Add organizationId?: string to sendTextMessage() config — calls trackWhatsAppSent() automatically after each successful send - NudgeHandler, ContentHandler, EnrollHandler, MediaHandler, AdminHandler: pass organizationId in tenantConfig so every outbound message triggers the debit without touching each callsite individually ⑯ GET /billing/summary now includes wallet block: - wallet: { balance, isHardStopped, creditToFcfa, balanceFcfa } appended to every summary response ⑱ BillingPage: wallet transaction table: - Table showing last 20 transactions (date, type, description, amount, balance after) - Type labels with emoji (recharge vs debit), BYOK badge, red/green amount colouring - Inserted between feature breakdown and chat widget Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>f5126bc -
feat(billing): implement full wallet/ledger system with hard-stop enforcement
CognxSafeTrack +1: Claude Sonnet 4.6- Add walletBalance + isHardStopped to Organization schema; WalletTransaction ledger model with full audit trail - wallet.ts (worker): debitWallet (atomic $transaction), creditWallet (with AuditLog), checkWalletBalance (Redis 30s cache), alert queuing (low/critical/exhausted/restored) - wallet.ts (api): creditWallet for admin top-up with cache invalidation - usage-tracker.ts: debitWallet integrated into trackAiUsage, trackAudioUsage, trackWhatsAppSent; BYOK orgs skipped - BroadcastHandler + CampaignHandler: bulk debit after send (BROADCAST_PER_USER per recipient) - requireCredits.ts: migrated from aiCreditsUsed/Limit to walletBalance/isHardStopped; shares Redis cache key with worker - whatsapp-logic.ts: replaced assertCredits/CreditExhaustedError with checkWalletBalance/WalletExhaustedError - ai.ts: added requireCredits preHandler to all 11 AI routes (was 4) - billing.ts: /admin/allocate now calls creditWallet(); new GET /wallet route (balance + 20 last txns) - BillingPage: wallet card…0fd3320 -
feat(billing): enforce AI credit limits across all surfaces
CognxSafeTrack +1: Claude Sonnet 4.6- credit-guard.ts (worker): assertCredits() blocks AI when creditsUsed >= creditsLimit; Redis cache 60s prevents DB hit on every message - whatsapp-logic.ts: credit guard injected before handler chain; RECHARGE keyword bypasses the guard so users can always request a payment link - CommandHandler: new RECHARGE keyword sends payment URL or contact message - i18n: credit_exhausted + recharge_link + recharge_contact in 5 languages - requireCredits middleware (api): 402 on personalize-lesson, tts, generate-feedback, crm/generate-campaign - billing route: POST /v1/billing/admin/allocate (SUPER_ADMIN only) supports set/add actions + optional usage reset + Redis cache invalidation - ClientsManagementView: "Crédits IA" button (SUPER_ADMIN only) per org opens allocation modal with set/add/reset controls - BillingPage: red exhaustion banner when creditsUsed >= creditsLimit Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>6484585
-
fix(admin): make all modals scrollable and responsive on mobile
CognxSafeTrack +1: Claude Sonnet 4.6RechargeModal (BillingPage), create-org modal (ClientsManagementView), and create-template modal (TemplatesPage) now use the bottom-sheet pattern: fixed header + scrollable body + fixed footer, max-h-[90vh], and items-end on mobile so they slide up from the bottom. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>a3f8777 -
fix(training): fix o.map crash on /training page
CognxSafeTrack +1: Claude Sonnet 4.6TrainingService.getPendingAudios returns { pending, total, page, limit } but TrainingLab was calling setAudios(data) directly — audios became an object and .map() crashed on page load. Also guard missingWords ?? []. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>e8b9f53 -
feat(billing): complete billing system, push notifications, and tech debt fixes
CognxSafeTrack +1: Claude Sonnet 4.6- Usage tracking: instrument pedagogy, audio transcription, AI Agent mode (was silently untracked) - EmailHandler: implement templates 2 (password reset), 3 (quota alert), 4 (monthly report) - Quota alert: auto-queue email + push at 85% threshold via maybeQueueQuotaAlert - Push notifications: PushHandler + send-push BullMQ job; sendPushToUser scoped by organizationId to prevent cross-tenant reads - BillingPage: add credit recharge modal with 3 packs, null guard for missing orgId - i18n: add nav.billing key in fr/en/es/pt locales - Tests: billing.test.ts (10 tests), payments.test.ts (13 tests) - Tech debt: fix empty catch blocks in ai-pedagogy.ts, exclude usage tokens from CRM API response, pass organizationId to generateChat in AIAgentHandler - Docs: add backlog and business model to docs/business/ Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>8280d7d
-
fix(ux): dashboard error state with 15s timeout and retry button
CognxSafeTrack +1: Claude Sonnet 4.6Add fetchError state, Promise.race timeout (15s), and an error screen with AlertTriangle + "Réessayer" button if the API doesn't respond. Prevents the spinner from hanging indefinitely on network failures. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>34c32dc -
fix(audit): resolve technical debt + UX issues from 2026-05-11 audit
CognxSafeTrack +1: Claude Sonnet 4.6Backend: - Add missing DB indexes on Response.userId and Response.enrollmentId - Add 15s timeout with fallback on AI personalization in pedagogy.ts - Fix race condition in NavigationHandler with atomic $transaction - Fix OnboardingHandler hard reset: use FK cascades, 5-lang confirmation - Change seedDatabase to static import in CommandHandler - Add proper pagination to /enrollments admin endpoint UX/Admin: - Fix responsive grid on dashboard stats (1col mobile → 5col desktop) - Show filename + status label during file upload in ContactsPage - Add success/error toast with import summary on contact CSV/XLSX upload - Add inline form validation with field-level errors in ClientsManagementView - Wire up Copy button on AI-generated messages in ContactsPage Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>9fe2cc7 -
feat(admin): responsive bottom sheet + spring animation for TierInfoModal
CognxSafeTrack +1: Claude Sonnet 4.6- Mobile: slides up from bottom (spring, damping 32), rounded-t-3xl, drag handle pill, max 85dvh with internal scroll + overscroll-contain - Desktop (sm+): centered card with same spring entrance - AnimatePresence handles exit animation (slides back down) - ESC key closes + body scroll lock during open - Backdrop separated from panel div for correct z-layering Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>74aa3a1 -
feat(admin): tier color + info modal for WhatsApp messaging limits
CognxSafeTrack +1: Claude Sonnet 4.6- Color-coded limit display: red (TIER_50/250), blue (TIER_1K), emerald (10K+) - Info button (ℹ) on DailyLimitCell and billing modal opens TierInfoModal - TierInfoModal: ladder of all 6 Meta tiers with current highlighted, quality rating panel (GREEN/YELLOW/RED) with contextual explanation - BillingTierDisplay: reusable component for billing modal with same colors Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>00b9619 -
refactor(debt): resolve all 10 technical debt items from audit
CognxSafeTrack +1: Claude Sonnet 4.6- Redis: singleton pattern (lib/redis.ts) in api + worker, removes 16+ new Redis() calls - Meta token: systemUserTokenIssuedAt field + weekly cron monitor (warn @50d, critical @55d) - Rate limiting: @fastify/rate-limit global 200/min, login 10/min, forgot-password 3/min - Dead code: delete WhatsAppConnectButton.tsx, archive one-off scripts - Silent catches: logger.warn(err) on all backend catch blocks, console.warn on frontend - Meta API types: 5 typed interfaces replace 6 as any casts in organization.ts - Frontend logger: lib/logger.ts wraps Sentry (active when VITE_SENTRY_DSN is set) - Migration: add systemUserTokenIssuedAt + metaBusinessId to Organization schema Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>a966957
-
fix(wizard): replace alert() with toast + add tech debt audit doc
CognxSafeTrack +1: Claude Sonnet 4.6Replaces native alert() calls in OnboardingWizard with useToast(). Adds docs/audit_dette_technique_10052026.md with 10 identified debt items (2 critical: token expiry monitoring, Redis singleton). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>e1ca6ab -
feat(wizard): rewrite OnboardingWizard for super-admin org creation
CognxSafeTrack +1: Claude Sonnet 4.63 steps: Organisation (name + slug auto + mode selector) → Administrateur (name + email + optional password) → WhatsApp (optional: direct config OR embedded signup OR skip). Fixes: - admin credentials were never collected or sent to the API - mode was hardcoded to EDTECH - contract/signerName were collected but never transmitted - WhatsApp is now integrated but fully optional (configurable later) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>bccc5b6 -
feat(meta-status): sync daily limit + quality rating live from Meta phone number API
CognxSafeTrack +1: Claude Sonnet 4.6fetchMetaStatus now calls GET /{phoneNumberId}?fields=messaging_limit_tier,quality_rating to get the real tier from Meta (TIER_1K, TIER_10K, TIER_100K, UNLIMITED) instead of a hardcoded value inferred from WABA status. Frontend: TIER_LABELS map for human-readable labels, QualityDot badge (green/amber/red) displayed next to the limit. dailyLimitLabel now uses the real tier from the API. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>8aa43f4 -
feat(meta-status): store metaBusinessId for reliable business verification
CognxSafeTrack +1: Claude Sonnet 4.6Add optional `metaBusinessId` field to Organization (schema + shared-types). fetchMetaStatus now queries /{businessId}?fields=verification_status directly when metaBusinessId is stored — bypasses WABA edge permission restrictions that block test WABAs and certain system user token scopes. Direct setup form gains a "Business ID Meta" input that saves via PUT /organizations/:id. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>70a5a84 -
fix(meta-status): split combined Meta API call + fix business badge for test WABAs
CognxSafeTrack +1: Claude Sonnet 4.6Split `account_review_status,business{...}` into two independent calls so test WABAs (which forbid the `business` field) no longer poison WABA status. Business verification is now optional/non-fatal (wrapped in try/catch). BusinessBadge and BusinessVerificationCell now show "—" with neutral styling when `businessId` is undefined (field inaccessible) instead of misleading "Non vérifiée" amber warning + "Vérifier →" link. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>7e2166c
-
feat(whatsapp-setup): direct config mode for platform-owner accounts
CognxSafeTrack +1: Claude Sonnet 4.6Backend: - accessToken now optional in /whatsapp-setup — falls back to WHATSAPP_ACCESS_TOKEN env var (for platform-owner self-setup) Frontend: - "Reconfigurer WhatsApp" button now opens a choice modal instead of launching Embedded Signup directly - Option A (Direct): enter WABA ID + optional token — for accounts already configured on Meta (token from env var used if field left empty) - Option B (Embedded Signup): Facebook login popup — for new client accounts - Phone Number ID pre-filled from existing DB record - Auto-refreshes Meta status after successful save Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>2f67731 -
fix(admin): show Reconfigurer WhatsApp when phone exists but token/wabaId missing
CognxSafeTrack +1: Claude Sonnet 4.6Orgs migrated from single-tenant setup have a WhatsAppPhoneNumber record but no wabaId/systemUserToken in Organization table. Without the token the Meta status endpoint returns configured:false and WABA shows "Non connecté". When fetchMetaStatus returns configured:false on an org that already has a phone number, replace the "En ligne" indicator with an amber "Reconfigurer WhatsApp" button that re-triggers the Embedded Signup flow to store the correct wabaId and long-lived token. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>4f87bfb