From 33f5b5a707a4ac0fff390bbcc9dde52fe15efa23 Mon Sep 17 00:00:00 2001 From: dahoud <41957584+DahoudG@users.noreply.github.com> Date: Sat, 18 Apr 2026 08:07:30 +0000 Subject: [PATCH] =?UTF-8?q?feat:=20auto-activation=20apr=C3=A8s=20paiement?= =?UTF-8?q?=20Wave=20=E2=80=94=20plus=20de=20page=20d'attente?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - auth_bloc: quand onboardingState==VALIDATED, refresh token et vérifier si statutCompte==ACTIF → dashboard direct (pas d'écran d'attente) - Edge case: si activation backend échouée, fallback vers AwaitingValidationPage avec polling 15s - onboarding_bloc: séparer VALIDATED de AWAITING_VALIDATION dans le switch --- .../presentation/bloc/auth_bloc.dart | 45 ++++++++++++++++++- .../onboarding/bloc/onboarding_bloc.dart | 19 +++++--- 2 files changed, 57 insertions(+), 7 deletions(-) diff --git a/lib/features/authentication/presentation/bloc/auth_bloc.dart b/lib/features/authentication/presentation/bloc/auth_bloc.dart index 9c4dc65..425d022 100644 --- a/lib/features/authentication/presentation/bloc/auth_bloc.dart +++ b/lib/features/authentication/presentation/bloc/auth_bloc.dart @@ -74,7 +74,7 @@ class AuthAccountNotActive extends AuthState { /// Compte EN_ATTENTE_VALIDATION — l'OrgAdmin doit compléter l'onboarding. /// On ne déconnecte PAS pour permettre les appels API de souscription. class AuthPendingOnboarding extends AuthState { - final String onboardingState; // NO_SUBSCRIPTION | AWAITING_PAYMENT | PAYMENT_INITIATED | AWAITING_VALIDATION + final String onboardingState; // NO_SUBSCRIPTION | AWAITING_PAYMENT | PAYMENT_INITIATED | AWAITING_VALIDATION | VALIDATED final String? souscriptionId; final String? organisationId; final String? typeOrganisation; @@ -145,6 +145,29 @@ class AuthBloc extends Bloc { } if (status != null && status.isPendingOnboarding) { + // Souscription VALIDATED = paiement confirmé, activation imminente. + // Rafraîchir le token : si le backend a déjà activé le membre, + // les rôles MEMBRE/MEMBRE_ACTIF seront dans le nouveau token → dashboard. + if (status.onboardingState == 'VALIDATED') { + await _authService.refreshToken(); + final refreshedStatus = await _authService.getAuthStatus(AppConfig.apiBaseUrl); + if (refreshedStatus != null && refreshedStatus.isActive) { + // Activation effective → procéder au dashboard + final refreshedRawUser = await _authService.getCurrentUser(); + final user = await _enrichUserWithOrgContext(refreshedRawUser ?? rawUser); + final permissions = await PermissionEngine.getEffectivePermissions(user); + final token = await _authService.getValidToken(); + await DashboardCacheManager.invalidateForRole(user.primaryRole); + emit(AuthAuthenticated( + user: user, + effectiveRole: user.primaryRole, + effectivePermissions: permissions, + accessToken: token ?? '', + )); + return; + } + // Activation pas encore effective (edge case) → afficher page d'attente avec polling + } // OrgAdmin en attente → rediriger vers l'onboarding (sans déconnecter) final user = await _enrichUserWithOrgContext(rawUser); final orgId = status.organisationId ?? @@ -244,6 +267,26 @@ class AuthBloc extends Bloc { } if (status != null && status.isPendingOnboarding) { + // Souscription VALIDATED : vérifier si le backend a finalisé l'activation + if (status.onboardingState == 'VALIDATED') { + await _authService.refreshToken(); + final refreshedStatus = await _authService.getAuthStatus(AppConfig.apiBaseUrl); + if (refreshedStatus != null && refreshedStatus.isActive) { + final refreshedRawUser = await _authService.getCurrentUser(); + final userActive = await _enrichUserWithOrgContext(refreshedRawUser ?? rawUser); + final permissions = await PermissionEngine.getEffectivePermissions(userActive); + final token = await _authService.getValidToken(); + await DashboardCacheManager.invalidateForRole(userActive.primaryRole); + emit(AuthAuthenticated( + user: userActive, + effectiveRole: userActive.primaryRole, + effectivePermissions: permissions, + accessToken: token ?? '', + )); + return; + } + // Activation pas encore effective → continuer vers la page d'attente + } final user = await _enrichUserWithOrgContext(rawUser); final orgId = status.organisationId ?? (user.organizationContexts.isNotEmpty diff --git a/lib/features/onboarding/bloc/onboarding_bloc.dart b/lib/features/onboarding/bloc/onboarding_bloc.dart index 949f29c..f42b65e 100644 --- a/lib/features/onboarding/bloc/onboarding_bloc.dart +++ b/lib/features/onboarding/bloc/onboarding_bloc.dart @@ -17,7 +17,7 @@ abstract class OnboardingEvent extends Equatable { /// Démarre le workflow (charge les formules + état courant si souscription existante) class OnboardingStarted extends OnboardingEvent { final String? existingSouscriptionId; - final String initialState; // NO_SUBSCRIPTION | AWAITING_PAYMENT | PAYMENT_INITIATED | AWAITING_VALIDATION + final String initialState; // NO_SUBSCRIPTION | AWAITING_PAYMENT | PAYMENT_INITIATED | AWAITING_VALIDATION | VALIDATED final String? typeOrganisation; final String? organisationId; const OnboardingStarted({ @@ -155,7 +155,7 @@ class OnboardingPaiementEchoue extends OnboardingState { List get props => [message, souscription, waveLaunchUrl]; } -/// Étape 5 : en attente de validation SuperAdmin +/// Étape 5 : en attente d'activation (paiement reçu, activation en cours) class OnboardingStepAttente extends OnboardingState { final SouscriptionStatusModel? souscription; const OnboardingStepAttente({this.souscription}); @@ -233,10 +233,17 @@ class OnboardingBloc extends Bloc { } case 'AWAITING_VALIDATION': - case 'VALIDATED': // Paiement confirmé mais activation compte non encore effective - final sosc = await _datasource.getMaSouscription(); - _souscription = sosc; - emit(OnboardingStepAttente(souscription: sosc)); + final soscAwait = await _datasource.getMaSouscription(); + _souscription = soscAwait; + emit(OnboardingStepAttente(souscription: soscAwait)); + + case 'VALIDATED': + // Paiement confirmé, souscription validée, mais activation pas encore effective + // (edge case : auth_bloc a déjà tenté un refresh sans succès). + // Afficher la page d'attente avec polling 15s (AwaitingValidationPage). + final soscValidated = await _datasource.getMaSouscription(); + _souscription = soscValidated; + emit(OnboardingStepAttente(souscription: soscValidated)); case 'REJECTED': final sosc = await _datasource.getMaSouscription();