Files
colibre/docs/superpowers/specs/2026-06-24-espace-compte-design.md
Colin Maudry ba5ad2c2ac docs(compte): design de l'espace compte multi-sections (#73)
Design validé en brainstorming : coquille account_shell (sidebar +
offcanvas), routage /compte/* (une page par section), 3 niveaux d'accès
avec stub d'abonnement, section Compte (email avec re-vérification,
mot de passe, suppression via modale).

Refs #73

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-24 18:32:17 +02:00

6.9 KiB

Espace « Mon compte » — restructuration et section Compte

Date : 2026-06-24 Statut : design validé Issue : #73 (comptes premium — abonnement mensuel à prix libre)

Contexte

La page /compte actuelle (src/pages/compte.py) est une page unique étroite qui affiche l'email, un formulaire de changement de mot de passe (POST vers /auth/change-password) et un bouton de déconnexion. L'authentification passe par un Blueprint Flask /auth/* (src/auth/routes.py) avec des formulaires HTML côté serveur ; les pages sont des register_page Dash.

L'objectif est de transformer /compte en un espace à plusieurs sections (jusqu'à 6 à terme) avec une navigation latérale, et d'implémenter intégralement la première section, Compte (/compte/admin).

Sections cibles

Section URL Statut Accès
Compte /compte/admin ce lot compte
Abonnement /compte/abonnement coquille compte
Mes archives /compte/archives futur abonnement
Mes filtres /compte/filtres futur abonnement
Mon SIRET /compte/siret futur abonnement

Niveaux d'accès

Trois niveaux :

  1. Visiteur sans compte → redirigé vers /connexion?next=<path>.
  2. Compte sans abonnement valide → accès à Compte et Abonnement uniquement ; toute page gated redirige vers /compte/abonnement.
  3. Compte avec abonnement valide → accès à toutes les sections.

L'abonnement n'est pas encore implémenté : on introduit une abstraction current_user_has_subscription() -> bool qui renvoie False pour l'instant. C'est le seul point à brancher lorsque la facturation arrivera.

Architecture

Routage (une page par section)

  • Chaque section est un register_page distinct sous /compte/*, fidèle à la convention « une page = un fichier » du projet.
  • La page /compte actuelle devient une redirection vers /compte/admin (renvoie dcc.Location(href="/compte/admin")).
  • Ce lot crée /compte/admin (section Compte) et la coquille /compte/abonnement (page enregistrée minimale, contenu de vente à venir). Les sections gated (archives, filtres, siret) ne sont pas créées dans ce lot.

Coquille partagée

Nouveau module src/pages/_compte_shell.py exposant :

  • account_shell(active: str, contenu) -> Component Construit la mise en page commune :

    • Sidebar verticale (desktop) listant les sections accessibles à l'utilisateur courant ; l'entrée active est surlignée.
    • Bouton « ☰ Sections » + dbc.Offcanvas (mobile) reprenant la même liste.
    • Insère contenu dans la zone principale.
    • Sections gated masquées tant que current_user_has_subscription() est faux (Compte + Abonnement seules visibles). La vente des fonctionnalités se fera dans le contenu de la section Abonnement, pas via des entrées verrouillées.
    • Construit avec Dash Bootstrap Components (dbc.Row/dbc.Col, dbc.Nav/dbc.NavLink, dbc.Offcanvas).
  • account_guard(require_subscription: bool) -> Component | None Appelé en tête de chaque layout() :

    • non authentifié → dcc.Location(href="/connexion?next=<path>")
    • authentifié, require_subscription et pas d'abonnement → dcc.Location(href="/compte/abonnement")
    • sinon → None (la page se rend normalement).

La définition des sections (libellé, URL, icône, require_subscription) est centralisée dans une structure unique dans _compte_shell.py, pour que sidebar, offcanvas et gardes restent cohérents et que l'ajout d'une section future tienne en une ligne.

Section Compte (/compte/admin)

Agencement : sections empilées avec séparateurs, dans account_shell.

  1. Adresse email — affiche l'email actuel, champ « nouvelle adresse », bouton « Mettre à jour l'email » → POST /auth/change-email.
  2. Mot de passe — formulaire existant (mot de passe actuel + nouveau + confirmation), inchangé → POST /auth/change-password. Sa cible de redirection passe de /compte à /compte/admin.
  3. Zone danger — encadré rouge, bouton « Supprimer mon compte » qui ouvre une modale dbc.Modal de confirmation demandant la re-saisie du mot de passe → POST /auth/delete-account.

Le bouton Déconnexion est conservé (POST /auth/logout).

Les messages de succès/erreur sont passés en query string et rendus en dbc.Alert, sur le modèle actuel ; ERROR_MESSAGES est étendu.

Changement d'email avec re-vérification

Le changement d'email ne prend pas effet immédiatement : la nouvelle adresse doit être confirmée par email, par cohérence avec l'inscription. L'ancienne adresse reste active tant que la nouvelle n'est pas vérifiée (évite qu'une faute de frappe verrouille le compte).

Flux :

  1. POST /auth/change-email (@login_required) :
    • valide la nouvelle adresse (validate_email, normalisée en minuscules) ;
    • vérifie l'unicité (get_user_by_email → erreur email_taken) ;
    • enregistre l'adresse en attente sur l'utilisateur et envoie un lien de vérification à cette nouvelle adresse (réutilise le mécanisme de jetons de vérification existant) ;
    • redirige /compte/admin?email_pending=1.
  2. Clic sur le lien → la nouvelle adresse devient l'email du compte et le pending_email est effacé, puis redirection vers /compte/admin?email_changed=1.

Implications stockage : ajout d'un champ pending_email à la table users (migration de schéma) et nouvelle fonction DB update_email(user_id, email). Le détail exact du jeton (réutilisation de create_email_verification_token vs table dédiée) est laissé au plan d'implémentation.

Suppression de compte

POST /auth/delete-account (@login_required) :

  • vérifie le mot de passe courant (check_password_hash) → sinon ?error=invalid_current_password ;
  • purge les jetons (delete_email_verification_tokens_for_user, delete_password_reset_tokens_for_user) puis delete_user(current_user.id) ;
  • logout_user() ;
  • redirige vers /?account_deleted=1.

Tests

  • Accès (Selenium) : les trois niveaux, avec le stub d'abonnement forcé à True/False ; redirection des non-authentifiés ; redirection des non-abonnés sur une page gated ; présence/masquage des entrées de sidebar.
  • Navigation (Selenium) : /compte/compte/admin ; surlignage de la section active ; ouverture de l'offcanvas mobile.
  • Routes :
    • change-email : succès (passage en attente), email déjà pris, email invalide.
    • delete-account : mauvais mot de passe (refus), succès (compte supprimé + déconnexion).

Hors périmètre

  • Contenu réel de la section Abonnement (perks, tarifs, paiement).
  • Sections Archives, Filtres, Mon SIRET.
  • Implémentation réelle de la facturation derrière current_user_has_subscription().