fix(mobile): URL changement mdp corrigée + v3.0 — multi-org, AppAuth, sécurité prod

Auth:
- profile_repository.dart: /api/auth/change-password → /api/membres/auth/change-password

Multi-org (Phase 3):
- OrgSelectorPage, OrgSwitcherBloc, OrgSwitcherEntry
- org_context_service.dart: headers X-Active-Organisation-Id + X-Active-Role

Navigation:
- MorePage: navigation conditionnelle par typeOrganisation
- Suppression adaptive_navigation (remplacé par main_navigation_layout)

Auth AppAuth:
- keycloak_webview_auth_service: fixes AppAuth Android
- AuthBloc: gestion REAUTH_REQUIS + premierLoginComplet

Onboarding:
- Nouveaux états: payment_method_page, onboarding_shared_widgets
- SouscriptionStatusModel mis à jour StatutValidationSouscription

Android:
- build.gradle: ProGuard/R8, network_security_config
- Gradle wrapper mis à jour
This commit is contained in:
dahoud
2026-04-07 20:56:03 +00:00
parent 22f9c7e9a1
commit 70cbd1c873
63 changed files with 9316 additions and 6122 deletions

View File

@@ -18,9 +18,16 @@ abstract class OnboardingEvent extends Equatable {
class OnboardingStarted extends OnboardingEvent {
final String? existingSouscriptionId;
final String initialState; // NO_SUBSCRIPTION | AWAITING_PAYMENT | PAYMENT_INITIATED | AWAITING_VALIDATION
const OnboardingStarted({required this.initialState, this.existingSouscriptionId});
final String? typeOrganisation;
final String? organisationId;
const OnboardingStarted({
required this.initialState,
this.existingSouscriptionId,
this.typeOrganisation,
this.organisationId,
});
@override
List<Object?> get props => [initialState, existingSouscriptionId];
List<Object?> get props => [initialState, existingSouscriptionId, typeOrganisation, organisationId];
}
/// L'utilisateur a sélectionné une formule et une plage
@@ -51,6 +58,11 @@ class OnboardingDemandeConfirmee extends OnboardingEvent {
const OnboardingDemandeConfirmee();
}
/// Ouvre l'écran de choix du moyen de paiement (depuis le récapitulatif)
class OnboardingChoixPaiementOuvert extends OnboardingEvent {
const OnboardingChoixPaiementOuvert();
}
/// Initie le paiement Wave
class OnboardingPaiementInitie extends OnboardingEvent {
const OnboardingPaiementInitie();
@@ -109,6 +121,14 @@ class OnboardingStepSummary extends OnboardingState {
List<Object?> get props => [souscription];
}
/// Étape 3b : choix du moyen de paiement (Wave, Orange Money, etc.)
class OnboardingStepChoixPaiement extends OnboardingState {
final SouscriptionStatusModel souscription;
const OnboardingStepChoixPaiement(this.souscription);
@override
List<Object?> get props => [souscription];
}
/// Étape 4 : paiement Wave — URL à ouvrir dans le navigateur
class OnboardingStepPaiement extends OnboardingState {
final SouscriptionStatusModel souscription;
@@ -118,6 +138,9 @@ class OnboardingStepPaiement extends OnboardingState {
List<Object?> get props => [souscription, waveLaunchUrl];
}
/// Paiement confirmé — déclenche un re-check du statut du compte
class OnboardingPaiementConfirme extends OnboardingState {}
/// Étape 5 : en attente de validation SuperAdmin
class OnboardingStepAttente extends OnboardingState {
final SouscriptionStatusModel? souscription;
@@ -145,7 +168,7 @@ class OnboardingBloc extends Bloc<OnboardingEvent, OnboardingState> {
String? _codeFormule;
String? _plage;
String? _typePeriode;
String? _typeOrganisation;
String _typeOrganisation = '';
String? _organisationId;
SouscriptionStatusModel? _souscription;
@@ -154,12 +177,19 @@ class OnboardingBloc extends Bloc<OnboardingEvent, OnboardingState> {
on<OnboardingFormuleSelected>(_onFormuleSelected);
on<OnboardingPeriodeSelected>(_onPeriodeSelected);
on<OnboardingDemandeConfirmee>(_onDemandeConfirmee);
on<OnboardingChoixPaiementOuvert>(_onChoixPaiementOuvert);
on<OnboardingPaiementInitie>(_onPaiementInitie);
on<OnboardingRetourDepuisWave>(_onRetourDepuisWave);
}
Future<void> _onStarted(OnboardingStarted event, Emitter<OnboardingState> emit) async {
emit(OnboardingLoading());
if (event.typeOrganisation != null && event.typeOrganisation!.isNotEmpty) {
_typeOrganisation = event.typeOrganisation!;
}
if (event.organisationId != null && event.organisationId!.isNotEmpty) {
_organisationId = event.organisationId;
}
try {
_formules = await _datasource.getFormules();
@@ -189,6 +219,7 @@ 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));
@@ -218,14 +249,23 @@ class OnboardingBloc extends Bloc<OnboardingEvent, OnboardingState> {
void _onPeriodeSelected(OnboardingPeriodeSelected event, Emitter<OnboardingState> emit) {
_typePeriode = event.typePeriode;
_typeOrganisation = event.typeOrganisation;
// typeOrganisation already set from OnboardingStarted; override only if event provides one
if (event.typeOrganisation.isNotEmpty) {
_typeOrganisation = event.typeOrganisation;
}
_organisationId = event.organisationId;
}
void _onChoixPaiementOuvert(OnboardingChoixPaiementOuvert event, Emitter<OnboardingState> emit) {
if (_souscription != null) {
emit(OnboardingStepChoixPaiement(_souscription!));
}
}
Future<void> _onDemandeConfirmee(
OnboardingDemandeConfirmee event, Emitter<OnboardingState> emit) async {
if (_codeFormule == null || _plage == null || _typePeriode == null ||
_typeOrganisation == null || _organisationId == null) {
_organisationId == null) {
emit(const OnboardingError('Données manquantes. Recommencez depuis le début.'));
return;
}
@@ -235,7 +275,7 @@ class OnboardingBloc extends Bloc<OnboardingEvent, OnboardingState> {
typeFormule: _codeFormule!,
plageMembres: _plage!,
typePeriode: _typePeriode!,
typeOrganisation: _typeOrganisation!,
typeOrganisation: _typeOrganisation.isNotEmpty ? _typeOrganisation : null,
organisationId: _organisationId!,
);
if (sosc != null) {
@@ -281,12 +321,11 @@ class OnboardingBloc extends Bloc<OnboardingEvent, OnboardingState> {
if (souscId != null) {
await _datasource.confirmerPaiement(souscId);
}
final sosc = await _datasource.getMaSouscription();
_souscription = sosc;
emit(OnboardingStepAttente(souscription: sosc));
// Émettre OnboardingPaiementConfirme pour déclencher re-check du compte
// Si le backend auto-active le compte, AuthStatusChecked redirigera vers dashboard
emit(OnboardingPaiementConfirme());
} catch (e) {
// En cas d'erreur, on affiche quand même l'écran d'attente
emit(OnboardingStepAttente(souscription: _souscription));
emit(OnboardingPaiementConfirme());
}
}
}