import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:get_it/get_it.dart'; import '../../../../core/utils/logger.dart'; import '../../bloc/admin_users_bloc.dart'; import '../../data/models/admin_user_model.dart'; import '../../data/repositories/admin_user_repository.dart'; import '../../../organizations/data/models/organization_model.dart'; import '../../../organizations/data/services/organization_service.dart'; import '../../../../shared/design_system/unionflow_design_system.dart'; import '../../../../shared/widgets/core_card.dart'; import '../../../../shared/design_system/components/uf_app_bar.dart'; import '../../../../shared/design_system/components/uf_buttons.dart'; /// Page détail d'un utilisateur + édition des rôles class UserManagementDetailPage extends StatelessWidget { final String userId; const UserManagementDetailPage({super.key, required this.userId}); @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppColors.background, appBar: const UFAppBar( title: 'Détail utilisateur', ), body: BlocBuilder( builder: (context, state) { if (state is AdminUsersLoading) { return const Center(child: CircularProgressIndicator()); } if (state is AdminUsersError) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Text(state.message), const SizedBox(height: 16), ElevatedButton( onPressed: () => context.read().add(AdminUserDetailRequested(userId)), child: const Text('Réessayer'), ), ], ), ); } if (state is AdminUserDetailLoaded) { return _UserDetailContent( user: state.user, userRoles: state.userRoles, allRoles: state.allRoles, userId: userId, ); } return const SizedBox(); }, ), ); } } class _UserDetailContent extends StatefulWidget { final AdminUserModel user; final List userRoles; final List allRoles; final String userId; const _UserDetailContent({ required this.user, required this.userRoles, required this.allRoles, required this.userId, }); @override State<_UserDetailContent> createState() => _UserDetailContentState(); } class _UserDetailContentState extends State<_UserDetailContent> { late Set _selectedRoleNames; @override void initState() { super.initState(); _selectedRoleNames = widget.userRoles.map((r) => r.name).toSet(); } @override Widget build(BuildContext context) { return BlocListener( listener: (context, state) { if (state is AdminUserRolesUpdated) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Rôles mis à jour')), ); } }, child: SingleChildScrollView( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ CoreCard( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(widget.user.displayName, style: AppTypography.headerSmall), const SizedBox(height: 8), if (widget.user.email != null) Text('Email: ${widget.user.email}', style: AppTypography.bodyTextSmall), if (widget.user.username != null) Text('Username: ${widget.user.username}', style: AppTypography.bodyTextSmall), Text( 'Statut: ${widget.user.enabled == true ? "Actif" : "Inactif"}', style: AppTypography.bodyTextSmall.copyWith( color: widget.user.enabled == true ? AppColors.success : AppColors.error, fontWeight: FontWeight.bold, ), ), ], ), ), const SizedBox(height: 16), Text( 'RÔLES (SÉLECTION)', style: AppTypography.subtitleSmall.copyWith( fontWeight: FontWeight.bold, letterSpacing: 1.1, ), ), const SizedBox(height: 8), ...widget.allRoles.map((role) { final selected = _selectedRoleNames.contains(role.name); return CheckboxListTile( title: Text(role.name, style: AppTypography.bodyTextSmall), activeColor: AppColors.primaryGreen, contentPadding: EdgeInsets.zero, dense: true, value: selected, onChanged: (v) { setState(() { if (v == true) { _selectedRoleNames.add(role.name); } else { _selectedRoleNames.remove(role.name); } }); }, ); }), const SizedBox(height: 24), UFPrimaryButton( label: 'Enregistrer les rôles', onPressed: () { context.read().add( AdminUserRolesUpdateRequested(widget.userId, _selectedRoleNames.toList()), ); }, ), const SizedBox(height: 24), const Divider(height: 1), const SizedBox(height: 16), Text( 'ASSOCIER À UNE ORGANISATION', style: AppTypography.subtitleSmall.copyWith( fontWeight: FontWeight.bold, letterSpacing: 1.1, ), ), const SizedBox(height: 8), Text( 'Permet à cet utilisateur (ex. admin d\'organisation) de voir « Mes organisations » et d\'accéder au dashboard de l\'organisation.', style: AppTypography.bodyTextSmall.copyWith(color: AppColors.textSecondaryLight), ), const SizedBox(height: 12), OutlinedButton.icon( onPressed: widget.user.email == null || widget.user.email!.isEmpty ? null : () => _openAssocierOrganisationDialog(context, widget.user.email!), icon: const Icon(Icons.business, size: 18), label: const Text('Associer à une organisation'), style: OutlinedButton.styleFrom( foregroundColor: AppColors.primaryGreen, side: const BorderSide(color: AppColors.primaryGreen), ), ), ], ), ), ); } Future _openAssocierOrganisationDialog(BuildContext context, String userEmail) async { final orgService = GetIt.I(); final adminRepo = GetIt.I(); List organisations = []; try { organisations = await orgService.getOrganizations(page: 0, size: 200); } catch (e, st) { AppLogger.error('UserManagementDetail: chargement organisations échoué', error: e, stackTrace: st); if (!context.mounted) return; ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Impossible de charger les organisations')), ); return; } if (!context.mounted) return; final orgsWithId = organisations.where((o) => o.id != null && o.id!.isNotEmpty).toList(); if (orgsWithId.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Aucune organisation disponible. Créez-en une d\'abord.')), ); return; } String? selectedOrgId = orgsWithId.first.id; await showDialog( context: context, builder: (ctx) => StatefulBuilder( builder: (ctx2, setDialogState) { return AlertDialog( title: const Text('Associer à une organisation'), content: SingleChildScrollView( child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Utilisateur: $userEmail', style: AppTypography.bodyTextSmall), const SizedBox(height: 16), DropdownButtonFormField( value: selectedOrgId, isExpanded: true, decoration: const InputDecoration( labelText: 'Organisation', border: OutlineInputBorder(), ), items: orgsWithId .map((o) => DropdownMenuItem(value: o.id, child: Text(o.nom, overflow: TextOverflow.ellipsis))) .toList(), onChanged: (v) => setDialogState(() => selectedOrgId = v), ), ], ), ), actions: [ TextButton( onPressed: () => Navigator.of(ctx).pop(), child: const Text('Annuler'), ), FilledButton( onPressed: () async { if (selectedOrgId == null) return; try { await adminRepo.associerOrganisation(email: userEmail, organisationId: selectedOrgId!); if (ctx.mounted) Navigator.of(ctx).pop(); if (context.mounted) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Utilisateur associé à l\'organisation avec succès.')), ); } } catch (e) { if (ctx.mounted) { ScaffoldMessenger.of(ctx).showSnackBar( SnackBar(content: Text('Erreur: ${e.toString().replaceFirst('Exception: ', '')}')), ); } } }, child: const Text('Associer'), ), ], ); }, ), ); } }