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."
+ >
+
+
+ )}