diff --git a/apps/api/start/routes.ts b/apps/api/start/routes.ts index 7dece90..e2a2e72 100644 --- a/apps/api/start/routes.ts +++ b/apps/api/start/routes.ts @@ -365,20 +365,36 @@ router .use(middleware.auth()) /** - * Demo — auth requise. Mode démo opt-in par org (cf. CLAUDE.md → - * Architecture). Routes opérantes seulement si `org.demo_mode = true`. + * Demo — réservé aux admins Rubis (outil de prospection commerciale). + * Gating fin : + * - GET /state : auth seul, parce que `DemoClock` dans `AppLayout` + * fetch sur chaque page pour tous les users. Un non-admin reçoit + * `{ active: false }` (son org n'est jamais en mode démo) — pas + * de leak, juste un payload neutre. + * - GET /inbox + POST /start, /end, /tick : auth + admin. Mutations + * et lecture des emails capturés (potentiellement sensibles). + * + * UI : la section "Démonstration" dans /parametres est aussi gated + * sur `user.isAdmin` (cf. parametres.tsx). */ + router + .group(() => { + router.get('state', [controllers.Demo, 'state']).as('state') + }) + .prefix('demo') + .as('demo') + .use(middleware.auth()) + router .group(() => { router.post('start', [controllers.Demo, 'start']).as('start') router.post('end', [controllers.Demo, 'end']).as('end') router.post('tick', [controllers.Demo, 'tick']).as('tick') - router.get('state', [controllers.Demo, 'state']).as('state') router.get('inbox', [controllers.Demo, 'inbox']).as('inbox') }) .prefix('demo') - .as('demo') - .use(middleware.auth()) + .as('demo.admin') + .use([middleware.auth(), middleware.admin()]) /** * Dashboard — auth requise. Calculs agrégés on-the-fly (pas de cache V1). diff --git a/apps/web/src/components/settings/BankingSection.tsx b/apps/web/src/components/settings/BankingSection.tsx index a3d7caa..b5fa07d 100644 --- a/apps/web/src/components/settings/BankingSection.tsx +++ b/apps/web/src/components/settings/BankingSection.tsx @@ -81,16 +81,20 @@ export function BankingSection({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [callbackStatus, callbackReason]); - if (!isPaid) { - return ; - } - - // Banking pas encore activé mais teaser ON → afficher "Bientôt disponible" - // pour annoncer la feature aux Pro/Business pendant la fenêtre KYC Powens. + // Order matters : `comingSoon` doit gagner sur `isPaid`. La feature n'est + // pas dispo pour personne pendant la fenêtre KYC Powens, donc inciter un + // Free à passer Pro pour "connecter sa banque" serait trompeur (il + // tomberait sur "Bientôt disponible" après l'upgrade). On annonce la + // feature à venir à tout le monde ; l'upsell reviendra automatiquement + // une fois `BANKING_ENABLED=true` en prod. if (status?.comingSoon) { return ; } + if (!isPaid) { + return ; + } + return ( @@ -188,11 +195,21 @@ function ParametresPage() { - Connecter votre banque - + bankingStatus?.comingSoon ? ( + <> + Bientôt : votre banque connectée à Rubis + + ) : ( + <> + Connecter votre banque + + ) + } + description={ + bankingStatus?.comingSoon + ? "Nous finalisons notre agrément AISP avec Powens. Une fois ouvert, Rubis lira vos virements entrants pour détecter automatiquement les factures payées — en lecture seule, sans déplacement de fonds." + : "Rubis lit vos virements entrants pour détecter automatiquement les factures payées. Lecture seule, aucun déplacement de fonds. Disponible sur les plans Pro et Business." } - description="Rubis lit vos virements entrants pour détecter automatiquement les factures payées. Lecture seule, aucun déplacement de fonds. Disponible sur les plans Pro et Business." > )} - - Faire vivre Rubis en accéléré - - } - description="Mode démo : horloge virtuelle qui avance dans le temps, emails capturés au lieu d'être envoyés à de vrais clients. Idéal pour montrer Rubis à un prospect." - > - - + {isAdmin && ( + + Faire vivre Rubis en accéléré + + } + description="Mode démo : horloge virtuelle qui avance dans le temps, emails capturés au lieu d'être envoyés à de vrais clients. Idéal pour montrer Rubis à un prospect." + > + + + )}