Aller au contenu

Story CP-023: Module Selection + Pack Detection UI

Module: control-plane Slice: Slice 8c from architecture (split from CP-008) Brand context: Papillon HR Suite (warm, guided registration UX) Priority: 8 (after CP-008) Depends on: CP-008 Estimated complexity: M


Objective

Build the module selection step (Step 2) of the self-registration flow. Users pick which modules they want, the UI auto-detects pack combinations for discounts, a sticky price summary bar updates in real-time, and at least one module must be selected to proceed. This slice reuses the ModuleCard component from CP-021 and the module catalog data structure from CP-020.


Backend Scope

No new backend endpoints in this slice. Module selection is stored as part of the SelfRegistrationRequest (selectedModuleCodes) already submitted in CP-008. This slice is frontend-only for the selection UX — the selected modules are passed forward to the registration request or stored client-side until the onboarding flow completes.

Pack Detection Logic (client-side)

Packs are predefined combinations of modules with a discount. Pack detection runs client-side for UX preview as modules are selected. The server-side PricingEngine (CP-009) is the authoritative source for billing — both use the same pack definitions.

Pack Name Modules Discount
Pack Contrôle ABSMGT + QRCONTR ~12-30% depending on segment
Pack Finance EXPMGT + COMMIT ~5-15%
Pack Essentiel HRCORE + ABSMGT ~17-20%
Pack Standard HRCORE + ABSMGT + QRCONTR Incremental discount
Pack Complet HRCORE + ABSMGT + QRCONTR + EXPMGT + PERFOB Highest discount

Pack detection algorithm: 1. On every module selection/deselection, check if current selection is a superset of any pack 2. If multiple packs match, show the one with the highest discount 3. Show animated price transition in summary bar when pack is detected/lost

Module Metadata (hardcoded — same as CP-021)

Each sellable module has: name, icon, one-line description (French), feature bullet list (French), starting price per employee count segment.

Sellable module codes: HRCORE, EMPMGT, ABSMGT, QRCONTR, TIMACT, EXPMGT, COMMIT, PAYROL, HSEMGT, PERFOB, GPEC, LTRAIN, ATSMGT, ADVDOC, CRMMGT


Frontend Scope

Pages & Routes

  • /inscription/modules — Module selection (Step 2 of registration)

Components

  • RegistrationStep2Page.tsx — Module selection grid with pack detection
  • ModuleCard.tsx — Reuse from CP-021 (available, selected states only — no locked/coming soon in registration)
  • PriceSummaryBar.tsx — Sticky bottom bar showing module count, total price, pack discount if detected. Component spec in design §19.6.
  • StepIndicator.tsx — Reuse from CP-008 (shows 2/3 active)

UI States (ALL REQUIRED)

Registration Step 2 (Module Selection):

State Behavior French Copy
Loading Skeleton: 6 module cards
Empty selection "Continuer" disabled, summary shows "0 modules" "Selectionnez au moins 1 module. Vous pourrez en ajouter plus tard."
Modules selected Summary bar shows count + price + pack if detected "{count} module(s) . {price} FCFA/mois"
Pack detected Animated price change in summary "Pack {packName} detecte . -{discount}%"
Error Toast "Une erreur est survenue."

PriceSummaryBar States:

State Behavior French Copy
No selection Disabled continue button, zero count "0 modules . 0 FCFA/mois"
Selection without pack Active continue button, sum of individual prices "{count} module(s) . {price} FCFA/mois"
Selection with pack Active continue button, strikethrough original + discounted price "Pack {packName} detecte . -{discount}% . {discountedPrice} FCFA/mois"

Responsive Behavior

  • 360px (mobile): Stacked module cards (1 column). Sticky bottom summary bar.
  • 768px (tablet): 2-column card grid.
  • 1024px+ (desktop): 3-column card grid. Summary bar at bottom of card area.

Interactions

  • Module card tap/click: Toggles selection. Card transitions between Available/Selected states with 200ms animation.
  • Pack auto-detection: As modules are selected, pack detection runs client-side. If a pack is detected, the PriceSummaryBar animates the price change (strikethrough old price → show discounted price).
  • Pack lost: If a module is deselected and the pack is broken, the discount disappears with reverse animation.
  • "Continuer →" button: Disabled until at least 1 module is selected. On click, saves selection to registration state and proceeds to email verification (if free trial) or payment choice.

French micro-copy:

Key French Text
modules.step_title "Choisissez vos modules"
modules.step_subtitle "Selectionnez au moins 1 module. Vous pourrez en ajouter plus tard."
modules.count "{count} module(s)"
modules.price "{price} FCFA/mois"
modules.pack_detected "Pack {packName} detecte . -{discount}%"
modules.continue "Continuer →"
modules.back "← Retour"

Acceptance Criteria

AC-071: Module selection requires at least 1
Given I am on the module selection step
When I try to continue without selecting any module
Then the "Continuer" button is disabled

AC-072: Pack auto-detection
Given I select Absences + Presence QR on the module selection step
When both are selected
Then the summary shows "Pack Controle detecte . -12%"
And the price updates from individual sum to pack price

AC-073: Onboarding completes in under 5 minutes
Given I start the registration form
When I fill all fields, verify email, and reach the Dashboard
Then the total elapsed time is under 5 minutes (functional, not a strict test — UX validates)

OHADA & Regulatory Rules

  • No direct OHADA regulatory requirements for module selection UI.
  • Module prices are displayed in XOF with zero decimal places (e.g., "15 000 FCFA/mois").

Standards & Conventions

  • docs/standards/react-typescript-guidelines.md — §Multi-step forms, §Component composition, §Animation patterns
  • docs/design/control-plane-design.md — §19.2 ModuleCard, §19.6 PriceSummaryBar

Testing Requirements

Unit Tests

  • Pack detection: all 5 packs detected when modules match
  • Pack detection: highest discount wins when multiple packs match
  • Pack detection: pack lost when required module deselected
  • PriceSummaryBar: renders correct count and price
  • PriceSummaryBar: shows strikethrough + discount when pack detected
  • ModuleCard: toggle selection state on click
  • Continue button disabled when 0 modules selected

Integration Tests

  • Full Step 2 flow: select modules → pack detected → continue
  • Module card rendering from catalog data
  • Price calculation with and without pack discount

What QA Will Validate

  • Module selection: card selection toggles correctly
  • Pack detection: price summary animates on pack detection/loss
  • "Continuer" button disabled until at least 1 module selected
  • All 3 responsive breakpoints (360px, 768px, 1024px)
  • Step indicator shows 2/3
  • Module descriptions and prices displayed in French

Out of Scope

  • Registration form (Step 1) — CP-008
  • Email verification — CP-022
  • Provisioning pipeline — CP-024
  • Payment flow (Step 3) — CP-009
  • Locked/coming soon module states — CP-021 (only available/selected used during registration)

Definition of Done

  • [ ] All backend endpoints implemented — N/A (frontend-only)
  • [ ] All frontend pages render correctly at 360px, 768px, 1024px
  • [ ] All 5 UI states implemented (loading, empty, error, offline, success)
  • [ ] French micro-copy matches design spec exactly
  • [ ] All acceptance criteria pass manually
  • [ ] Unit tests written and passing
  • [ ] Integration tests written and passing
  • [ ] Multi-tenant isolation verified — N/A (pre-tenant operation)
  • [ ] Offline write+sync works — N/A (registration always online)
  • [ ] No TypeScript any — all types explicit
  • [ ] No Java raw types — N/A (frontend-only)
  • [ ] API responses follow standard envelope format — N/A (frontend-only)
  • [ ] Audit trail entries created for every data mutation — N/A (no mutations)