Files
unionflow-mobile-apps/lib/features/members/bloc/membres_state.dart
dahoud 36a903c80e feat(members): swipe par rôle + suppression SuperAdmin avec cascade UX
Swipe actions différenciées par rôle :
- SuperAdmin : → Reset MDP, ← Affecter Org
- OrgAdmin  : → Reset MDP, ← lifecycle selon statut (Suspendre/Activer/Réactiver)
  (masqué si cible = ORGADMIN/SUPERADMIN — cohérent avec guard backend)
- Autres rôles : → Reset MDP seulement

Suppression compte (SuperAdmin uniquement) :
- Nouveau callback onDeleteAccount dans MembersPage + MemberDetailPage
- Bouton rouge 'Supprimer ce compte' dans action sheet (zone destructive)
- Dialog de confirmation adaptatif dark/light avec badge admin si cible ORGADMIN
- Bouton caché si compte déjà désactivé (actif=false)
- Bannière 'Compte désactivé' visible sur page détail d'un compte soft-deleted
- BlocListener MembreDeleted : SnackBar + maybePop() + reload liste
- Bloc gère 409 Conflict (mono-admin) → MembresActionForbidden avec message backend

Nouvelles signatures :
- onLifecycleAction : (memberId, organisationId, action, motif) — inclut orgId
  pour permettre au SuperAdmin d'agir via l'org du membre lui-même
- 'actif' et 'roleCode' exposés dans la map via _convertMembreToMap
2026-04-15 20:14:08 +00:00

271 lines
6.4 KiB
Dart

/// États pour le BLoC des membres
library membres_state;
import 'package:equatable/equatable.dart';
import '../data/models/membre_complete_model.dart';
/// Classe de base pour tous les états des membres
abstract class MembresState extends Equatable {
const MembresState();
@override
List<Object?> get props => [];
}
/// État initial
class MembresInitial extends MembresState {
const MembresInitial();
}
/// État de chargement
class MembresLoading extends MembresState {
const MembresLoading();
}
/// État de chargement avec données existantes (pour refresh)
class MembresRefreshing extends MembresState {
final List<MembreCompletModel> currentMembres;
const MembresRefreshing(this.currentMembres);
@override
List<Object?> get props => [currentMembres];
}
/// État de succès avec liste de membres
class MembresLoaded extends MembresState {
final List<MembreCompletModel> membres;
final int totalElements;
final int currentPage;
final int pageSize;
final int totalPages;
final bool hasMore;
final String? organisationId;
const MembresLoaded({
required this.membres,
required this.totalElements,
this.currentPage = 0,
this.pageSize = 20,
required this.totalPages,
this.organisationId,
}) : hasMore = currentPage < totalPages - 1;
@override
List<Object?> get props => [
membres,
totalElements,
currentPage,
pageSize,
totalPages,
hasMore,
organisationId,
];
MembresLoaded copyWith({
List<MembreCompletModel>? membres,
int? totalElements,
int? currentPage,
int? pageSize,
int? totalPages,
String? organisationId,
}) {
return MembresLoaded(
membres: membres ?? this.membres,
totalElements: totalElements ?? this.totalElements,
currentPage: currentPage ?? this.currentPage,
pageSize: pageSize ?? this.pageSize,
totalPages: totalPages ?? this.totalPages,
organisationId: organisationId ?? this.organisationId,
);
}
}
/// État de succès avec un seul membre
class MembreDetailLoaded extends MembresState {
final MembreCompletModel membre;
const MembreDetailLoaded(this.membre);
@override
List<Object?> get props => [membre];
}
/// État de succès après création
class MembreCreated extends MembresState {
final MembreCompletModel membre;
const MembreCreated(this.membre);
@override
List<Object?> get props => [membre];
}
/// État de succès après mise à jour
class MembreUpdated extends MembresState {
final MembreCompletModel membre;
const MembreUpdated(this.membre);
@override
List<Object?> get props => [membre];
}
/// État de succès après suppression
class MembreDeleted extends MembresState {
final String id;
const MembreDeleted(this.id);
@override
List<Object?> get props => [id];
}
/// État de succès après activation
class MembreActivated extends MembresState {
final MembreCompletModel membre;
const MembreActivated(this.membre);
@override
List<Object?> get props => [membre];
}
/// État de succès après désactivation
class MembreDeactivated extends MembresState {
final MembreCompletModel membre;
const MembreDeactivated(this.membre);
@override
List<Object?> get props => [membre];
}
/// État de succès après affectation à une organisation
class MembreAffecte extends MembresState {
final MembreCompletModel membre;
const MembreAffecte(this.membre);
@override
List<Object?> get props => [membre];
}
/// État de succès après réinitialisation du mot de passe
/// [membre] contient motDePasseTemporaire renseigné (retourné une seule fois)
class MotDePasseReinitialise extends MembresState {
final MembreCompletModel membre;
const MotDePasseReinitialise(this.membre);
@override
List<Object?> get props => [membre];
}
// ── États cycle de vie adhésion ──────────────────────────────────────────
/// Invitation envoyée avec succès
class MembreInvite extends MembresState {
final String membreId;
final String organisationId;
final String nouveauStatut;
const MembreInvite({
required this.membreId,
required this.organisationId,
required this.nouveauStatut,
});
@override
List<Object?> get props => [membreId, organisationId, nouveauStatut];
}
/// Adhésion activée
class AdhesionActivee extends MembresState {
final String membreId;
final String nouveauStatut;
const AdhesionActivee({required this.membreId, required this.nouveauStatut});
@override
List<Object?> get props => [membreId, nouveauStatut];
}
/// Adhésion suspendue
class AdhesionSuspendue extends MembresState {
final String membreId;
final String nouveauStatut;
const AdhesionSuspendue({required this.membreId, required this.nouveauStatut});
@override
List<Object?> get props => [membreId, nouveauStatut];
}
/// Membre radié
class MembreRadie extends MembresState {
final String membreId;
final String nouveauStatut;
const MembreRadie({required this.membreId, required this.nouveauStatut});
@override
List<Object?> get props => [membreId, nouveauStatut];
}
/// État avec statistiques
class MembresStatsLoaded extends MembresState {
final Map<String, dynamic> stats;
const MembresStatsLoaded(this.stats);
@override
List<Object?> get props => [stats];
}
/// État d'erreur
class MembresError extends MembresState {
final String message;
final String? code;
final dynamic error;
const MembresError({
required this.message,
this.code,
this.error,
});
@override
List<Object?> get props => [message, code, error];
}
/// État d'erreur réseau
class MembresNetworkError extends MembresError {
const MembresNetworkError({
required super.message,
super.code,
super.error,
});
}
/// Accès refusé (HTTP 403) sur une action lifecycle (radier, suspendre, archiver).
/// Distinct de MembresNetworkError pour permettre un message utilisateur ciblé.
class MembresActionForbidden extends MembresError {
const MembresActionForbidden({required super.message, super.code = '403', super.error});
}
/// État d'erreur de validation
class MembresValidationError extends MembresError {
final Map<String, String> validationErrors;
const MembresValidationError({
required super.message,
required this.validationErrors,
super.code,
});
@override
List<Object?> get props => [message, code, validationErrors];
}