feat(onboarding): UI/UX polish + mapping typeOrg + gestion erreur paiement Wave

Plan selection :
- Grille 2×2 compacte pour les plages (au lieu de liste verticale)
- Badge  POPULAIRE sur STANDARD
- Remise annuelle affichée (−X%/an)
- AnimatedSwitcher + auto-scroll vers formules quand plage sélectionnée
- Dark mode adaptatif complet

Récapitulatif :
- Dark mode complet (AppColors pairs)
- Bloc Total gradient gold adaptatif
- NoteBox avec backgrounds accent.withOpacity()
- Utilise OnboardingBottomBar (consistence)

Payment method :
- Dark mode + couleurs de marque Wave/Orange hardcodées (intentionnel)
- Logo container reste blanc (brand)
- Mapping typeOrganisation détaillé → enum backend ASSOCIATION/MUTUELLE/
  COOPERATIVE/FEDERATION (fix HTTP 400 'Valeur invalide pour typeOrganisation')

Wave payment :
- Dark mode adaptatif
- Message dev clair (simulation automatique)
- Gestion OnboardingPaiementEchoue : SnackBar rouge + reset flags + reste sur page
  (plus de faux succès quand confirmerPaiement() return false ou lève exception)

Bloc : nouvel état OnboardingPaiementEchoue, _onRetourDepuisWave vérifie le return
de confirmerPaiement() (plus de catch silencieux qui émettait OnboardingPaiementConfirme)

Shared widgets : OnboardingSectionTitle + OnboardingBottomBar dark mode + hint optionnel
This commit is contained in:
dahoud
2026-04-15 20:14:27 +00:00
parent 36a903c80e
commit 21b519de53
8 changed files with 1081 additions and 859 deletions

View File

@@ -141,6 +141,20 @@ class OnboardingStepPaiement extends OnboardingState {
/// Paiement confirmé — déclenche un re-check du statut du compte
class OnboardingPaiementConfirme extends OnboardingState {}
/// Erreur de confirmation paiement — l'utilisateur peut réessayer
class OnboardingPaiementEchoue extends OnboardingState {
final String message;
final SouscriptionStatusModel souscription;
final String waveLaunchUrl;
const OnboardingPaiementEchoue({
required this.message,
required this.souscription,
required this.waveLaunchUrl,
});
@override
List<Object?> get props => [message, souscription, waveLaunchUrl];
}
/// Étape 5 : en attente de validation SuperAdmin
class OnboardingStepAttente extends OnboardingState {
final SouscriptionStatusModel? souscription;
@@ -316,16 +330,32 @@ class OnboardingBloc extends Bloc<OnboardingEvent, OnboardingState> {
Future<void> _onRetourDepuisWave(
OnboardingRetourDepuisWave event, Emitter<OnboardingState> emit) async {
emit(OnboardingLoading());
final souscId = _souscription?.souscriptionId;
final waveUrl = _souscription?.waveLaunchUrl;
if (souscId == null) {
emit(const OnboardingError('Souscription introuvable.'));
return;
}
try {
final souscId = _souscription?.souscriptionId;
if (souscId != null) {
await _datasource.confirmerPaiement(souscId);
final success = await _datasource.confirmerPaiement(souscId);
if (success) {
emit(OnboardingPaiementConfirme());
} else {
// La confirmation a échoué côté backend — l'utilisateur doit réessayer
emit(OnboardingPaiementEchoue(
message: 'Le paiement n\'a pas pu être confirmé. Vérifiez que le paiement Wave a bien été effectué et réessayez.',
souscription: _souscription!,
waveLaunchUrl: waveUrl ?? '',
));
}
// Émettre OnboardingPaiementConfirme pour déclencher re-check du compte
// Si le backend auto-active le compte, AuthStatusChecked redirigera vers dashboard
emit(OnboardingPaiementConfirme());
} catch (e) {
emit(OnboardingPaiementConfirme());
emit(OnboardingPaiementEchoue(
message: 'Erreur lors de la confirmation: ${e.toString().replaceFirst("Exception: ", "")}',
souscription: _souscription!,
waveLaunchUrl: waveUrl ?? '',
));
}
}
}