refactoring
This commit is contained in:
292
lib/features/onboarding/bloc/onboarding_bloc.dart
Normal file
292
lib/features/onboarding/bloc/onboarding_bloc.dart
Normal file
@@ -0,0 +1,292 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:injectable/injectable.dart';
|
||||
import '../data/datasources/souscription_datasource.dart';
|
||||
import '../data/models/formule_model.dart';
|
||||
import '../data/models/souscription_status_model.dart';
|
||||
import '../../../../core/utils/logger.dart';
|
||||
|
||||
// ─────────────────────────────────────────────────────────────── Events ──────
|
||||
|
||||
abstract class OnboardingEvent extends Equatable {
|
||||
const OnboardingEvent();
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
/// 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
|
||||
const OnboardingStarted({required this.initialState, this.existingSouscriptionId});
|
||||
@override
|
||||
List<Object?> get props => [initialState, existingSouscriptionId];
|
||||
}
|
||||
|
||||
/// L'utilisateur a sélectionné une formule et une plage
|
||||
class OnboardingFormuleSelected extends OnboardingEvent {
|
||||
final String codeFormule;
|
||||
final String plage;
|
||||
const OnboardingFormuleSelected({required this.codeFormule, required this.plage});
|
||||
@override
|
||||
List<Object?> get props => [codeFormule, plage];
|
||||
}
|
||||
|
||||
/// L'utilisateur a sélectionné la période et le type d'organisation
|
||||
class OnboardingPeriodeSelected extends OnboardingEvent {
|
||||
final String typePeriode;
|
||||
final String typeOrganisation;
|
||||
final String organisationId;
|
||||
const OnboardingPeriodeSelected({
|
||||
required this.typePeriode,
|
||||
required this.typeOrganisation,
|
||||
required this.organisationId,
|
||||
});
|
||||
@override
|
||||
List<Object?> get props => [typePeriode, typeOrganisation, organisationId];
|
||||
}
|
||||
|
||||
/// Confirme la demande et crée la souscription en base
|
||||
class OnboardingDemandeConfirmee extends OnboardingEvent {
|
||||
const OnboardingDemandeConfirmee();
|
||||
}
|
||||
|
||||
/// Initie le paiement Wave
|
||||
class OnboardingPaiementInitie extends OnboardingEvent {
|
||||
const OnboardingPaiementInitie();
|
||||
}
|
||||
|
||||
/// L'utilisateur est revenu dans l'app après le paiement Wave
|
||||
class OnboardingRetourDepuisWave extends OnboardingEvent {
|
||||
const OnboardingRetourDepuisWave();
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────── States ──────
|
||||
|
||||
abstract class OnboardingState extends Equatable {
|
||||
const OnboardingState();
|
||||
@override
|
||||
List<Object?> get props => [];
|
||||
}
|
||||
|
||||
class OnboardingInitial extends OnboardingState {}
|
||||
class OnboardingLoading extends OnboardingState {}
|
||||
|
||||
class OnboardingError extends OnboardingState {
|
||||
final String message;
|
||||
const OnboardingError(this.message);
|
||||
@override
|
||||
List<Object?> get props => [message];
|
||||
}
|
||||
|
||||
/// Étape 1 : choix formule + plage (affiche la grille des formules)
|
||||
class OnboardingStepFormule extends OnboardingState {
|
||||
final List<FormuleModel> formules;
|
||||
const OnboardingStepFormule(this.formules);
|
||||
@override
|
||||
List<Object?> get props => [formules];
|
||||
}
|
||||
|
||||
/// Étape 2 : choix période + type organisation (formule/plage déjà sélectionnés)
|
||||
class OnboardingStepPeriode extends OnboardingState {
|
||||
final String codeFormule;
|
||||
final String plage;
|
||||
final List<FormuleModel> formules;
|
||||
const OnboardingStepPeriode({
|
||||
required this.codeFormule,
|
||||
required this.plage,
|
||||
required this.formules,
|
||||
});
|
||||
@override
|
||||
List<Object?> get props => [codeFormule, plage, formules];
|
||||
}
|
||||
|
||||
/// Étape 3 : récapitulatif avec montant calculé (avant paiement)
|
||||
class OnboardingStepSummary extends OnboardingState {
|
||||
final SouscriptionStatusModel souscription;
|
||||
const OnboardingStepSummary(this.souscription);
|
||||
@override
|
||||
List<Object?> get props => [souscription];
|
||||
}
|
||||
|
||||
/// Étape 4 : paiement Wave — URL à ouvrir dans le navigateur
|
||||
class OnboardingStepPaiement extends OnboardingState {
|
||||
final SouscriptionStatusModel souscription;
|
||||
final String waveLaunchUrl;
|
||||
const OnboardingStepPaiement({required this.souscription, required this.waveLaunchUrl});
|
||||
@override
|
||||
List<Object?> get props => [souscription, waveLaunchUrl];
|
||||
}
|
||||
|
||||
/// Étape 5 : en attente de validation SuperAdmin
|
||||
class OnboardingStepAttente extends OnboardingState {
|
||||
final SouscriptionStatusModel? souscription;
|
||||
const OnboardingStepAttente({this.souscription});
|
||||
@override
|
||||
List<Object?> get props => [souscription];
|
||||
}
|
||||
|
||||
/// Souscription rejetée par le SuperAdmin
|
||||
class OnboardingRejected extends OnboardingState {
|
||||
final String? commentaire;
|
||||
const OnboardingRejected({this.commentaire});
|
||||
@override
|
||||
List<Object?> get props => [commentaire];
|
||||
}
|
||||
|
||||
// ─────────────────────────────────────────────────────────────── BLoC ────────
|
||||
|
||||
@injectable
|
||||
class OnboardingBloc extends Bloc<OnboardingEvent, OnboardingState> {
|
||||
final SouscriptionDatasource _datasource;
|
||||
|
||||
// Données accumulées au fil du wizard
|
||||
List<FormuleModel> _formules = [];
|
||||
String? _codeFormule;
|
||||
String? _plage;
|
||||
String? _typePeriode;
|
||||
String? _typeOrganisation;
|
||||
String? _organisationId;
|
||||
SouscriptionStatusModel? _souscription;
|
||||
|
||||
OnboardingBloc(this._datasource) : super(OnboardingInitial()) {
|
||||
on<OnboardingStarted>(_onStarted);
|
||||
on<OnboardingFormuleSelected>(_onFormuleSelected);
|
||||
on<OnboardingPeriodeSelected>(_onPeriodeSelected);
|
||||
on<OnboardingDemandeConfirmee>(_onDemandeConfirmee);
|
||||
on<OnboardingPaiementInitie>(_onPaiementInitie);
|
||||
on<OnboardingRetourDepuisWave>(_onRetourDepuisWave);
|
||||
}
|
||||
|
||||
Future<void> _onStarted(OnboardingStarted event, Emitter<OnboardingState> emit) async {
|
||||
emit(OnboardingLoading());
|
||||
try {
|
||||
_formules = await _datasource.getFormules();
|
||||
|
||||
switch (event.initialState) {
|
||||
case 'NO_SUBSCRIPTION':
|
||||
emit(OnboardingStepFormule(_formules));
|
||||
|
||||
case 'AWAITING_PAYMENT':
|
||||
final sosc = await _datasource.getMaSouscription();
|
||||
if (sosc != null) {
|
||||
_souscription = sosc;
|
||||
emit(OnboardingStepSummary(sosc));
|
||||
} else {
|
||||
emit(OnboardingStepFormule(_formules));
|
||||
}
|
||||
|
||||
case 'PAYMENT_INITIATED':
|
||||
final sosc = await _datasource.getMaSouscription();
|
||||
if (sosc != null && sosc.waveLaunchUrl != null) {
|
||||
_souscription = sosc;
|
||||
emit(OnboardingStepPaiement(
|
||||
souscription: sosc,
|
||||
waveLaunchUrl: sosc.waveLaunchUrl!,
|
||||
));
|
||||
} else {
|
||||
emit(OnboardingStepAttente(souscription: sosc));
|
||||
}
|
||||
|
||||
case 'AWAITING_VALIDATION':
|
||||
final sosc = await _datasource.getMaSouscription();
|
||||
_souscription = sosc;
|
||||
emit(OnboardingStepAttente(souscription: sosc));
|
||||
|
||||
case 'REJECTED':
|
||||
final sosc = await _datasource.getMaSouscription();
|
||||
emit(OnboardingRejected(commentaire: sosc?.statutValidation));
|
||||
|
||||
default:
|
||||
emit(OnboardingStepFormule(_formules));
|
||||
}
|
||||
} catch (e) {
|
||||
AppLogger.error('OnboardingBloc._onStarted: $e');
|
||||
emit(const OnboardingError('Impossible de charger les formules. Vérifiez votre connexion.'));
|
||||
}
|
||||
}
|
||||
|
||||
void _onFormuleSelected(OnboardingFormuleSelected event, Emitter<OnboardingState> emit) {
|
||||
_codeFormule = event.codeFormule;
|
||||
_plage = event.plage;
|
||||
emit(OnboardingStepPeriode(
|
||||
codeFormule: event.codeFormule,
|
||||
plage: event.plage,
|
||||
formules: _formules,
|
||||
));
|
||||
}
|
||||
|
||||
void _onPeriodeSelected(OnboardingPeriodeSelected event, Emitter<OnboardingState> emit) {
|
||||
_typePeriode = event.typePeriode;
|
||||
_typeOrganisation = event.typeOrganisation;
|
||||
_organisationId = event.organisationId;
|
||||
}
|
||||
|
||||
Future<void> _onDemandeConfirmee(
|
||||
OnboardingDemandeConfirmee event, Emitter<OnboardingState> emit) async {
|
||||
if (_codeFormule == null || _plage == null || _typePeriode == null ||
|
||||
_typeOrganisation == null || _organisationId == null) {
|
||||
emit(const OnboardingError('Données manquantes. Recommencez depuis le début.'));
|
||||
return;
|
||||
}
|
||||
emit(OnboardingLoading());
|
||||
try {
|
||||
final sosc = await _datasource.creerDemande(
|
||||
typeFormule: _codeFormule!,
|
||||
plageMembres: _plage!,
|
||||
typePeriode: _typePeriode!,
|
||||
typeOrganisation: _typeOrganisation!,
|
||||
organisationId: _organisationId!,
|
||||
);
|
||||
if (sosc != null) {
|
||||
_souscription = sosc;
|
||||
emit(OnboardingStepSummary(sosc));
|
||||
} else {
|
||||
emit(const OnboardingError('Erreur lors de la création de la demande.'));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(OnboardingError('Erreur: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onPaiementInitie(
|
||||
OnboardingPaiementInitie event, Emitter<OnboardingState> emit) async {
|
||||
final souscId = _souscription?.souscriptionId;
|
||||
if (souscId == null) {
|
||||
emit(const OnboardingError('Souscription introuvable.'));
|
||||
return;
|
||||
}
|
||||
emit(OnboardingLoading());
|
||||
try {
|
||||
final updated = await _datasource.initierPaiement(souscId);
|
||||
if (updated?.waveLaunchUrl != null) {
|
||||
_souscription = updated;
|
||||
emit(OnboardingStepPaiement(
|
||||
souscription: updated!,
|
||||
waveLaunchUrl: updated.waveLaunchUrl!,
|
||||
));
|
||||
} else {
|
||||
emit(const OnboardingError('Impossible d\'initier le paiement Wave.'));
|
||||
}
|
||||
} catch (e) {
|
||||
emit(OnboardingError('Erreur paiement: $e'));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _onRetourDepuisWave(
|
||||
OnboardingRetourDepuisWave event, Emitter<OnboardingState> emit) async {
|
||||
emit(OnboardingLoading());
|
||||
try {
|
||||
final souscId = _souscription?.souscriptionId;
|
||||
if (souscId != null) {
|
||||
await _datasource.confirmerPaiement(souscId);
|
||||
}
|
||||
final sosc = await _datasource.getMaSouscription();
|
||||
_souscription = sosc;
|
||||
emit(OnboardingStepAttente(souscription: sosc));
|
||||
} catch (e) {
|
||||
// En cas d'erreur, on affiche quand même l'écran d'attente
|
||||
emit(OnboardingStepAttente(souscription: _souscription));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user