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
271 lines
6.4 KiB
Dart
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];
|
|
}
|
|
|