Aller au contenu

Story CP-008 : Formulaire d'Auto-Inscription + Détection des Doublons

Module : control-plane Slice : Slice 8a from architecture (split from original CP-008) Brand context : Papillon HR Suite (UX d'inscription chaleureuse et guidée) Priority : 8 Depends on : CP-001, CP-002, CP-003, CP-004, CP-005, CP-006, CP-007 Estimated complexity : M


Objectif

Construire le formulaire d'auto-inscription (Étape 1) avec la collecte des informations d'entreprise, la détection des doublons (blocage par correspondance exacte email/téléphone, signalement de nom d'entreprise similaire), la validation hCaptcha et la limitation du débit. C'est le point d'entrée du parcours d'onboarding. Crée un tenant avec le statut PENDING_VERIFICATION et envoie un email de vérification. La vérification d'email, le flux d'essai/paiement, la sélection des modules et le provisionnement sont dans les stories suivantes (CP-022, CP-023, CP-024).


Périmètre Backend

Entités & Records

DTOs :

public record SelfRegistrationRequest(
    @NotBlank @Size(min=2, max=200) String companyName,
    @NotBlank @Size(min=2, max=2) String countryCode,  // "CI" only for MVP
    @NotBlank String employeeCountRange,  // "2-12", "13-29", etc.
    @NotBlank @Size(min=2, max=200) String adminFullName,  // Split at first space → User.first_name + User.last_name
    @NotBlank @Email @Size(max=254) String adminEmail,
    @NotBlank String adminPhone,  // CI format: 10 digits
    @Nullable @Size(max=50) String rccm,
    @NotEmpty List<String> selectedModuleCodes,
    boolean freeTrial,
    @NotBlank String hCaptchaToken
) {}

public record RegistrationResponse(
    UUID tenantId, String message
) {}

Couche Service

OnboardingService.java : - register(SelfRegistrationRequest) → RegistrationResponse : 1. Valider le token hCaptcha (appel API externe) 2. Vérifier la limite de débit (3 par IP par heure) — compteurs stockés dans le cache local Caffeine (instance unique MVP) ou Redis (production multi-instance). Clé : rate_limit:register:{ip}, TTL : 1 heure, valeur : nombre de requêtes. 3. Vérifier les doublons email/téléphone (correspondance exacte → BLOQUER) 4. Vérifier le nom d'entreprise similaire (Levenshtein < 3 après normalisation → SIGNALER pour les ops, ne pas bloquer) 5. Créer le Tenant avec status=PENDING_VERIFICATION 6. Créer les TenantSettings avec les valeurs par défaut 7. Stocker les modules sélectionnés (pas encore activés) 8. Envoyer l'email de vérification (FR-CP-002) 9. Retourner tenantId

DuplicateDetectionService.java : - checkDuplicates(String email, String phone, String companyName) → DuplicateResult - Correspondance exacte email → BLOQUER - Correspondance exacte téléphone → BLOQUER - Nom d'entreprise similaire (normaliser : minuscules, supprimer les suffixes légaux "SARL"/"SA"/"SAS"/"SUARL", trim, distance de Levenshtein < 3) → SIGNALER

Endpoints API

Méthode Chemin Corps de requête Réponse Auth
POST /api/v1/onboarding/register SelfRegistrationRequest RegistrationResponse Public (hCaptcha)

Règles de validation

  • companyName : min 2, max 200
  • countryCode : doit être "CI" (MVP)
  • employeeCountRange : doit être l'une des valeurs "2-12", "13-29", "30-49", "50-99", "100-199", "200-350"
  • adminEmail : email valide, unique globalement
  • adminPhone : format CI (10 chiffres), unique globalement
  • selectedModuleCodes : au moins 1, tous doivent être des codes de modules vendables valides
  • hCaptchaToken : validé contre l'API hCaptcha
  • Limite de débit : 3 inscriptions par IP par heure

Considérations multi-tenant

  • L'inscription est une opération PRÉ-tenant — aucun TenantContext n'existe encore
  • Ce slice ne crée que la ligne Tenant avec le statut PENDING_VERIFICATION ; le provisionnement (CP-024) crée le contexte tenant réel

Périmètre Frontend

Pages & Routes

  • /inscription — Formulaire d'inscription (Étape 1 uniquement dans ce slice)

Composants

  • RegistrationStep1Page.tsx — Formulaire d'informations d'entreprise (nom d'entreprise, pays, tranche d'effectif, nom de l'admin, email, téléphone, RCCM, case à cocher essai gratuit)
  • StepIndicator.tsx — Spec composant dans design §19.1 (affiche 1/3 actif)

États UI (TOUS REQUIS)

Inscription Étape 1 :

État Comportement Texte en Français
Loading N/A (formulaire statique)
Empty Tous les champs vides sauf pays (pré-rempli CI) Titre : "Créez votre compte Papillon en 5 minutes"
Erreur de validation Bordure rouge + erreur sous le champ, secousse du champ (150ms) Messages d'erreur par champ (voir ci-dessous)
Doublon Message d'erreur avec lien de connexion "Un compte existe déjà avec cet e-mail." / "Un compte existe déjà avec ce numéro."
Envoi en cours Bouton : spinner + "Envoi en cours…", tous les champs désactivés "Envoi en cours…"
Erreur serveur Toast, champs restent remplis "Une erreur est survenue. Veuillez réessayer."
Limite de débit Toast "Trop de tentatives. Veuillez réessayer dans quelques minutes."
Succès Redirection vers Étape 2 ou vérification d'email

Comportement responsive

Inscription Étape 1 : - 360px (mobile) : Carte colonne unique sur fond chaud (#FFFDF7). Indicateur d'étape en haut. Champs empilés verticalement. - 1024px+ (bureau) : Mise en page divisée — panneau gauche (50%) : bleu sarcelle profond #0D6B6E avec SVG géométrique, accroche "Simplifiez la gestion de vos RH", badges de confiance. Panneau droit (50%) : carte blanche avec formulaire.

Interactions

  • Soumission Étape 1 : "Continuer →" valide tous les champs, déclenche hCaptcha, soumet au backend. En cas de succès, envoie l'email de vérification et affiche un message de confirmation.
  • Format automatique du téléphone : Pendant la saisie, formatage en XX XX XX XX XX. Indicatif pays +225 pré-rempli et non modifiable.
  • Unicité email/téléphone : Vérifiée à la soumission (côté serveur). Si doublon → message d'erreur avec lien de connexion.
  • hCaptcha : Challenge invisible en premier ; bascule vers challenge visible si suspect.

Micro-textes français (inscription) :

Clé Texte Français
registration.title "Créez votre compte Papillon en 5 minutes"
registration.subtitle "Simplifiez la gestion de vos ressources humaines"
registration.company_name.label "Nom de l'entreprise"
registration.company_name.placeholder "Ex : Acme SARL"
registration.country.label "Pays"
registration.employee_count.label "Nombre de salariés"
registration.employee_count.placeholder "Sélectionnez une tranche"
registration.admin_name.label "Votre nom complet"
registration.admin_name.placeholder "Ex : Moussa Koné"
registration.email.label "Adresse e-mail"
registration.email.placeholder "[email protected]"
registration.phone.label "Numéro de téléphone"
registration.phone.placeholder "07 12 34 56 78"
registration.rccm.label "RCCM (optionnel)"
registration.rccm.helper "Vous pourrez le fournir plus tard"
registration.trial.label "Démarrer un essai gratuit de 14 jours"
registration.submit "Continuer →"
registration.already_registered "Déjà inscrit ?"
registration.login_link "Se connecter"
registration.duplicate_email "Un compte existe déjà avec cet e-mail."
registration.duplicate_phone "Un compte existe déjà avec ce numéro."
registration.rate_limited "Trop de tentatives. Veuillez réessayer dans quelques minutes."
registration.server_error "Une erreur est survenue. Veuillez réessayer."
verification.email_sent "Un e-mail de vérification a été envoyé à {email}"
verification.check_inbox "Vérifiez votre boîte de réception (et les spams)"

Critères d'acceptation

AC-061: Successful self-registration
Given I am on the registration page
When I fill all required fields, pass hCaptcha, and submit
Then a tenant is created with status PENDING_VERIFICATION
And I receive a verification email within 60 seconds

AC-064: Duplicate email blocks registration
Given email "[email protected]" is already registered
When I try to register with that email
Then I see "Un compte existe déjà avec cet e-mail."

AC-065: Fuzzy company name flags for ops
Given a tenant "Acme SARL" already exists
When I register "ACME Sarl" (Levenshtein distance < 3 after normalization)
Then registration proceeds (NOT blocked)
And an alert is created for ALTARYS ops in admin console

AC-066: Rate limiting
Given 3 registrations have been attempted from my IP in the last hour
When I try to register again
Then I see "Trop de tentatives. Veuillez réessayer dans quelques minutes."

Règles OHADA & Réglementaires

  • Loi 2013-450 : L'inscription collecte des données personnelles (nom, email, téléphone). Le consentement est implicite par la soumission du formulaire. Les CGV doivent inclure une clause de traitement des données (légal/opérationnel, pas du code).
  • Optionnalité du RCCM : Le RCCM n'est pas obligatoire à l'inscription (de nombreuses petites entreprises ne l'ont pas encore). Il peut être fourni ultérieurement. Mais il est nécessaire pour une facturation conforme FNE (voir CP-009).
  • hCaptcha : Alternative axée sur la confidentialité à Google reCAPTCHA. Important pour la conformité CI en matière de protection des données.

Standards & Conventions

  • docs/standards/java-spring-guidelines.md — §External API calls (hCaptcha), §Application Events
  • docs/standards/api-guidelines.md — §Public endpoints, §Rate limiting, §Error responses
  • docs/standards/react-typescript-guidelines.md — §Multi-step forms, §StepIndicator, §Public pages
  • docs/standards/offline-sync-guidelines.md — N/A (inscription toujours en ligne)

Exigences de test

Tests unitaires

  • DuplicateDetectionService : correspondance exacte email, correspondance exacte téléphone, correspondance de nom similaire
  • DuplicateDetectionService : normalisation (supprimer SARL/SA/SAS, minuscules, trim)
  • OnboardingService.register : validation hCaptcha, vérification limite de débit, vérification des doublons, création du tenant
  • Validation : toutes les contraintes de champs (companyName min/max, countryCode "CI" uniquement, format téléphone, format email)

Tests d'intégration

  • Flux d'inscription complet : POST /api/v1/onboarding/register → tenant créé avec PENDING_VERIFICATION
  • Détection des doublons : email, téléphone, nom similaire
  • Limite de débit : 4ème tentative en 1 heure → 429
  • Validation du token hCaptcha (API externe mockée)

Ce que le QA validera

  • Formulaire d'inscription : tous les champs, messages de validation, format automatique du téléphone
  • L'indicateur d'étape affiche la progression (1/3 actif)
  • Mise en page divisée bureau (branding à gauche, formulaire à droite)
  • Mise en page colonne unique mobile
  • hCaptcha apparaît à la soumission
  • Badges de confiance visibles sur bureau ("Conforme OHADA", "Donnees securisees", "Support reactif")
  • Email/téléphone en doublon affiche les messages d'erreur corrects avec lien de connexion

Hors périmètre

  • Vérification d'email + flux d'essai/paiement — CP-022
  • Sélection des modules + UI de détection de pack — CP-023
  • Pipeline de provisionnement — CP-024
  • Flux de paiement (Étape 3 sans essai gratuit) — CP-009
  • Onboarding Canal B (CRM) — CP-013
  • Onboarding Canal C (back-office admin) — V2
  • Flux de création de mot de passe — Keycloak s'en charge
  • Tableau de bord post-inscription (célébration de bienvenue) — fonctionnalité du host shell

Définition de Done

  • [ ] Tous les endpoints backend implémentés et retournant les réponses correctes
  • [ ] Toutes les pages frontend s'affichent correctement à 360px, 768px, 1024px
  • [ ] Les 5 états UI implémentés (loading, empty, error, offline, success)
  • [ ] Les micro-textes français correspondent exactement à la spec design
  • [ ] Tous les critères d'acceptation passent manuellement
  • [ ] Tests unitaires écrits et passants
  • [ ] Tests d'intégration écrits et passants
  • [ ] Isolation multi-tenant vérifiée (les données du nouveau tenant sont isolées)
  • [ ] Écriture hors ligne + sync fonctionnelle (Papillon) — N/A (inscription toujours en ligne)
  • [ ] Pas de any TypeScript — tous les types explicites
  • [ ] Pas de types Java bruts — tous les génériques explicites
  • [ ] Les réponses API respectent le format d'enveloppe standard
  • [ ] Entrées d'audit créées pour chaque mutation de données