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
This commit is contained in:
@@ -278,6 +278,14 @@ class MembresBloc extends Bloc<MembresEvent, MembresState> {
|
||||
emit(MembreDeleted(event.id));
|
||||
} on DioException catch (e) {
|
||||
if (e.type == DioExceptionType.cancel) return;
|
||||
// 409 Conflict = mono-admin orphelinage détecté par le backend.
|
||||
// Émettre MembresActionForbidden avec le message métier du backend.
|
||||
if (e.response?.statusCode == 409 || e.response?.statusCode == 403) {
|
||||
final msg = _extractErrorMessage(e)
|
||||
?? 'Suppression impossible : vérifiez que le membre n\'est pas le seul admin d\'une organisation.';
|
||||
emit(MembresActionForbidden(message: msg, error: e));
|
||||
return;
|
||||
}
|
||||
emit(MembresNetworkError(
|
||||
message: _getNetworkErrorMessage(e),
|
||||
code: e.response?.statusCode.toString(),
|
||||
@@ -582,6 +590,18 @@ class MembresBloc extends Bloc<MembresEvent, MembresState> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Extrait le message d'erreur du corps JSON d'une réponse HTTP (champ "error"
|
||||
/// ou "message"). Retourne null si non disponible.
|
||||
String? _extractErrorMessage(DioException e) {
|
||||
try {
|
||||
final data = e.response?.data;
|
||||
if (data is Map) {
|
||||
return (data['message'] ?? data['error'])?.toString();
|
||||
}
|
||||
} catch (_) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
// ── Handlers cycle de vie des adhésions ──────────────────────────────────
|
||||
|
||||
Future<void> _onInviterMembre(
|
||||
@@ -629,6 +649,11 @@ class MembresBloc extends Bloc<MembresEvent, MembresState> {
|
||||
));
|
||||
} on DioException catch (e) {
|
||||
if (e.type == DioExceptionType.cancel) return;
|
||||
if (e.response?.statusCode == 403) {
|
||||
final msg = _extractErrorMessage(e) ?? 'Action non autorisée sur ce membre.';
|
||||
emit(MembresActionForbidden(message: msg, error: e));
|
||||
return;
|
||||
}
|
||||
emit(MembresNetworkError(
|
||||
message: _getNetworkErrorMessage(e),
|
||||
code: e.response?.statusCode.toString(),
|
||||
@@ -656,6 +681,11 @@ class MembresBloc extends Bloc<MembresEvent, MembresState> {
|
||||
));
|
||||
} on DioException catch (e) {
|
||||
if (e.type == DioExceptionType.cancel) return;
|
||||
if (e.response?.statusCode == 403) {
|
||||
final msg = _extractErrorMessage(e) ?? 'Action non autorisée sur ce membre.';
|
||||
emit(MembresActionForbidden(message: msg, error: e));
|
||||
return;
|
||||
}
|
||||
emit(MembresNetworkError(
|
||||
message: _getNetworkErrorMessage(e),
|
||||
code: e.response?.statusCode.toString(),
|
||||
@@ -683,6 +713,11 @@ class MembresBloc extends Bloc<MembresEvent, MembresState> {
|
||||
));
|
||||
} on DioException catch (e) {
|
||||
if (e.type == DioExceptionType.cancel) return;
|
||||
if (e.response?.statusCode == 403) {
|
||||
final msg = _extractErrorMessage(e) ?? 'Action non autorisée sur ce membre.';
|
||||
emit(MembresActionForbidden(message: msg, error: e));
|
||||
return;
|
||||
}
|
||||
emit(MembresNetworkError(
|
||||
message: _getNetworkErrorMessage(e),
|
||||
code: e.response?.statusCode.toString(),
|
||||
|
||||
Reference in New Issue
Block a user