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 200countryCode: 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 globalementadminPhone: format CI (10 chiffres), unique globalementselectedModuleCodes: au moins 1, tous doivent être des codes de modules vendables valideshCaptchaToken: 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 Eventsdocs/standards/api-guidelines.md— §Public endpoints, §Rate limiting, §Error responsesdocs/standards/react-typescript-guidelines.md— §Multi-step forms, §StepIndicator, §Public pagesdocs/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
anyTypeScript — 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