feat: auto-activation après paiement Wave — plus de page d'attente
- 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
This commit is contained in:
@@ -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<AuthEvent, AuthState> {
|
||||
}
|
||||
|
||||
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<AuthEvent, AuthState> {
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
@@ -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<Object?> 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<OnboardingEvent, OnboardingState> {
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
Reference in New Issue
Block a user