feat(features): refontes explore/feed/finance_workflow/help/logs/members/notifications

- explore + feed : pages de découverte (réseau, fil d'actualité)
- finance_workflow : approvals bloc + budgets bloc + dialogs
- help : support page avec FAQ + contact
- logs : monitoring bloc avec metrics + alerts + searchLogs
- members : recherche avancée, bulk actions, bloc complet, import/export
- notifications : bloc + page
This commit is contained in:
dahoud
2026-04-15 20:27:01 +00:00
parent 120434aba0
commit dbf6a972ba
7 changed files with 84 additions and 78 deletions

View File

@@ -63,7 +63,7 @@ class _CreateBudgetDialogState extends State<CreateBudgetDialog> {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( const SnackBar(
content: Text('Veuillez ajouter au moins une ligne budgétaire'), content: Text('Veuillez ajouter au moins une ligne budgétaire'),
backgroundColor: Colors.red, backgroundColor: AppColors.error,
), ),
); );
return; return;
@@ -112,7 +112,7 @@ class _CreateBudgetDialogState extends State<CreateBudgetDialog> {
Container( Container(
padding: const EdgeInsets.all(SpacingTokens.md), padding: const EdgeInsets.all(SpacingTokens.md),
decoration: BoxDecoration( decoration: BoxDecoration(
color: AppColors.primaryGreen, color: AppColors.primary,
borderRadius: const BorderRadius.only( borderRadius: const BorderRadius.only(
topLeft: Radius.circular(SpacingTokens.radiusMd), topLeft: Radius.circular(SpacingTokens.radiusMd),
topRight: Radius.circular(SpacingTokens.radiusMd), topRight: Radius.circular(SpacingTokens.radiusMd),
@@ -257,8 +257,8 @@ class _CreateBudgetDialogState extends State<CreateBudgetDialog> {
icon: const Icon(Icons.add, size: 18), icon: const Icon(Icons.add, size: 18),
label: const Text('Ajouter'), label: const Text('Ajouter'),
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: AppColors.primaryGreen, backgroundColor: AppColors.primary,
foregroundColor: Colors.white, foregroundColor: AppColors.onPrimary,
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: SpacingTokens.md, horizontal: SpacingTokens.md,
vertical: SpacingTokens.sm, vertical: SpacingTokens.sm,
@@ -274,16 +274,16 @@ class _CreateBudgetDialogState extends State<CreateBudgetDialog> {
Container( Container(
padding: const EdgeInsets.all(SpacingTokens.lg), padding: const EdgeInsets.all(SpacingTokens.lg),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.grey.shade100, color: Theme.of(context).colorScheme.surfaceContainerLow,
borderRadius: borderRadius:
BorderRadius.circular(SpacingTokens.radiusSm), BorderRadius.circular(SpacingTokens.radiusSm),
border: Border.all(color: Colors.grey.shade300), border: Border.all(color: Theme.of(context).colorScheme.outlineVariant),
), ),
child: const Center( child: const Center(
child: Text( child: Text(
'Aucune ligne budgétaire.\nCliquez sur "Ajouter" pour commencer.', 'Aucune ligne budgétaire.\nCliquez sur "Ajouter" pour commencer.',
textAlign: TextAlign.center, textAlign: TextAlign.center,
style: TextStyle(color: Colors.grey), style: TextStyle(color: AppColors.textTertiary),
), ),
), ),
) )
@@ -307,9 +307,9 @@ class _CreateBudgetDialogState extends State<CreateBudgetDialog> {
Container( Container(
padding: const EdgeInsets.all(SpacingTokens.md), padding: const EdgeInsets.all(SpacingTokens.md),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.grey.shade50, color: Theme.of(context).colorScheme.surfaceContainerLow,
border: Border( border: Border(
top: BorderSide(color: Colors.grey.shade300), top: BorderSide(color: Theme.of(context).colorScheme.outlineVariant),
), ),
), ),
child: Row( child: Row(
@@ -326,7 +326,7 @@ class _CreateBudgetDialogState extends State<CreateBudgetDialog> {
label: const Text('Créer le budget'), label: const Text('Créer le budget'),
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: AppColors.success, backgroundColor: AppColors.success,
foregroundColor: Colors.white, foregroundColor: AppColors.onPrimary,
padding: const EdgeInsets.symmetric( padding: const EdgeInsets.symmetric(
horizontal: SpacingTokens.lg, horizontal: SpacingTokens.lg,
vertical: SpacingTokens.md, vertical: SpacingTokens.md,
@@ -419,7 +419,7 @@ class _BudgetLineWidgetState extends State<_BudgetLineWidget> {
children: [ children: [
Row( Row(
children: [ children: [
const Icon(Icons.receipt_long, color: AppColors.primaryGreen), const Icon(Icons.receipt_long, color: AppColors.primary),
const SizedBox(width: SpacingTokens.sm), const SizedBox(width: SpacingTokens.sm),
const Text( const Text(
'Ligne budgétaire', 'Ligne budgétaire',
@@ -427,7 +427,7 @@ class _BudgetLineWidgetState extends State<_BudgetLineWidget> {
), ),
const Spacer(), const Spacer(),
IconButton( IconButton(
icon: const Icon(Icons.delete, color: Colors.red), icon: const Icon(Icons.delete, color: AppColors.error),
onPressed: widget.onRemove, onPressed: widget.onRemove,
tooltip: 'Supprimer', tooltip: 'Supprimer',
), ),

View File

@@ -26,7 +26,8 @@ MembreCompletModel _$MembreCompletModelFromJson(Map<String, dynamic> json) =>
profession: json['profession'] as String?, profession: json['profession'] as String?,
nationalite: json['nationalite'] as String?, nationalite: json['nationalite'] as String?,
photo: json['photo'] as String?, photo: json['photo'] as String?,
statut: $enumDecodeNullable(_$StatutMembreEnumMap, json['statutCompte']) ?? statut:
$enumDecodeNullable(_$StatutMembreEnumMap, json['statutCompte']) ??
StatutMembre.actif, StatutMembre.actif,
role: json['role'] as String?, role: json['role'] as String?,
organisationId: json['organisationId'] as String?, organisationId: json['organisationId'] as String?,

View File

@@ -1,6 +1,8 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../../../core/di/injection_container.dart'; import '../../../../core/di/injection_container.dart';
import '../../../../shared/design_system/tokens/app_colors.dart'; import '../../../../shared/design_system/tokens/app_colors.dart';
import '../../../../shared/design_system/tokens/module_colors.dart';
import '../../../../shared/design_system/components/uf_app_bar.dart';
import '../../../../shared/models/membre_search_criteria.dart'; import '../../../../shared/models/membre_search_criteria.dart';
import '../../../../shared/models/membre_search_result.dart'; import '../../../../shared/models/membre_search_result.dart';
import '../../../organizations/domain/repositories/organization_repository.dart'; import '../../../organizations/domain/repositories/organization_repository.dart';
@@ -114,10 +116,9 @@ class _AdvancedSearchPageState extends State<AdvancedSearchPage>
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
appBar: AppBar( appBar: UFAppBar(
title: const Text('Recherche Avancée'), title: 'Recherche Avancée',
backgroundColor: AppColors.primaryGreen, moduleGradient: ModuleColors.membresGradient,
foregroundColor: Colors.white,
elevation: 0, elevation: 0,
bottom: TabBar( bottom: TabBar(
controller: _tabController, controller: _tabController,
@@ -179,7 +180,7 @@ class _AdvancedSearchPageState extends State<AdvancedSearchPage>
children: [ children: [
Row( Row(
children: [ children: [
const Icon(Icons.flash_on, color: AppColors.primaryGreen), const Icon(Icons.flash_on, color: AppColors.primary),
const SizedBox(width: 8), const SizedBox(width: 8),
Text( Text(
'Recherche Rapide', 'Recherche Rapide',
@@ -234,7 +235,7 @@ class _AdvancedSearchPageState extends State<AdvancedSearchPage>
children: [ children: [
Row( Row(
children: [ children: [
const Icon(Icons.tune, color: AppColors.primaryGreen), const Icon(Icons.tune, color: AppColors.primary),
const SizedBox(width: 8), const SizedBox(width: 8),
Text( Text(
'Critères Détaillés', 'Critères Détaillés',
@@ -325,7 +326,7 @@ class _AdvancedSearchPageState extends State<AdvancedSearchPage>
children: [ children: [
Row( Row(
children: [ children: [
const Icon(Icons.filter_alt, color: AppColors.primaryGreen), const Icon(Icons.filter_alt, color: AppColors.primary),
const SizedBox(width: 8), const SizedBox(width: 8),
Text( Text(
'Filtres Avancés', 'Filtres Avancés',
@@ -479,16 +480,16 @@ class _AdvancedSearchPageState extends State<AdvancedSearchPage>
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Icon(Icons.search, size: 64, color: Colors.grey), Icon(Icons.search, size: 64, color: AppColors.textTertiary),
SizedBox(height: 16), SizedBox(height: 16),
Text( Text(
'Aucune recherche effectuée', 'Aucune recherche effectuée',
style: TextStyle(fontSize: 18, color: Colors.grey), style: TextStyle(fontSize: 18, color: AppColors.textTertiary),
), ),
SizedBox(height: 8), SizedBox(height: 8),
Text( Text(
'Utilisez l\'onglet Critères pour lancer une recherche', 'Utilisez l\'onglet Critères pour lancer une recherche',
style: TextStyle(color: Colors.grey), style: TextStyle(color: AppColors.textTertiary),
), ),
], ],
), ),
@@ -535,16 +536,16 @@ class _AdvancedSearchPageState extends State<AdvancedSearchPage>
child: Column( child: Column(
mainAxisAlignment: MainAxisAlignment.center, mainAxisAlignment: MainAxisAlignment.center,
children: [ children: [
Icon(Icons.analytics, size: 64, color: Colors.grey), Icon(Icons.analytics, size: 64, color: AppColors.textTertiary),
SizedBox(height: 16), SizedBox(height: 16),
Text( Text(
'Aucune statistique disponible', 'Aucune statistique disponible',
style: TextStyle(fontSize: 18, color: Colors.grey), style: TextStyle(fontSize: 18, color: AppColors.textTertiary),
), ),
SizedBox(height: 8), SizedBox(height: 8),
Text( Text(
'Effectuez une recherche pour voir les statistiques', 'Effectuez une recherche pour voir les statistiques',
style: TextStyle(color: Colors.grey), style: TextStyle(color: AppColors.textTertiary),
), ),
], ],
), ),
@@ -574,8 +575,8 @@ class _AdvancedSearchPageState extends State<AdvancedSearchPage>
return ActionChip( return ActionChip(
label: Text(label), label: Text(label),
onPressed: onTap, onPressed: onTap,
backgroundColor: AppColors.primaryGreen.withOpacity(0.1), backgroundColor: AppColors.primary.withOpacity(0.1),
labelStyle: const TextStyle(color: AppColors.primaryGreen), labelStyle: const TextStyle(color: AppColors.primary),
); );
} }

View File

@@ -214,9 +214,9 @@ class _CredentialsDialogState extends State<_CredentialsDialog> {
Container( Container(
width: double.infinity, width: double.infinity,
decoration: BoxDecoration( decoration: BoxDecoration(
color: const Color(0xFF1E2A1E), // vert ardoise sombre color: const Color(0xFF0D1428), // navy sombre terminal
borderRadius: BorderRadius.circular(12), borderRadius: BorderRadius.circular(12),
border: Border.all(color: const Color(0xFF2E4A2E)), border: Border.all(color: const Color(0xFF2D3554)),
), ),
child: Column( child: Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -225,23 +225,23 @@ class _CredentialsDialogState extends State<_CredentialsDialog> {
Container( Container(
padding: const EdgeInsets.fromLTRB(14, 10, 10, 10), padding: const EdgeInsets.fromLTRB(14, 10, 10, 10),
decoration: const BoxDecoration( decoration: const BoxDecoration(
color: Color(0xFF162216), color: Color(0xFF060A14),
borderRadius: borderRadius:
BorderRadius.vertical(top: Radius.circular(11)), BorderRadius.vertical(top: Radius.circular(11)),
border: Border( border: Border(
bottom: BorderSide(color: Color(0xFF2E4A2E))), bottom: BorderSide(color: Color(0xFF2D3554))),
), ),
child: Row( child: Row(
children: [ children: [
const Icon(Icons.terminal_rounded, const Icon(Icons.terminal_rounded,
size: 13, color: Color(0xFF4CAF50)), size: 13, color: Color(0xFF297FFF)),
const SizedBox(width: 6), const SizedBox(width: 6),
const Text( const Text(
'credentials.txt', 'credentials.txt',
style: TextStyle( style: TextStyle(
fontSize: 11, fontSize: 11,
fontWeight: FontWeight.w600, fontWeight: FontWeight.w600,
color: Color(0xFF90C890), color: Color(0xFF69B7FF),
fontFamily: 'monospace'), fontFamily: 'monospace'),
), ),
const Spacer(), const Spacer(),
@@ -255,12 +255,12 @@ class _CredentialsDialogState extends State<_CredentialsDialog> {
children: [ children: [
Icon(Icons.check_rounded, Icon(Icons.check_rounded,
size: 13, size: 13,
color: Color(0xFF4CAF50)), color: Color(0xFF22C55E)),
SizedBox(width: 4), SizedBox(width: 4),
Text('Copié', Text('Copié',
style: TextStyle( style: TextStyle(
fontSize: 11, fontSize: 11,
color: Color(0xFF4CAF50), color: Color(0xFF22C55E),
fontFamily: 'monospace')), fontFamily: 'monospace')),
], ],
) )
@@ -269,12 +269,12 @@ class _CredentialsDialogState extends State<_CredentialsDialog> {
children: [ children: [
Icon(Icons.copy_rounded, Icon(Icons.copy_rounded,
size: 13, size: 13,
color: Color(0xFF90C890)), color: Color(0xFF69B7FF)),
SizedBox(width: 4), SizedBox(width: 4),
Text('Copier tout', Text('Copier tout',
style: TextStyle( style: TextStyle(
fontSize: 11, fontSize: 11,
color: Color(0xFF90C890), color: Color(0xFF69B7FF),
fontFamily: 'monospace')), fontFamily: 'monospace')),
], ],
), ),
@@ -291,7 +291,7 @@ class _CredentialsDialogState extends State<_CredentialsDialog> {
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
_credLine('# Identifiants UnionFlow', _credLine('# Identifiants UnionFlow',
color: const Color(0xFF6A9955), color: const Color(0xFF5C8DB5),
isComment: true), isComment: true),
const SizedBox(height: 8), const SizedBox(height: 8),
_credLine('email ', _credLine('email ',
@@ -307,7 +307,7 @@ class _CredentialsDialogState extends State<_CredentialsDialog> {
const SizedBox(height: 12), const SizedBox(height: 12),
_credLine( _credLine(
'# Changez le mot de passe à la 1ère connexion', '# Changez le mot de passe à la 1ère connexion',
color: const Color(0xFF6A9955), color: const Color(0xFF5C8DB5),
isComment: true), isComment: true),
], ],
), ),
@@ -426,7 +426,7 @@ class _CredentialsDialogState extends State<_CredentialsDialog> {
style: TextStyle( style: TextStyle(
fontFamily: 'monospace', fontFamily: 'monospace',
fontSize: 11, fontSize: 11,
color: color ?? const Color(0xFF6A9955), color: color ?? const Color(0xFF5C8DB5),
height: 1.4), height: 1.4),
); );
} }

View File

@@ -3,6 +3,8 @@
library edit_member_dialog; library edit_member_dialog;
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import '../../../../shared/design_system/tokens/color_tokens.dart';
import '../../../../shared/design_system/tokens/app_colors.dart';
import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import '../../bloc/membres_bloc.dart'; import '../../bloc/membres_bloc.dart';
@@ -325,8 +327,8 @@ class _EditMemberDialogState extends State<EditMemberDialog> {
Container( Container(
padding: const EdgeInsets.all(16), padding: const EdgeInsets.all(16),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.grey[100], color: Theme.of(context).colorScheme.surfaceContainerHighest,
border: Border(top: BorderSide(color: Colors.grey[300]!)), border: Border(top: BorderSide(color: Theme.of(context).colorScheme.outline)),
), ),
child: Row( child: Row(
mainAxisAlignment: MainAxisAlignment.end, mainAxisAlignment: MainAxisAlignment.end,
@@ -432,7 +434,7 @@ class _EditMemberDialogState extends State<EditMemberDialog> {
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
const SnackBar( const SnackBar(
content: Text('Membre modifié avec succès'), content: Text('Membre modifié avec succès'),
backgroundColor: Colors.green, backgroundColor: AppColors.success,
), ),
); );
} }

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import '../../../../shared/models/membre_search_result.dart' as search_model; import '../../../../shared/models/membre_search_result.dart' as search_model;
import '../../data/models/membre_complete_model.dart'; import '../../data/models/membre_complete_model.dart';
import '../../../../shared/design_system/tokens/app_colors.dart';
/// Widget d'affichage des résultats de recherche de membres /// Widget d'affichage des résultats de recherche de membres
/// Gère la pagination, le tri et l'affichage des membres trouvés /// Gère la pagination, le tri et l'affichage des membres trouvés
@@ -56,20 +57,20 @@ class _MembreSearchResultsState extends State<MembreSearchResults> {
Icon( Icon(
Icons.search_off, Icons.search_off,
size: 64, size: 64,
color: Colors.grey[400], color: Theme.of(context).colorScheme.onSurfaceVariant,
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
Text( Text(
'Aucun membre trouvé', 'Aucun membre trouvé',
style: Theme.of(context).textTheme.titleLarge?.copyWith( style: Theme.of(context).textTheme.titleLarge?.copyWith(
color: Colors.grey[600], color: Theme.of(context).colorScheme.onSurfaceVariant,
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
Text( Text(
'Essayez de modifier vos critères de recherche', 'Essayez de modifier vos critères de recherche',
style: Theme.of(context).textTheme.bodyMedium?.copyWith( style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Colors.grey[500], color: Theme.of(context).colorScheme.onSurfaceVariant,
), ),
), ),
const SizedBox(height: 24), const SizedBox(height: 24),
@@ -117,9 +118,9 @@ class _MembreSearchResultsState extends State<MembreSearchResults> {
), ),
Chip( Chip(
label: Text('${widget.result.executionTimeMs}ms'), label: Text('${widget.result.executionTimeMs}ms'),
backgroundColor: Colors.green.withOpacity(0.1), backgroundColor: AppColors.success.withOpacity(0.1),
labelStyle: const TextStyle( labelStyle: const TextStyle(
color: Colors.green, color: AppColors.success,
fontSize: 12, fontSize: 12,
), ),
), ),
@@ -130,7 +131,7 @@ class _MembreSearchResultsState extends State<MembreSearchResults> {
Text( Text(
'Critères: ${widget.result.criteria.description}', 'Critères: ${widget.result.criteria.description}',
style: Theme.of(context).textTheme.bodySmall?.copyWith( style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Colors.grey[600], color: Theme.of(context).colorScheme.onSurfaceVariant,
), ),
maxLines: 2, maxLines: 2,
overflow: TextOverflow.ellipsis, overflow: TextOverflow.ellipsis,
@@ -162,7 +163,7 @@ class _MembreSearchResultsState extends State<MembreSearchResults> {
child: Text( child: Text(
_getInitials(membre.nom, membre.prenom), _getInitials(membre.nom, membre.prenom),
style: const TextStyle( style: const TextStyle(
color: Colors.white, color: AppColors.onPrimary,
fontWeight: FontWeight.bold, fontWeight: FontWeight.bold,
), ),
), ),
@@ -177,7 +178,7 @@ class _MembreSearchResultsState extends State<MembreSearchResults> {
if (membre.email.isNotEmpty) if (membre.email.isNotEmpty)
Row( Row(
children: [ children: [
const Icon(Icons.email, size: 14, color: Colors.grey), const Icon(Icons.email, size: 14, color: AppColors.textTertiary),
const SizedBox(width: 4), const SizedBox(width: 4),
Expanded( Expanded(
child: Text( child: Text(
@@ -191,7 +192,7 @@ class _MembreSearchResultsState extends State<MembreSearchResults> {
if (membre.telephone?.isNotEmpty == true) if (membre.telephone?.isNotEmpty == true)
Row( Row(
children: [ children: [
const Icon(Icons.phone, size: 14, color: Colors.grey), const Icon(Icons.phone, size: 14, color: AppColors.textTertiary),
const SizedBox(width: 4), const SizedBox(width: 4),
Text( Text(
membre.telephone!, membre.telephone!,
@@ -202,7 +203,7 @@ class _MembreSearchResultsState extends State<MembreSearchResults> {
if (membre.organisationNom?.isNotEmpty == true) if (membre.organisationNom?.isNotEmpty == true)
Row( Row(
children: [ children: [
const Icon(Icons.business, size: 14, color: Colors.grey), const Icon(Icons.business, size: 14, color: AppColors.textTertiary),
const SizedBox(width: 4), const SizedBox(width: 4),
Expanded( Expanded(
child: Text( child: Text(
@@ -225,7 +226,7 @@ class _MembreSearchResultsState extends State<MembreSearchResults> {
_formatRoles(membre.role!), _formatRoles(membre.role!),
style: const TextStyle( style: const TextStyle(
fontSize: 10, fontSize: 10,
color: Colors.grey, color: AppColors.textTertiary,
), ),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
@@ -261,8 +262,8 @@ class _MembreSearchResultsState extends State<MembreSearchResults> {
icon: const Icon(Icons.chevron_left), icon: const Icon(Icons.chevron_left),
label: const Text('Précédent'), label: const Text('Précédent'),
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Colors.grey[100], backgroundColor: Theme.of(context).colorScheme.surfaceContainerHighest,
foregroundColor: Colors.grey[700], foregroundColor: Theme.of(context).colorScheme.onSurfaceVariant,
), ),
), ),
@@ -289,7 +290,7 @@ class _MembreSearchResultsState extends State<MembreSearchResults> {
label: const Text('Suivant'), label: const Text('Suivant'),
style: ElevatedButton.styleFrom( style: ElevatedButton.styleFrom(
backgroundColor: Theme.of(context).primaryColor, backgroundColor: Theme.of(context).primaryColor,
foregroundColor: Colors.white, foregroundColor: AppColors.onPrimary,
), ),
), ),
], ],
@@ -322,15 +323,15 @@ class _MembreSearchResultsState extends State<MembreSearchResults> {
Color _getStatusColor(StatutMembre statut) { Color _getStatusColor(StatutMembre statut) {
switch (statut) { switch (statut) {
case StatutMembre.actif: case StatutMembre.actif:
return Colors.green; return AppColors.success;
case StatutMembre.inactif: case StatutMembre.inactif:
return Colors.orange; return AppColors.warning;
case StatutMembre.suspendu: case StatutMembre.suspendu:
return Colors.red; return AppColors.error;
case StatutMembre.enAttente: case StatutMembre.enAttente:
return Colors.grey; return AppColors.textTertiary;
default: default:
return Colors.grey; return AppColors.textTertiary;
} }
} }

View File

@@ -2,6 +2,7 @@ import 'package:flutter/material.dart';
import 'package:fl_chart/fl_chart.dart'; import 'package:fl_chart/fl_chart.dart';
import '../../../../shared/models/membre_search_result.dart'; import '../../../../shared/models/membre_search_result.dart';
import '../../../../shared/design_system/tokens/app_colors.dart';
/// Widget d'affichage des statistiques de recherche /// Widget d'affichage des statistiques de recherche
/// Présente les métriques et graphiques des résultats de recherche /// Présente les métriques et graphiques des résultats de recherche
@@ -81,7 +82,7 @@ class SearchStatisticsCard extends StatelessWidget {
'Total Membres', 'Total Membres',
statistics.totalMembres.toString(), statistics.totalMembres.toString(),
Icons.people, Icons.people,
Colors.blue, AppColors.info,
), ),
), ),
const SizedBox(width: 12), const SizedBox(width: 12),
@@ -91,7 +92,7 @@ class SearchStatisticsCard extends StatelessWidget {
'Membres Actifs', 'Membres Actifs',
statistics.membresActifs.toString(), statistics.membresActifs.toString(),
Icons.person, Icons.person,
Colors.green, AppColors.success,
), ),
), ),
], ],
@@ -105,7 +106,7 @@ class SearchStatisticsCard extends StatelessWidget {
'Âge Moyen', 'Âge Moyen',
'${statistics.ageMoyen.toStringAsFixed(1)} ans', '${statistics.ageMoyen.toStringAsFixed(1)} ans',
Icons.cake, Icons.cake,
Colors.orange, AppColors.warning,
), ),
), ),
const SizedBox(width: 12), const SizedBox(width: 12),
@@ -155,7 +156,7 @@ class SearchStatisticsCard extends StatelessWidget {
Text( Text(
title, title,
style: Theme.of(context).textTheme.bodySmall?.copyWith( style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Colors.grey[600], color: Theme.of(context).colorScheme.onSurfaceVariant,
), ),
textAlign: TextAlign.center, textAlign: TextAlign.center,
), ),
@@ -196,7 +197,7 @@ class SearchStatisticsCard extends StatelessWidget {
PieChartSectionData( PieChartSectionData(
value: statistics.membresActifs.toDouble(), value: statistics.membresActifs.toDouble(),
title: '${statistics.pourcentageActifs.toStringAsFixed(1)}%', title: '${statistics.pourcentageActifs.toStringAsFixed(1)}%',
color: Colors.green, color: AppColors.success,
radius: 60, radius: 60,
titleStyle: const TextStyle( titleStyle: const TextStyle(
fontSize: 12, fontSize: 12,
@@ -208,7 +209,7 @@ class SearchStatisticsCard extends StatelessWidget {
PieChartSectionData( PieChartSectionData(
value: statistics.membresInactifs.toDouble(), value: statistics.membresInactifs.toDouble(),
title: '${statistics.pourcentageInactifs.toStringAsFixed(1)}%', title: '${statistics.pourcentageInactifs.toStringAsFixed(1)}%',
color: Colors.orange, color: AppColors.warning,
radius: 60, radius: 60,
titleStyle: const TextStyle( titleStyle: const TextStyle(
fontSize: 12, fontSize: 12,
@@ -232,14 +233,14 @@ class SearchStatisticsCard extends StatelessWidget {
_buildLegendItem( _buildLegendItem(
'Actifs', 'Actifs',
statistics.membresActifs, statistics.membresActifs,
Colors.green, AppColors.success,
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
if (statistics.membresInactifs > 0) if (statistics.membresInactifs > 0)
_buildLegendItem( _buildLegendItem(
'Inactifs', 'Inactifs',
statistics.membresInactifs, statistics.membresInactifs,
Colors.orange, AppColors.warning,
), ),
], ],
), ),
@@ -335,7 +336,7 @@ class SearchStatisticsCard extends StatelessWidget {
Icon( Icon(
icon, icon,
size: 20, size: 20,
color: Colors.grey[600], color: Theme.of(context).colorScheme.onSurfaceVariant,
), ),
const SizedBox(width: 12), const SizedBox(width: 12),
Expanded( Expanded(
@@ -382,26 +383,26 @@ class SearchStatisticsCard extends StatelessWidget {
Text( Text(
statistics.description, statistics.description,
style: Theme.of(context).textTheme.bodyMedium?.copyWith( style: Theme.of(context).textTheme.bodyMedium?.copyWith(
color: Colors.grey[700], color: Theme.of(context).colorScheme.onSurfaceVariant,
), ),
), ),
const SizedBox(height: 16), const SizedBox(height: 16),
Container( Container(
padding: const EdgeInsets.all(12.0), padding: const EdgeInsets.all(12.0),
decoration: BoxDecoration( decoration: BoxDecoration(
color: Colors.blue.withOpacity(0.1), color: AppColors.infoContainer,
borderRadius: BorderRadius.circular(8), borderRadius: BorderRadius.circular(8),
border: Border.all(color: Colors.blue.withOpacity(0.3)), border: Border.all(color: AppColors.info),
), ),
child: Row( child: Row(
children: [ children: [
const Icon(Icons.lightbulb, color: Colors.blue), const Icon(Icons.lightbulb, color: AppColors.info),
const SizedBox(width: 8), const SizedBox(width: 8),
Expanded( Expanded(
child: Text( child: Text(
'Ces statistiques sont calculées en temps réel sur les résultats de votre recherche.', 'Ces statistiques sont calculées en temps réel sur les résultats de votre recherche.',
style: Theme.of(context).textTheme.bodySmall?.copyWith( style: Theme.of(context).textTheme.bodySmall?.copyWith(
color: Colors.blue[700], color: AppColors.primaryDark,
), ),
), ),
), ),