Récemment livré
Ajouté (super-admin)
- Monitoring & incidents centralisés : nouvelle page
/super-admin/monitoring— santé infra (latence DB), tableau par institution (users actifs, dernière connexion, activité 7j, notifications échouées 7j). Sur la fiche tenant, carte « Activity & incidents » : notifications en échec (canal, template, destinataire, erreur) + 25 derniers événements d'audit. EndpointsGET /v1/super-admin/monitoring/healthetGET /v1/super-admin/tenants/:id/activity(agrégation cross-schéma défensive, cache 30 s). - Feature flags par tenant : le super-admin active/désactive des
fonctionnalités par institution (Mobile Money, reporting
réglementaire, portail membre, agent terrain, webhooks). Colonne
Tenant.features(JSONB, migration2026-07-05-tenant-features.sql). Enforcement backend via@RequiresFeature(...)+FeatureGuardglobal : les endpoints d'une fonctionnalité désactivée renvoient 403. Toggles dans la console super-admin (fiche tenant → Features), effet immédiat (invalidation du cache tenant). Défaut : tout activé (rétro-compat,features = null). - Gestion des comptes utilisateurs d'un tenant depuis la console
super-admin (fiche tenant → « Back-office accounts ») :
- Lister les users back-office d'une institution (rôle, statut, 2FA, dernière connexion)
- Réinitialiser le mot de passe d'un user → mdp temporaire généré
(10 caractères lisibles) +
mustChangePasswordforcé, retourné une fois - Activer / désactiver un compte (garde-fou : impossible de désactiver le dernier DIRECTOR actif)
- Désactiver le 2FA d'un user (dépannage perte de device TOTP)
- Endpoints
GET/POST /v1/super-admin/tenants/:id/users*, toutes les actions sensibles auditées (SuperAdminAuditLog).
Corrections
- Super-admin — déconnexion au clic sur un tenant : le layout
redirigeait vers
/super-admin/loginpendant la réhydratation du store persistant (le token était momentanémentnull), éjectant l'opérateur dès qu'une page chargeait « en dur » (deep link/tenants/:id, rechargement). Ajout d'un flag_hasHydrated(onRehydrateStorage) : la redirection n'est décidée qu'une fois le localStorage lu. - Super-admin — tables manquantes en prod :
public."SuperAdmin"/public."SuperAdminAuditLog"n'avaient jamais été créées (modèle ajouté après le bootstrap initial,db:pushnon rejoué). Migration2026-06-24-super-admin-tables.sqlajoutée (idempotente).
Site public
- Refonte de la home
kairos-mfi.com— contenu passé d'un ton technique (SaaS, multi-tenant, PostgreSQL) à un ton descriptif orienté bénéfices utilisateur : « Vos membres reprennent le contrôle de leur épargne », « Vos agents rentrent le soir avec des chiffres, pas des feuilles volantes », etc. - Illustration animée dans le hero : téléphone stylisé montrant
l'app membre (solde, transactions, échéance) + badges flottants
(notification push, résumé "Ma journée" agent). Animations CSS
pures (float, fade-slide, pop-in) — respecte
prefers-reduced-motion. - Bibliothèque d'icônes SVG inline (
landing/svg-icons.mjs) — 16 illustrations sur mesure (smartphone, agent, ledger, chart, etc.). Pas de dépendance externe, teinte pilotée parcurrentColor. - Nouvelles sections : "Pour chaque personne dans votre institution" (6 personas), "Comment ça se passe" (4 étapes de mise en route), "Améliorations en continu" (dernière version du changelog en preview), CTA final ("Demander une démo").
- Scroll reveal via
IntersectionObserverinline (~15 lignes JS, pas de lib externe). landing/hero.md,landing/highlights.md,landing/personas.mdrestent éditables — le scriptbuild-landing.mjsextrait les sections et assemble le HTML.
Site public
- Landing kairos-mfi.com : site statique généré automatiquement
à partir de
landing/hero.md,landing/highlights.md,CHANGELOG.md,docs/manuels/*.mdetdocs/modules/*.md. HTML/CSS minimal (~140 KB total), pas de framework, build < 100ms. - Pages :
/(landing marketing avec features + dernières évolutions),/docs/(index manuels + modules),/changelog.html(historique complet), un fichier HTML par manuel et par module. - Le build est intégré à
scripts/deploy.sh:node scripts/build-landing.mjstourne avant ledocker compose up. - CTAs landing →
app.kairos-mfi.com(espace pro) etapp.kairos-mfi.com/portal(espace membre). Sous-domaineapp.à créer en infra (cf. CHANGELOG ci-dessous).
Sur le terrain
- Décaissement de crédit en tournée : nouvel endpoint
POST /v1/field-agent/loan-disbursement(rôles FIELD_AGENT, DIRECTOR). L'agent livre du cash au membre directement chez lui pour un prêt préalablement APPROVED par un DIRECTOR ou CREDIT_MANAGER. Sépération des pouvoirs : l'agent ne décide pas, il livre. - Délègue toute la logique métier (échéancier, écritures comptables
OHADA, passage à ACTIVE, SMS, push, webhook LOAN_DISBURSED) à
LoansService.disburse()en mode "cash direct" (toAccountId: undefined). Ajoute juste un audit logfield-agent.loan-disbursementtagué canal terrain. GET /v1/field-agent/my-dayretourne désormais aussitotalDisbursement(cash sorti vers les membres) en plus detotalDepositettotalRepayment(cash collecté).- Mobile agent : bouton « Décaisser crédit » sur la fiche membre (visible si au moins un prêt en statut APPROVED). Sheet de confirmation affichant le montant approuvé (non modifiable), champ notes libres. Liste « Crédits à décaisser » en bandeau jaune sur la fiche pour signaler la tâche.
Nouveautés
- Push notifications FCM : intégration Firebase Cloud Messaging v1
côté backend (provider OAuth2 service account, fallback Expo Push
préservé pour clients RN/Expo). Câblage automatique sur dépôt /
retrait / remboursement / décaissement / rappels d'échéance (cron).
Module mobile :
firebase_messaging+ register au login, unregister au logout, SnackBar foreground, notif système background. - Mobile agent — Création de membre : formulaire simplifié
(FAB sur liste membres) → POST
/v1/members→ membre PENDING en attente de validation KYC. - Mobile agent — Dépôt cash + remboursement crédit en tournée : bottom sheet sur la fiche membre, opérations sans session caisse, SMS de confirmation au membre.
- Mobile agent — Carte « Ma journée » : résumé temps réel des
opérations collectées (
GET /v1/field-agent/my-day). - Mobile membre — Détail compte + transactions paginées : tap sur un compte → historique avec scroll infini.
- Mobile membre — Détail crédit + échéancier : statut, prochaine échéance, retards.
- Mobile membre — Change-password depuis le profil.
- Mobile membre — Mot de passe oublié par SMS OTP (flow 2 étapes).
- Web back-office — Bouton « Activer / Réinitialiser portail » sur la fiche membre (DIRECTOR, CREDIT_MANAGER, FIELD_AGENT). Génère un mot de passe temporaire 10 caractères lisibles si body vide.
- Backend — Module FieldAgent : 3 endpoints
(
POST /v1/field-agent/cash-deposit,POST /v1/field-agent/loan-repayment,GET /v1/field-agent/my-day) — opérations cash en tournée hors session caisse. - Backend — Reset mot de passe portail membre par SMS OTP :
POST /v1/portal/auth/password-reset/{request,confirm}(réutilise l'infrastructureOtpCodeexistante).
Améliorations
TenantMiddleware: le bypass de résolution tenant pour/portal/*matche maintenant strictement/v1/portal/au lieu de toute URL contenant/portal/. Corrige le 500Cannot read properties of undefined (reading 'schema')qui survenait surPOST /v1/members/:id/portal/enable.POST /v1/members/:id/portal/enableautorise désormaisFIELD_AGENT(DIRECTOR, CREDIT_MANAGER, FIELD_AGENT). Body vide → génère un mot de passe temporaire et le retourne (temporaryPassword).
Corrections
- Mobile : login membre lisait
data['token'](n'existait pas) au lieu dedata['accessToken']. Provoquait un crashtype 'Null' is not a subtype of type 'String'. - Mobile : les Decimal Prisma arrivent en string ;
parseAmount()helper ajouté pour parser robustement (num ou string). - Mobile :
/portal/accountset/portal/loansrenvoient{ items: [...] }, pas uneListdirecte. - Mobile agent : la fiche membre tentait d'appeler
/members/:id/accountsqui n'existe pas. Comptes et crédits sont désormais extraits directement deGET /v1/members/:id(déjà inclus viainclude). - Hostinger SMTP :
.env.prodexposaitSMTP_PASSWORDalors queSmtpEmailProviderlitSMTP_PASS. Renommé pour rétablir l'envoi d'emails (KYC, invitations).
Versions antérieures
Les évolutions livrées avant la mise en place de ce changelog sont
listées dans les manuels par rôle (docs/manuels/) et dans les
docs techniques par module (docs/modules/).