refactoring
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import '../../../../core/di/injection_container.dart';
|
||||
import '../../../../shared/design_system/tokens/app_colors.dart';
|
||||
import '../../../../shared/models/membre_search_criteria.dart';
|
||||
import '../../../../shared/models/membre_search_result.dart';
|
||||
import '../../../organizations/domain/repositories/organization_repository.dart';
|
||||
@@ -115,7 +116,7 @@ class _AdvancedSearchPageState extends State<AdvancedSearchPage>
|
||||
return Scaffold(
|
||||
appBar: AppBar(
|
||||
title: const Text('Recherche Avancée'),
|
||||
backgroundColor: Theme.of(context).primaryColor,
|
||||
backgroundColor: AppColors.primaryGreen,
|
||||
foregroundColor: Colors.white,
|
||||
elevation: 0,
|
||||
bottom: TabBar(
|
||||
@@ -178,7 +179,7 @@ class _AdvancedSearchPageState extends State<AdvancedSearchPage>
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(Icons.flash_on, color: Theme.of(context).primaryColor),
|
||||
const Icon(Icons.flash_on, color: AppColors.primaryGreen),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'Recherche Rapide',
|
||||
@@ -233,7 +234,7 @@ class _AdvancedSearchPageState extends State<AdvancedSearchPage>
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(Icons.tune, color: Theme.of(context).primaryColor),
|
||||
const Icon(Icons.tune, color: AppColors.primaryGreen),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'Critères Détaillés',
|
||||
@@ -324,7 +325,7 @@ class _AdvancedSearchPageState extends State<AdvancedSearchPage>
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(Icons.filter_alt, color: Theme.of(context).primaryColor),
|
||||
const Icon(Icons.filter_alt, color: AppColors.primaryGreen),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'Filtres Avancés',
|
||||
@@ -499,7 +500,7 @@ class _AdvancedSearchPageState extends State<AdvancedSearchPage>
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
const Icon(Icons.error, size: 64, color: Colors.red),
|
||||
const Icon(Icons.error, size: 64, color: AppColors.error),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Erreur de recherche',
|
||||
@@ -509,7 +510,7 @@ class _AdvancedSearchPageState extends State<AdvancedSearchPage>
|
||||
Text(
|
||||
_errorMessage!,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(color: Colors.red),
|
||||
style: const TextStyle(color: AppColors.error),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
ElevatedButton(
|
||||
@@ -573,8 +574,8 @@ class _AdvancedSearchPageState extends State<AdvancedSearchPage>
|
||||
return ActionChip(
|
||||
label: Text(label),
|
||||
onPressed: onTap,
|
||||
backgroundColor: Theme.of(context).primaryColor.withOpacity(0.1),
|
||||
labelStyle: TextStyle(color: Theme.of(context).primaryColor),
|
||||
backgroundColor: AppColors.primaryGreen.withOpacity(0.1),
|
||||
labelStyle: const TextStyle(color: AppColors.primaryGreen),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -620,7 +621,7 @@ class _AdvancedSearchPageState extends State<AdvancedSearchPage>
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Veuillez spécifier au moins un critère de recherche'),
|
||||
backgroundColor: Colors.orange,
|
||||
backgroundColor: AppColors.warning,
|
||||
),
|
||||
);
|
||||
return;
|
||||
@@ -649,7 +650,7 @@ class _AdvancedSearchPageState extends State<AdvancedSearchPage>
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text(result.resultDescription),
|
||||
backgroundColor: Colors.green,
|
||||
backgroundColor: AppColors.success,
|
||||
),
|
||||
);
|
||||
} catch (e) {
|
||||
@@ -661,7 +662,7 @@ class _AdvancedSearchPageState extends State<AdvancedSearchPage>
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Erreur de recherche: $e'),
|
||||
backgroundColor: Colors.red,
|
||||
backgroundColor: AppColors.error,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import '../../../../shared/design_system/tokens/app_colors.dart';
|
||||
import '../../../../shared/design_system/unionflow_design_system.dart';
|
||||
import '../../../../features/authentication/presentation/bloc/auth_bloc.dart';
|
||||
import '../../../../features/authentication/data/models/user_role.dart';
|
||||
@@ -174,13 +175,13 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
builder: (context, state) {
|
||||
if (state is! AuthAuthenticated) {
|
||||
return Container(
|
||||
color: const Color(0xFFF8F9FA),
|
||||
color: AppColors.lightBackground,
|
||||
child: const Center(child: CircularProgressIndicator()),
|
||||
);
|
||||
}
|
||||
|
||||
return Container(
|
||||
color: const Color(0xFFF8F9FA),
|
||||
color: AppColors.lightBackground,
|
||||
child: _buildMembersContent(state),
|
||||
);
|
||||
},
|
||||
@@ -196,19 +197,19 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
children: [
|
||||
// Header avec titre et actions
|
||||
_buildMembersHeader(state),
|
||||
const SizedBox(height: 16),
|
||||
const SizedBox(height: 8),
|
||||
|
||||
// Statistiques et métriques
|
||||
_buildMembersMetrics(),
|
||||
const SizedBox(height: 16),
|
||||
const SizedBox(height: 8),
|
||||
|
||||
// Barre de recherche et filtres
|
||||
_buildSearchAndFilters(),
|
||||
const SizedBox(height: 16),
|
||||
const SizedBox(height: 8),
|
||||
|
||||
// Onglets de catégories
|
||||
_buildCategoryTabs(),
|
||||
const SizedBox(height: 16),
|
||||
const SizedBox(height: 8),
|
||||
|
||||
// Liste/Grille des membres
|
||||
_buildMembersDisplay(),
|
||||
@@ -265,8 +266,8 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
'Métriques & Statistiques',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Color(0xFF6C5CE7),
|
||||
fontSize: 18,
|
||||
color: AppColors.primaryGreen,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
@@ -280,7 +281,7 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
totalMembers.toString(),
|
||||
'+$newThisMonth ce mois',
|
||||
Icons.people,
|
||||
const Color(0xFF6C5CE7),
|
||||
AppColors.primaryGreen,
|
||||
trend: newThisMonth > 0 ? 'up' : 'stable',
|
||||
),
|
||||
),
|
||||
@@ -291,7 +292,7 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
activeMembers.toString(),
|
||||
'${((activeMembers / totalMembers) * 100).toStringAsFixed(1)}%',
|
||||
Icons.check_circle,
|
||||
const Color(0xFF00B894),
|
||||
AppColors.success,
|
||||
trend: 'up',
|
||||
),
|
||||
),
|
||||
@@ -308,7 +309,7 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
avgContribution.toStringAsFixed(0),
|
||||
'Contribution',
|
||||
Icons.trending_up,
|
||||
const Color(0xFF0984E3),
|
||||
AppColors.brandGreenLight,
|
||||
trend: 'up',
|
||||
),
|
||||
),
|
||||
@@ -319,7 +320,7 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
newThisMonth.toString(),
|
||||
'Ce mois',
|
||||
Icons.new_releases,
|
||||
const Color(0xFFF39C12),
|
||||
AppColors.warning,
|
||||
trend: newThisMonth > 0 ? 'up' : 'stable',
|
||||
),
|
||||
),
|
||||
@@ -388,7 +389,7 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF6B7280),
|
||||
color: AppColors.textSecondaryLight,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 2),
|
||||
@@ -396,7 +397,7 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
subtitle,
|
||||
style: const TextStyle(
|
||||
fontSize: 10,
|
||||
color: Color(0xFF9CA3AF),
|
||||
color: AppColors.textSecondaryLight,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -424,7 +425,7 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
const Icon(Icons.search, color: Color(0xFF6B7280)),
|
||||
const Icon(Icons.search, color: AppColors.textSecondaryLight),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
@@ -432,7 +433,7 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'Rechercher par nom, email, département...',
|
||||
border: InputBorder.none,
|
||||
hintStyle: TextStyle(color: Color(0xFF9CA3AF)),
|
||||
hintStyle: TextStyle(color: AppColors.textSecondaryLight),
|
||||
),
|
||||
onChanged: (value) {
|
||||
setState(() {
|
||||
@@ -449,13 +450,13 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
_searchQuery = '';
|
||||
});
|
||||
},
|
||||
icon: const Icon(Icons.clear, color: Color(0xFF6B7280)),
|
||||
icon: const Icon(Icons.clear, color: AppColors.textSecondaryLight),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Container(
|
||||
height: 32,
|
||||
width: 1,
|
||||
color: const Color(0xFFE5E7EB),
|
||||
color: AppColors.lightBorder,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
IconButton(
|
||||
@@ -466,7 +467,7 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
},
|
||||
icon: Icon(
|
||||
_showAdvancedFilters ? Icons.filter_list_off : Icons.filter_list,
|
||||
color: _showAdvancedFilters ? const Color(0xFF6C5CE7) : const Color(0xFF6B7280),
|
||||
color: _showAdvancedFilters ? AppColors.primaryGreen : AppColors.textSecondaryLight,
|
||||
),
|
||||
tooltip: 'Filtres avancés',
|
||||
),
|
||||
@@ -478,7 +479,7 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
},
|
||||
icon: Icon(
|
||||
_isGridView ? Icons.view_list : Icons.grid_view,
|
||||
color: const Color(0xFF6B7280),
|
||||
color: AppColors.textSecondaryLight,
|
||||
),
|
||||
tooltip: _isGridView ? 'Vue liste' : 'Vue grille',
|
||||
),
|
||||
@@ -526,14 +527,14 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
});
|
||||
},
|
||||
backgroundColor: Colors.white,
|
||||
selectedColor: const Color(0xFF6C5CE7).withOpacity(0.1),
|
||||
checkmarkColor: const Color(0xFF6C5CE7),
|
||||
selectedColor: AppColors.primaryGreen.withOpacity(0.1),
|
||||
checkmarkColor: AppColors.primaryGreen,
|
||||
labelStyle: TextStyle(
|
||||
color: isSelected ? const Color(0xFF6C5CE7) : const Color(0xFF6B7280),
|
||||
color: isSelected ? AppColors.primaryGreen : AppColors.textSecondaryLight,
|
||||
fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal,
|
||||
),
|
||||
side: BorderSide(
|
||||
color: isSelected ? const Color(0xFF6C5CE7) : const Color(0xFFE5E7EB),
|
||||
color: isSelected ? AppColors.primaryGreen : AppColors.lightBorder,
|
||||
),
|
||||
),
|
||||
);
|
||||
@@ -549,7 +550,7 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.white,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: const Color(0xFFE5E7EB)),
|
||||
border: Border.all(color: AppColors.lightBorder),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -558,7 +559,7 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
'Filtres Avancés',
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF374151),
|
||||
color: AppColors.textPrimaryLight,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
@@ -589,14 +590,14 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
});
|
||||
},
|
||||
backgroundColor: Colors.grey[50],
|
||||
selectedColor: const Color(0xFF6C5CE7).withOpacity(0.1),
|
||||
checkmarkColor: const Color(0xFF6C5CE7),
|
||||
selectedColor: AppColors.primaryGreen.withOpacity(0.1),
|
||||
checkmarkColor: AppColors.primaryGreen,
|
||||
labelStyle: TextStyle(
|
||||
color: isSelected ? const Color(0xFF6C5CE7) : const Color(0xFF6B7280),
|
||||
color: isSelected ? AppColors.primaryGreen : AppColors.textSecondaryLight,
|
||||
fontSize: 12,
|
||||
),
|
||||
side: BorderSide(
|
||||
color: isSelected ? const Color(0xFF6C5CE7) : const Color(0xFFE5E7EB),
|
||||
color: isSelected ? AppColors.primaryGreen : AppColors.lightBorder,
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
@@ -616,14 +617,14 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
icon: const Icon(Icons.clear_all, size: 16),
|
||||
label: const Text('Réinitialiser'),
|
||||
style: TextButton.styleFrom(
|
||||
foregroundColor: const Color(0xFF6B7280),
|
||||
foregroundColor: AppColors.textSecondaryLight,
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
Text(
|
||||
'${_getFilteredMembers().length} résultat(s)',
|
||||
style: const TextStyle(
|
||||
color: Color(0xFF6B7280),
|
||||
color: AppColors.textSecondaryLight,
|
||||
fontSize: 12,
|
||||
),
|
||||
),
|
||||
@@ -656,9 +657,9 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
Tab(text: 'Équipes', icon: Icon(Icons.groups, size: 18)),
|
||||
Tab(text: 'Analytics', icon: Icon(Icons.analytics, size: 18)),
|
||||
],
|
||||
labelColor: const Color(0xFF6C5CE7),
|
||||
unselectedLabelColor: const Color(0xFF6B7280),
|
||||
indicatorColor: const Color(0xFF6C5CE7),
|
||||
labelColor: AppColors.primaryGreen,
|
||||
unselectedLabelColor: AppColors.textSecondaryLight,
|
||||
indicatorColor: AppColors.primaryGreen,
|
||||
indicatorWeight: 3,
|
||||
labelStyle: const TextStyle(
|
||||
fontSize: 12,
|
||||
@@ -804,7 +805,7 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.w600,
|
||||
fontSize: 16,
|
||||
color: Color(0xFF1F2937),
|
||||
color: AppColors.textPrimaryLight,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -829,7 +830,7 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
Text(
|
||||
member['email'],
|
||||
style: const TextStyle(
|
||||
color: Color(0xFF6B7280),
|
||||
color: AppColors.textSecondaryLight,
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
@@ -844,24 +845,24 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
member['department'],
|
||||
style: TextStyle(
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.grey[600],
|
||||
color: AppColors.textSecondaryLight,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Icon(
|
||||
const Icon(
|
||||
Icons.location_on,
|
||||
size: 12,
|
||||
color: Colors.grey[500],
|
||||
color: AppColors.textSecondaryLight,
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Expanded(
|
||||
child: Text(
|
||||
member['location'],
|
||||
style: TextStyle(
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: Colors.grey[600],
|
||||
color: AppColors.textSecondaryLight,
|
||||
),
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
@@ -903,7 +904,7 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
'Rejoint ${_formatDate(joinDate)}',
|
||||
style: const TextStyle(
|
||||
fontSize: 10,
|
||||
color: Color(0xFF9CA3AF),
|
||||
color: AppColors.textSecondaryLight,
|
||||
),
|
||||
),
|
||||
const Spacer(),
|
||||
@@ -911,7 +912,7 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
'Actif ${_formatRelativeTime(lastActivity)}',
|
||||
style: const TextStyle(
|
||||
fontSize: 10,
|
||||
color: Color(0xFF9CA3AF),
|
||||
color: AppColors.textSecondaryLight,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -974,7 +975,7 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
child: const Icon(
|
||||
Icons.more_vert,
|
||||
size: 16,
|
||||
color: Color(0xFF6B7280),
|
||||
color: AppColors.textSecondaryLight,
|
||||
),
|
||||
),
|
||||
),
|
||||
@@ -1040,15 +1041,15 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
Color _getStatusColor(String status) {
|
||||
switch (status) {
|
||||
case 'Actif':
|
||||
return const Color(0xFF10B981);
|
||||
return AppColors.success;
|
||||
case 'Inactif':
|
||||
return const Color(0xFF6B7280);
|
||||
return AppColors.textSecondaryLight;
|
||||
case 'Suspendu':
|
||||
return const Color(0xFFDC2626);
|
||||
return AppColors.error;
|
||||
case 'En attente':
|
||||
return const Color(0xFFF59E0B);
|
||||
return AppColors.warning;
|
||||
default:
|
||||
return const Color(0xFF6B7280);
|
||||
return AppColors.textSecondaryLight;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1056,30 +1057,30 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
Color _getRoleColor(String role) {
|
||||
switch (role) {
|
||||
case 'Super Administrateur':
|
||||
return const Color(0xFF7C3AED);
|
||||
return AppColors.brandGreen;
|
||||
case 'Administrateur Org':
|
||||
return const Color(0xFF6366F1);
|
||||
return AppColors.primaryGreen;
|
||||
case 'Gestionnaire RH':
|
||||
return const Color(0xFF0EA5E9);
|
||||
return AppColors.info;
|
||||
case 'Modérateur':
|
||||
return const Color(0xFF059669);
|
||||
return AppColors.brandGreenLight;
|
||||
case 'Membre Actif':
|
||||
return const Color(0xFF6C5CE7);
|
||||
return AppColors.primaryGreen;
|
||||
case 'Consultant':
|
||||
return const Color(0xFFF59E0B);
|
||||
return AppColors.warning;
|
||||
case 'Membre Simple':
|
||||
return const Color(0xFF6B7280);
|
||||
return AppColors.textSecondaryLight;
|
||||
default:
|
||||
return const Color(0xFF6B7280);
|
||||
return AppColors.textSecondaryLight;
|
||||
}
|
||||
}
|
||||
|
||||
/// Obtient la couleur selon le score de contribution
|
||||
Color _getScoreColor(int score) {
|
||||
if (score >= 90) return const Color(0xFF10B981);
|
||||
if (score >= 70) return const Color(0xFF0EA5E9);
|
||||
if (score >= 50) return const Color(0xFFF59E0B);
|
||||
return const Color(0xFFDC2626);
|
||||
if (score >= 90) return AppColors.success;
|
||||
if (score >= 70) return AppColors.brandGreenLight;
|
||||
if (score >= 50) return AppColors.warning;
|
||||
return AppColors.error;
|
||||
}
|
||||
|
||||
/// Formate une date
|
||||
@@ -1174,7 +1175,7 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Messagerie groupée à venir. Utilisez l\'action « Message » sur un membre.'),
|
||||
backgroundColor: Color(0xFF6C5CE7),
|
||||
backgroundColor: AppColors.primaryGreen,
|
||||
),
|
||||
);
|
||||
},
|
||||
@@ -1190,7 +1191,7 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
const SnackBar(
|
||||
content: Text('Export des membres en cours...'),
|
||||
backgroundColor: Color(0xFF10B981),
|
||||
backgroundColor: AppColors.success,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -1234,7 +1235,7 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('Message à ${member['name']} à implémenter'),
|
||||
backgroundColor: const Color(0xFF0EA5E9),
|
||||
backgroundColor: AppColors.info,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -1257,7 +1258,7 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
ScaffoldMessenger.of(context).showSnackBar(
|
||||
SnackBar(
|
||||
content: Text('${member['name']} supprimé'),
|
||||
backgroundColor: const Color(0xFFDC2626),
|
||||
backgroundColor: AppColors.error,
|
||||
),
|
||||
);
|
||||
},
|
||||
@@ -1413,13 +1414,13 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
Container(
|
||||
padding: const EdgeInsets.all(24),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF6C5CE7).withOpacity(0.1),
|
||||
color: AppColors.primaryGreen.withOpacity(0.1),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(
|
||||
Icons.people_outline,
|
||||
size: 48,
|
||||
color: Color(0xFF6C5CE7),
|
||||
color: AppColors.primaryGreen,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
@@ -1428,7 +1429,7 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
style: TextStyle(
|
||||
fontSize: 18,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF374151),
|
||||
color: AppColors.textPrimaryLight,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
@@ -1437,7 +1438,7 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
? 'Aucun membre ne correspond à votre recherche'
|
||||
: 'Aucun membre ne correspond aux filtres sélectionnés',
|
||||
style: const TextStyle(
|
||||
color: Color(0xFF6B7280),
|
||||
color: AppColors.textSecondaryLight,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
@@ -1454,7 +1455,7 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
icon: const Icon(Icons.refresh),
|
||||
label: const Text('Réinitialiser les filtres'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFF6C5CE7),
|
||||
backgroundColor: AppColors.primaryGreen,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
),
|
||||
@@ -1667,7 +1668,7 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
width: 24,
|
||||
height: 24,
|
||||
decoration: BoxDecoration(
|
||||
color: index < 3 ? const Color(0xFFF59E0B) : const Color(0xFF6B7280),
|
||||
color: index < 3 ? AppColors.warning : AppColors.textSecondaryLight,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Center(
|
||||
@@ -1707,7 +1708,7 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
member['role'],
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: Color(0xFF6B7280),
|
||||
color: AppColors.textSecondaryLight,
|
||||
),
|
||||
),
|
||||
],
|
||||
@@ -1890,7 +1891,7 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
icon: const Icon(Icons.edit),
|
||||
label: const Text('Modifier'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: const Color(0xFF6C5CE7),
|
||||
backgroundColor: AppColors.primaryGreen,
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
),
|
||||
@@ -1929,7 +1930,7 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.w600,
|
||||
color: Color(0xFF374151),
|
||||
color: AppColors.textPrimaryLight,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
@@ -1948,7 +1949,7 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
Icon(
|
||||
icon,
|
||||
size: 20,
|
||||
color: const Color(0xFF6B7280),
|
||||
color: AppColors.textSecondaryLight,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
@@ -1959,7 +1960,7 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
label,
|
||||
style: const TextStyle(
|
||||
fontSize: 12,
|
||||
color: Color(0xFF6B7280),
|
||||
color: AppColors.textSecondaryLight,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
@@ -1967,7 +1968,7 @@ class _MembersPageState extends State<MembersPage> with TickerProviderStateMixin
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
fontWeight: FontWeight.w500,
|
||||
color: Color(0xFF374151),
|
||||
color: AppColors.textPrimaryLight,
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -79,7 +79,7 @@ class _MembersPageWithDataAndPaginationState extends State<MembersPageWithDataAn
|
||||
final pendingCount = widget.members.where((m) => m['status'] == 'En attente').length;
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: UnionFlowColors.surface,
|
||||
border: Border(
|
||||
@@ -100,10 +100,10 @@ class _MembersPageWithDataAndPaginationState extends State<MembersPageWithDataAn
|
||||
|
||||
Widget _buildStatBadge(String label, String value, Color color) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 8),
|
||||
padding: const EdgeInsets.symmetric(vertical: 8, horizontal: 8),
|
||||
decoration: BoxDecoration(
|
||||
color: color.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
border: Border.all(color: color.withOpacity(0.3), width: 1),
|
||||
),
|
||||
child: Column(
|
||||
@@ -118,7 +118,7 @@ class _MembersPageWithDataAndPaginationState extends State<MembersPageWithDataAn
|
||||
|
||||
Widget _buildSearchAndFilters() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: UnionFlowColors.surface,
|
||||
border: Border(bottom: BorderSide(color: UnionFlowColors.border.withOpacity(0.5), width: 1)),
|
||||
@@ -192,10 +192,10 @@ class _MembersPageWithDataAndPaginationState extends State<MembersPageWithDataAn
|
||||
return GestureDetector(
|
||||
onTap: () => setState(() => _filterStatus = label),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
|
||||
decoration: BoxDecoration(
|
||||
color: isSelected ? UnionFlowColors.unionGreen : UnionFlowColors.surface,
|
||||
borderRadius: BorderRadius.circular(20),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: isSelected ? UnionFlowColors.unionGreen : UnionFlowColors.border, width: 1),
|
||||
),
|
||||
child: Text(
|
||||
@@ -225,9 +225,9 @@ class _MembersPageWithDataAndPaginationState extends State<MembersPageWithDataAn
|
||||
onRefresh: () async => widget.onRefresh(),
|
||||
color: UnionFlowColors.unionGreen,
|
||||
child: ListView.separated(
|
||||
padding: const EdgeInsets.all(16),
|
||||
padding: const EdgeInsets.all(12),
|
||||
itemCount: filtered.length,
|
||||
separatorBuilder: (_, __) => const SizedBox(height: 12),
|
||||
separatorBuilder: (_, __) => const SizedBox(height: 6),
|
||||
itemBuilder: (context, index) => _buildMemberCard(filtered[index]),
|
||||
),
|
||||
);
|
||||
@@ -237,23 +237,22 @@ class _MembersPageWithDataAndPaginationState extends State<MembersPageWithDataAn
|
||||
return GestureDetector(
|
||||
onTap: () => _showMemberDetails(member),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(16),
|
||||
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 7),
|
||||
decoration: BoxDecoration(
|
||||
color: UnionFlowColors.surface,
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
boxShadow: UnionFlowColors.softShadow,
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(color: UnionFlowColors.border.withOpacity(0.3), width: 1),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 48,
|
||||
height: 48,
|
||||
width: 32,
|
||||
height: 32,
|
||||
decoration: const BoxDecoration(gradient: UnionFlowColors.primaryGradient, shape: BoxShape.circle),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
member['initiales'] ?? '??',
|
||||
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w700, fontSize: 18),
|
||||
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w700, fontSize: 13),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
@@ -322,14 +321,14 @@ class _MembersPageWithDataAndPaginationState extends State<MembersPageWithDataAn
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(24),
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: const BoxDecoration(color: UnionFlowColors.unionGreenPale, shape: BoxShape.circle),
|
||||
child: const Icon(Icons.people_outline, size: 64, color: UnionFlowColors.unionGreen),
|
||||
child: const Icon(Icons.people_outline, size: 40, color: UnionFlowColors.unionGreen),
|
||||
),
|
||||
const SizedBox(height: 24),
|
||||
const SizedBox(height: 12),
|
||||
const Text(
|
||||
'Aucun membre trouvé',
|
||||
style: TextStyle(fontSize: 16, fontWeight: FontWeight.w700, color: UnionFlowColors.textPrimary),
|
||||
style: TextStyle(fontSize: 14, fontWeight: FontWeight.w700, color: UnionFlowColors.textPrimary),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
@@ -389,40 +388,40 @@ class _MembersPageWithDataAndPaginationState extends State<MembersPageWithDataAn
|
||||
context: context,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (context) => Container(
|
||||
padding: const EdgeInsets.all(24),
|
||||
padding: const EdgeInsets.all(16),
|
||||
decoration: const BoxDecoration(
|
||||
color: UnionFlowColors.surface,
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(16)),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Container(
|
||||
width: 80,
|
||||
height: 80,
|
||||
width: 56,
|
||||
height: 56,
|
||||
decoration: const BoxDecoration(gradient: UnionFlowColors.primaryGradient, shape: BoxShape.circle),
|
||||
alignment: Alignment.center,
|
||||
child: Text(
|
||||
member['initiales'] ?? '??',
|
||||
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w900, fontSize: 32),
|
||||
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w900, fontSize: 22),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
member['name'] ?? '',
|
||||
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w700, color: UnionFlowColors.textPrimary),
|
||||
style: const TextStyle(fontSize: 14, fontWeight: FontWeight.w700, color: UnionFlowColors.textPrimary),
|
||||
),
|
||||
const SizedBox(height: 4),
|
||||
Text(
|
||||
member['role'] ?? '',
|
||||
style: const TextStyle(fontSize: 13, color: UnionFlowColors.textSecondary),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
const SizedBox(height: 12),
|
||||
_buildInfoRow(Icons.email_outlined, member['email'] ?? 'Non fourni'),
|
||||
_buildInfoRow(Icons.phone_outlined, member['phone'] ?? 'Non fourni'),
|
||||
_buildInfoRow(Icons.location_on_outlined, member['location'] ?? 'Non renseigné'),
|
||||
_buildInfoRow(Icons.work_outline, member['department'] ?? 'Aucun département'),
|
||||
const SizedBox(height: 24),
|
||||
const SizedBox(height: 12),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -22,21 +22,23 @@ final _getIt = GetIt.instance;
|
||||
|
||||
/// Wrapper qui fournit le BLoC à la page des membres
|
||||
class MembersPageWrapper extends StatelessWidget {
|
||||
const MembersPageWrapper({super.key});
|
||||
final String? organisationId;
|
||||
|
||||
const MembersPageWrapper({super.key, this.organisationId});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
AppLogger.info('MembersPageWrapper: Création du BlocProvider');
|
||||
|
||||
|
||||
return BlocProvider<MembresBloc>(
|
||||
create: (context) {
|
||||
AppLogger.info('MembresPageWrapper: Initialisation du MembresBloc');
|
||||
final bloc = _getIt<MembresBloc>();
|
||||
// Charger les membres au démarrage
|
||||
bloc.add(const LoadMembres());
|
||||
bloc.add(LoadMembres(organisationId: organisationId));
|
||||
return bloc;
|
||||
},
|
||||
child: const MembersPageConnected(),
|
||||
child: MembersPageConnected(organisationId: organisationId),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -45,15 +47,59 @@ class MembersPageWrapper extends StatelessWidget {
|
||||
///
|
||||
/// Cette page gère les états du BLoC et affiche l'UI appropriée
|
||||
class MembersPageConnected extends StatelessWidget {
|
||||
const MembersPageConnected({super.key});
|
||||
final String? organisationId;
|
||||
|
||||
const MembersPageConnected({super.key, this.organisationId});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocListener<MembresBloc, MembresState>(
|
||||
listener: (context, state) {
|
||||
// Après création : recharger la liste
|
||||
// Après création : afficher le mot de passe temporaire si disponible, puis recharger
|
||||
if (state is MembreCreated) {
|
||||
context.read<MembresBloc>().add(const LoadMembres(refresh: true));
|
||||
final motDePasse = state.membre.motDePasseTemporaire;
|
||||
if (motDePasse != null && motDePasse.isNotEmpty) {
|
||||
showDialog<void>(
|
||||
context: context,
|
||||
barrierDismissible: false,
|
||||
builder: (_) => AlertDialog(
|
||||
title: const Text('Compte créé avec succès'),
|
||||
content: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Le membre ${state.membre.nomComplet} a été créé.'),
|
||||
const SizedBox(height: 12),
|
||||
const Text(
|
||||
'Mot de passe temporaire :',
|
||||
style: TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
SelectableText(
|
||||
motDePasse,
|
||||
style: const TextStyle(
|
||||
fontSize: 18,
|
||||
fontFamily: 'monospace',
|
||||
letterSpacing: 2,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
const Text(
|
||||
'Communiquez ce mot de passe au membre. Il devra le changer à sa première connexion.',
|
||||
style: TextStyle(fontSize: 12, color: Colors.grey),
|
||||
),
|
||||
],
|
||||
),
|
||||
actions: [
|
||||
ElevatedButton(
|
||||
onPressed: () => Navigator.of(_).pop(),
|
||||
child: const Text('OK'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
context.read<MembresBloc>().add(LoadMembres(refresh: true, organisationId: organisationId));
|
||||
}
|
||||
|
||||
// Gestion des erreurs avec SnackBar
|
||||
@@ -67,7 +113,7 @@ class MembersPageConnected extends StatelessWidget {
|
||||
label: 'Réessayer',
|
||||
textColor: Colors.white,
|
||||
onPressed: () {
|
||||
context.read<MembresBloc>().add(const LoadMembres());
|
||||
context.read<MembresBloc>().add(LoadMembres(organisationId: organisationId));
|
||||
},
|
||||
),
|
||||
),
|
||||
@@ -134,19 +180,23 @@ class MembersPageConnected extends StatelessWidget {
|
||||
totalPages: state.totalPages,
|
||||
onPageChanged: (newPage, recherche) {
|
||||
AppLogger.userAction('Load page', data: {'page': newPage});
|
||||
context.read<MembresBloc>().add(LoadMembres(page: newPage, recherche: recherche));
|
||||
context.read<MembresBloc>().add(LoadMembres(page: newPage, recherche: recherche, organisationId: organisationId));
|
||||
},
|
||||
onRefresh: () {
|
||||
AppLogger.userAction('Refresh membres');
|
||||
context.read<MembresBloc>().add(const LoadMembres(refresh: true));
|
||||
context.read<MembresBloc>().add(LoadMembres(refresh: true, organisationId: organisationId));
|
||||
},
|
||||
onSearch: (query) {
|
||||
context.read<MembresBloc>().add(LoadMembres(page: 0, recherche: query));
|
||||
context.read<MembresBloc>().add(LoadMembres(page: 0, recherche: query, organisationId: organisationId));
|
||||
},
|
||||
onAddMember: () async {
|
||||
final bloc = context.read<MembresBloc>();
|
||||
await showDialog<void>(
|
||||
context: context,
|
||||
builder: (_) => const AddMemberDialog(),
|
||||
builder: (_) => BlocProvider.value(
|
||||
value: bloc,
|
||||
child: const AddMemberDialog(),
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
@@ -160,7 +210,7 @@ class MembersPageConnected extends StatelessWidget {
|
||||
child: NetworkErrorWidget(
|
||||
onRetry: () {
|
||||
AppLogger.userAction('Retry load membres after network error');
|
||||
context.read<MembresBloc>().add(const LoadMembres());
|
||||
context.read<MembresBloc>().add(LoadMembres(organisationId: organisationId));
|
||||
},
|
||||
),
|
||||
);
|
||||
@@ -175,7 +225,7 @@ class MembersPageConnected extends StatelessWidget {
|
||||
message: state.message,
|
||||
onRetry: () {
|
||||
AppLogger.userAction('Retry load membres after error');
|
||||
context.read<MembresBloc>().add(const LoadMembres());
|
||||
context.read<MembresBloc>().add(LoadMembres(organisationId: organisationId));
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user