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

@@ -53,6 +53,43 @@ class SouscriptionDatasource {
return null;
}
/// Mappe le type d'organisation détaillé (ex: MUTUELLE_EPARGNE, CLUB_SPORTIF)
/// vers l'une des 4 catégories acceptées par le backend souscription :
/// ASSOCIATION, MUTUELLE, COOPERATIVE, FEDERATION.
static String? _mapTypeOrganisationBilling(String? detailedType) {
if (detailedType == null || detailedType.isEmpty) return null;
switch (detailedType.toUpperCase()) {
// Catégorie ASSOCIATIF + RELIGIEUX
case 'ASSOCIATION':
case 'CLUB_SERVICE':
case 'CLUB_SPORTIF':
case 'CLUB_CULTUREL':
case 'EGLISE':
case 'GROUPE_PRIERE':
return 'ASSOCIATION';
// Catégorie FINANCIER_SOLIDAIRE (épargne/crédit)
case 'TONTINE':
case 'MUTUELLE_EPARGNE':
case 'MUTUELLE_CREDIT':
case 'MUTUELLE':
return 'MUTUELLE';
// Catégorie COOPERATIVE
case 'COOPERATIVE':
case 'GIE':
return 'COOPERATIVE';
// Catégorie PROFESSIONNEL + RESEAU_FEDERATION
case 'ONG':
case 'FONDATION':
case 'SYNDICAT':
case 'ORDRE_PROFESSIONNEL':
case 'FEDERATION':
case 'RESEAU':
return 'FEDERATION';
default:
return 'ASSOCIATION'; // fallback sûr
}
}
/// Crée une demande de souscription
Future<SouscriptionStatusModel?> creerDemande({
required String typeFormule,
@@ -63,14 +100,15 @@ class SouscriptionDatasource {
}) async {
try {
final opts = await _authOptions();
final mappedType = _mapTypeOrganisationBilling(typeOrganisation);
final body = <String, dynamic>{
'typeFormule': typeFormule,
'plageMembres': plageMembres,
'typePeriode': typePeriode,
'organisationId': organisationId,
};
if (typeOrganisation != null && typeOrganisation.isNotEmpty) {
body['typeOrganisation'] = typeOrganisation;
if (mappedType != null && mappedType.isNotEmpty) {
body['typeOrganisation'] = mappedType;
}
final response = await _dio.post(
'$_base/api/souscriptions/demande',