refactoring

This commit is contained in:
dahoud
2026-03-31 09:14:47 +00:00
parent 9bfffeeebe
commit 5383df6dcb
200 changed files with 11192 additions and 7063 deletions

View File

@@ -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,
),
);
}

View File

@@ -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,
),
),
],

View File

@@ -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),
],
),
),

View File

@@ -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));
},
),
);