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));
|
emit(MembreDeleted(event.id));
|
||||||
} on DioException catch (e) {
|
} on DioException catch (e) {
|
||||||
if (e.type == DioExceptionType.cancel) return;
|
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(
|
emit(MembresNetworkError(
|
||||||
message: _getNetworkErrorMessage(e),
|
message: _getNetworkErrorMessage(e),
|
||||||
code: e.response?.statusCode.toString(),
|
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 ──────────────────────────────────
|
// ── Handlers cycle de vie des adhésions ──────────────────────────────────
|
||||||
|
|
||||||
Future<void> _onInviterMembre(
|
Future<void> _onInviterMembre(
|
||||||
@@ -629,6 +649,11 @@ class MembresBloc extends Bloc<MembresEvent, MembresState> {
|
|||||||
));
|
));
|
||||||
} on DioException catch (e) {
|
} on DioException catch (e) {
|
||||||
if (e.type == DioExceptionType.cancel) return;
|
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(
|
emit(MembresNetworkError(
|
||||||
message: _getNetworkErrorMessage(e),
|
message: _getNetworkErrorMessage(e),
|
||||||
code: e.response?.statusCode.toString(),
|
code: e.response?.statusCode.toString(),
|
||||||
@@ -656,6 +681,11 @@ class MembresBloc extends Bloc<MembresEvent, MembresState> {
|
|||||||
));
|
));
|
||||||
} on DioException catch (e) {
|
} on DioException catch (e) {
|
||||||
if (e.type == DioExceptionType.cancel) return;
|
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(
|
emit(MembresNetworkError(
|
||||||
message: _getNetworkErrorMessage(e),
|
message: _getNetworkErrorMessage(e),
|
||||||
code: e.response?.statusCode.toString(),
|
code: e.response?.statusCode.toString(),
|
||||||
@@ -683,6 +713,11 @@ class MembresBloc extends Bloc<MembresEvent, MembresState> {
|
|||||||
));
|
));
|
||||||
} on DioException catch (e) {
|
} on DioException catch (e) {
|
||||||
if (e.type == DioExceptionType.cancel) return;
|
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(
|
emit(MembresNetworkError(
|
||||||
message: _getNetworkErrorMessage(e),
|
message: _getNetworkErrorMessage(e),
|
||||||
code: e.response?.statusCode.toString(),
|
code: e.response?.statusCode.toString(),
|
||||||
|
|||||||
@@ -248,6 +248,12 @@ class MembresNetworkError extends MembresError {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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
|
/// État d'erreur de validation
|
||||||
class MembresValidationError extends MembresError {
|
class MembresValidationError extends MembresError {
|
||||||
final Map<String, String> validationErrors;
|
final Map<String, String> validationErrors;
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,7 @@ import 'package:flutter/material.dart';
|
|||||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||||
import 'package:get_it/get_it.dart';
|
import 'package:get_it/get_it.dart';
|
||||||
|
|
||||||
|
import '../../../../shared/design_system/tokens/color_tokens.dart';
|
||||||
import '../../../../shared/widgets/error_widget.dart';
|
import '../../../../shared/widgets/error_widget.dart';
|
||||||
import '../../../../shared/widgets/loading_widget.dart';
|
import '../../../../shared/widgets/loading_widget.dart';
|
||||||
import '../../../../core/utils/logger.dart';
|
import '../../../../core/utils/logger.dart';
|
||||||
@@ -60,13 +61,31 @@ class MembersPageConnected extends StatelessWidget {
|
|||||||
context.read<MembresBloc>().add(LoadMembres(refresh: true, organisationId: organisationId));
|
context.read<MembresBloc>().add(LoadMembres(refresh: true, organisationId: organisationId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Accès refusé sur une action lifecycle (403) — SnackBar ciblé + rechargement liste
|
||||||
|
if (state is MembresActionForbidden) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Row(children: [
|
||||||
|
const Icon(Icons.block_outlined, color: Colors.white, size: 18),
|
||||||
|
const SizedBox(width: 8),
|
||||||
|
Expanded(child: Text(state.message)),
|
||||||
|
]),
|
||||||
|
backgroundColor: ColorTokens.error,
|
||||||
|
duration: const Duration(seconds: 5),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
// Recharger pour revenir à l'état MembresLoaded (évite la page d'erreur plein écran)
|
||||||
|
context.read<MembresBloc>().add(LoadMembres(organisationId: organisationId));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Gestion des erreurs avec SnackBar
|
// Gestion des erreurs avec SnackBar
|
||||||
if (state is MembresError) {
|
if (state is MembresError) {
|
||||||
final bloc = context.read<MembresBloc>();
|
final bloc = context.read<MembresBloc>();
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
SnackBar(
|
SnackBar(
|
||||||
content: Text(state.message),
|
content: Text(state.message),
|
||||||
backgroundColor: Colors.red,
|
backgroundColor: ColorTokens.error,
|
||||||
duration: const Duration(seconds: 4),
|
duration: const Duration(seconds: 4),
|
||||||
action: SnackBarAction(
|
action: SnackBarAction(
|
||||||
label: 'Réessayer',
|
label: 'Réessayer',
|
||||||
@@ -84,13 +103,31 @@ class MembersPageConnected extends StatelessWidget {
|
|||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(
|
const SnackBar(
|
||||||
content: Text('Membre activé avec succès'),
|
content: Text('Membre activé avec succès'),
|
||||||
backgroundColor: Colors.green,
|
backgroundColor: ColorTokens.success,
|
||||||
duration: Duration(seconds: 3),
|
duration: Duration(seconds: 3),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
context.read<MembresBloc>().add(LoadMembres(refresh: true, organisationId: organisationId));
|
context.read<MembresBloc>().add(LoadMembres(refresh: true, organisationId: organisationId));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Après suppression définitive : feedback + pop la page de détail si ouverte + rechargement
|
||||||
|
if (state is MembreDeleted) {
|
||||||
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
|
SnackBar(
|
||||||
|
content: Row(children: const [
|
||||||
|
Icon(Icons.delete_forever, color: Colors.white, size: 18),
|
||||||
|
SizedBox(width: 8),
|
||||||
|
Expanded(child: Text('Compte supprimé avec succès')),
|
||||||
|
]),
|
||||||
|
backgroundColor: ColorTokens.error,
|
||||||
|
duration: const Duration(seconds: 3),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
// Si on est dans MemberDetailPage, revenir à la liste (pop unique)
|
||||||
|
Navigator.of(context).maybePop();
|
||||||
|
context.read<MembresBloc>().add(LoadMembres(refresh: true, organisationId: organisationId));
|
||||||
|
}
|
||||||
|
|
||||||
// Après reset mot de passe : afficher le dialog credentials
|
// Après reset mot de passe : afficher le dialog credentials
|
||||||
if (state is MotDePasseReinitialise) {
|
if (state is MotDePasseReinitialise) {
|
||||||
showCredentialsDialog(context, state.membre);
|
showCredentialsDialog(context, state.membre);
|
||||||
@@ -101,7 +138,7 @@ class MembersPageConnected extends StatelessWidget {
|
|||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(
|
const SnackBar(
|
||||||
content: Text('Membre affecté à l\'organisation avec succès'),
|
content: Text('Membre affecté à l\'organisation avec succès'),
|
||||||
backgroundColor: Colors.green,
|
backgroundColor: ColorTokens.success,
|
||||||
duration: Duration(seconds: 3),
|
duration: Duration(seconds: 3),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
@@ -111,25 +148,25 @@ class MembersPageConnected extends StatelessWidget {
|
|||||||
// Lifecycle adhésion : succès + rechargement
|
// Lifecycle adhésion : succès + rechargement
|
||||||
if (state is MembreInvite) {
|
if (state is MembreInvite) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(content: Text('Invitation envoyée'), backgroundColor: Colors.blue, duration: Duration(seconds: 3)),
|
const SnackBar(content: Text('Invitation envoyée'), backgroundColor: ColorTokens.info, duration: Duration(seconds: 3)),
|
||||||
);
|
);
|
||||||
context.read<MembresBloc>().add(LoadMembres(refresh: true, organisationId: organisationId));
|
context.read<MembresBloc>().add(LoadMembres(refresh: true, organisationId: organisationId));
|
||||||
}
|
}
|
||||||
if (state is AdhesionActivee) {
|
if (state is AdhesionActivee) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(content: Text('Adhésion activée'), backgroundColor: Colors.green, duration: Duration(seconds: 3)),
|
const SnackBar(content: Text('Adhésion activée'), backgroundColor: ColorTokens.success, duration: Duration(seconds: 3)),
|
||||||
);
|
);
|
||||||
context.read<MembresBloc>().add(LoadMembres(refresh: true, organisationId: organisationId));
|
context.read<MembresBloc>().add(LoadMembres(refresh: true, organisationId: organisationId));
|
||||||
}
|
}
|
||||||
if (state is AdhesionSuspendue) {
|
if (state is AdhesionSuspendue) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(content: Text('Adhésion suspendue'), backgroundColor: Colors.orange, duration: Duration(seconds: 3)),
|
const SnackBar(content: Text('Adhésion suspendue'), backgroundColor: ColorTokens.warning, duration: Duration(seconds: 3)),
|
||||||
);
|
);
|
||||||
context.read<MembresBloc>().add(LoadMembres(refresh: true, organisationId: organisationId));
|
context.read<MembresBloc>().add(LoadMembres(refresh: true, organisationId: organisationId));
|
||||||
}
|
}
|
||||||
if (state is MembreRadie) {
|
if (state is MembreRadie) {
|
||||||
ScaffoldMessenger.of(context).showSnackBar(
|
ScaffoldMessenger.of(context).showSnackBar(
|
||||||
const SnackBar(content: Text('Membre radié de l\'organisation'), backgroundColor: Colors.red, duration: Duration(seconds: 3)),
|
const SnackBar(content: Text('Membre radié de l\'organisation'), backgroundColor: ColorTokens.error, duration: Duration(seconds: 3)),
|
||||||
);
|
);
|
||||||
context.read<MembresBloc>().add(LoadMembres(refresh: true, organisationId: organisationId));
|
context.read<MembresBloc>().add(LoadMembres(refresh: true, organisationId: organisationId));
|
||||||
}
|
}
|
||||||
@@ -141,7 +178,7 @@ class MembersPageConnected extends StatelessWidget {
|
|||||||
// État initial
|
// État initial
|
||||||
if (state is MembresInitial) {
|
if (state is MembresInitial) {
|
||||||
return Container(
|
return Container(
|
||||||
color: const Color(0xFFF8F9FA),
|
color: null,
|
||||||
child: const Center(
|
child: const Center(
|
||||||
child: AppLoadingWidget(message: 'Initialisation...'),
|
child: AppLoadingWidget(message: 'Initialisation...'),
|
||||||
),
|
),
|
||||||
@@ -151,7 +188,7 @@ class MembersPageConnected extends StatelessWidget {
|
|||||||
// État de chargement
|
// État de chargement
|
||||||
if (state is MembresLoading) {
|
if (state is MembresLoading) {
|
||||||
return Container(
|
return Container(
|
||||||
color: const Color(0xFFF8F9FA),
|
color: null,
|
||||||
child: const Center(
|
child: const Center(
|
||||||
child: AppLoadingWidget(message: 'Chargement des membres...'),
|
child: AppLoadingWidget(message: 'Chargement des membres...'),
|
||||||
),
|
),
|
||||||
@@ -162,7 +199,7 @@ class MembersPageConnected extends StatelessWidget {
|
|||||||
if (state is MembresRefreshing) {
|
if (state is MembresRefreshing) {
|
||||||
// Affiche un indicateur pendant le rafraîchissement
|
// Affiche un indicateur pendant le rafraîchissement
|
||||||
return Container(
|
return Container(
|
||||||
color: const Color(0xFFF8F9FA),
|
color: null,
|
||||||
child: const Center(
|
child: const Center(
|
||||||
child: AppLoadingWidget(message: 'Actualisation...'),
|
child: AppLoadingWidget(message: 'Actualisation...'),
|
||||||
),
|
),
|
||||||
@@ -172,7 +209,7 @@ class MembersPageConnected extends StatelessWidget {
|
|||||||
// Après création : on recharge la liste (listener a dispatché LoadMembres)
|
// Après création : on recharge la liste (listener a dispatché LoadMembres)
|
||||||
if (state is MembreCreated) {
|
if (state is MembreCreated) {
|
||||||
return Container(
|
return Container(
|
||||||
color: const Color(0xFFF8F9FA),
|
color: null,
|
||||||
child: const Center(
|
child: const Center(
|
||||||
child: AppLoadingWidget(message: 'Actualisation...'),
|
child: AppLoadingWidget(message: 'Actualisation...'),
|
||||||
),
|
),
|
||||||
@@ -216,25 +253,35 @@ class MembersPageConnected extends StatelessWidget {
|
|||||||
context.read<MembresBloc>().add(AffecterOrganisation(memberId, orgId));
|
context.read<MembresBloc>().add(AffecterOrganisation(memberId, orgId));
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
onLifecycleAction: organisationId != null
|
// Suppression définitive — SuperAdmin uniquement (organisationId == null)
|
||||||
? (memberId, action, motif) {
|
// Backend : DELETE /api/membres/{id} @RolesAllowed({ ADMIN, SUPER_ADMIN })
|
||||||
final bloc = context.read<MembresBloc>();
|
onDeleteAccount: organisationId == null
|
||||||
switch (action) {
|
? (memberId) {
|
||||||
case 'inviter':
|
context.read<MembresBloc>().add(DeleteMembre(memberId));
|
||||||
bloc.add(InviterMembre(membreId: memberId, organisationId: organisationId!));
|
|
||||||
break;
|
|
||||||
case 'activer':
|
|
||||||
bloc.add(ActiverAdhesion(membreId: memberId, organisationId: organisationId!, motif: motif));
|
|
||||||
break;
|
|
||||||
case 'suspendre':
|
|
||||||
bloc.add(SuspendrAdhesion(membreId: memberId, organisationId: organisationId!, motif: motif));
|
|
||||||
break;
|
|
||||||
case 'radier':
|
|
||||||
bloc.add(RadierAdhesion(membreId: memberId, organisationId: organisationId!, motif: motif));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
: null,
|
: null,
|
||||||
|
// onLifecycleAction est toujours fourni.
|
||||||
|
// orgAdmin : organisationId fixe (contexte org).
|
||||||
|
// superAdmin : organisationId provient du membre lui-même (passé par _lifecycleButtons).
|
||||||
|
onLifecycleAction: (memberId, targetOrgId, action, motif) {
|
||||||
|
final bloc = context.read<MembresBloc>();
|
||||||
|
// En contexte orgAdmin on utilise l'org fixe, en superAdmin l'org du membre
|
||||||
|
final effectiveOrgId = organisationId ?? targetOrgId;
|
||||||
|
switch (action) {
|
||||||
|
case 'inviter':
|
||||||
|
bloc.add(InviterMembre(membreId: memberId, organisationId: effectiveOrgId));
|
||||||
|
break;
|
||||||
|
case 'activer':
|
||||||
|
bloc.add(ActiverAdhesion(membreId: memberId, organisationId: effectiveOrgId, motif: motif));
|
||||||
|
break;
|
||||||
|
case 'suspendre':
|
||||||
|
bloc.add(SuspendrAdhesion(membreId: memberId, organisationId: effectiveOrgId, motif: motif));
|
||||||
|
break;
|
||||||
|
case 'radier':
|
||||||
|
bloc.add(RadierAdhesion(membreId: memberId, organisationId: effectiveOrgId, motif: motif));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -242,7 +289,7 @@ class MembersPageConnected extends StatelessWidget {
|
|||||||
if (state is MembresNetworkError) {
|
if (state is MembresNetworkError) {
|
||||||
AppLogger.error('MembersPageConnected: Erreur réseau', error: state.message);
|
AppLogger.error('MembersPageConnected: Erreur réseau', error: state.message);
|
||||||
return Container(
|
return Container(
|
||||||
color: const Color(0xFFF8F9FA),
|
color: null,
|
||||||
child: NetworkErrorWidget(
|
child: NetworkErrorWidget(
|
||||||
onRetry: () {
|
onRetry: () {
|
||||||
AppLogger.userAction('Retry load membres after network error');
|
AppLogger.userAction('Retry load membres after network error');
|
||||||
@@ -256,7 +303,7 @@ class MembersPageConnected extends StatelessWidget {
|
|||||||
if (state is MembresError) {
|
if (state is MembresError) {
|
||||||
AppLogger.error('MembersPageConnected: Erreur', error: state.message);
|
AppLogger.error('MembersPageConnected: Erreur', error: state.message);
|
||||||
return Container(
|
return Container(
|
||||||
color: const Color(0xFFF8F9FA),
|
color: null,
|
||||||
child: AppErrorWidget(
|
child: AppErrorWidget(
|
||||||
message: state.message,
|
message: state.message,
|
||||||
onRetry: () {
|
onRetry: () {
|
||||||
@@ -270,7 +317,7 @@ class MembersPageConnected extends StatelessWidget {
|
|||||||
// État par défaut (ne devrait jamais arriver)
|
// État par défaut (ne devrait jamais arriver)
|
||||||
AppLogger.warning('MembersPageConnected: État non géré: ${state.runtimeType}');
|
AppLogger.warning('MembersPageConnected: État non géré: ${state.runtimeType}');
|
||||||
return Container(
|
return Container(
|
||||||
color: const Color(0xFFF8F9FA),
|
color: null,
|
||||||
child: const Center(
|
child: const Center(
|
||||||
child: AppLoadingWidget(message: 'Chargement...'),
|
child: AppLoadingWidget(message: 'Chargement...'),
|
||||||
),
|
),
|
||||||
@@ -292,6 +339,8 @@ class MembersPageConnected extends StatelessWidget {
|
|||||||
'id': membre.id ?? '',
|
'id': membre.id ?? '',
|
||||||
'name': membre.nomComplet,
|
'name': membre.nomComplet,
|
||||||
'email': membre.email,
|
'email': membre.email,
|
||||||
|
'actif': membre.actif, // flag global désactivation (soft delete)
|
||||||
|
'roleCode': membre.role, // code brut ex: ORGADMIN, SUPERADMIN — utilisé pour les checks de permission
|
||||||
'role': _mapRoleToString(membre.role),
|
'role': _mapRoleToString(membre.role),
|
||||||
'status': _mapStatutToString(membre.statut),
|
'status': _mapStatutToString(membre.statut),
|
||||||
'joinDate': membre.dateAdhesion,
|
'joinDate': membre.dateAdhesion,
|
||||||
@@ -323,6 +372,10 @@ class MembersPageConnected extends StatelessWidget {
|
|||||||
'fonctionBureau': membre.fonctionBureau,
|
'fonctionBureau': membre.fonctionBureau,
|
||||||
'numeroMembre': membre.numeroMembre,
|
'numeroMembre': membre.numeroMembre,
|
||||||
'cotisationAJour': membre.cotisationAJour,
|
'cotisationAJour': membre.cotisationAJour,
|
||||||
|
'organisationNom': membre.organisationNom,
|
||||||
|
'statutKyc': membre.statutKyc?.name,
|
||||||
|
'niveauVigilanceKyc': membre.niveauVigilanceKyc?.name,
|
||||||
|
'statutMembre': membre.statut?.name,
|
||||||
|
|
||||||
// Propriétés calculées
|
// Propriétés calculées
|
||||||
'initiales': membre.initiales,
|
'initiales': membre.initiales,
|
||||||
|
|||||||
Reference in New Issue
Block a user