diff --git a/lib/features/admin/presentation/pages/user_management_page.dart b/lib/features/admin/presentation/pages/user_management_page.dart index 116e06c..309ad7c 100644 --- a/lib/features/admin/presentation/pages/user_management_page.dart +++ b/lib/features/admin/presentation/pages/user_management_page.dart @@ -41,9 +41,10 @@ class _UserManagementViewState extends State<_UserManagementView> { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: AppColors.background, + backgroundColor: Theme.of(context).scaffoldBackgroundColor, appBar: UFAppBar( title: 'Gestion des utilisateurs', + moduleGradient: ModuleColors.systemeGradient, actions: [ IconButton( icon: const Icon(Icons.refresh, size: 20), @@ -51,7 +52,9 @@ class _UserManagementViewState extends State<_UserManagementView> { ), ], ), - body: Column( + body: SafeArea( + top: false, + child: Column( children: [ Padding( padding: const EdgeInsets.all(12), @@ -63,18 +66,18 @@ class _UserManagementViewState extends State<_UserManagementView> { prefixIcon: const Icon(Icons.search, size: 18), border: OutlineInputBorder( borderRadius: BorderRadius.circular(RadiusTokens.md), - borderSide: const BorderSide(color: AppColors.lightBorder), + borderSide: BorderSide(color: Theme.of(context).colorScheme.outline), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(RadiusTokens.md), - borderSide: const BorderSide(color: AppColors.lightBorder), + borderSide: BorderSide(color: Theme.of(context).colorScheme.outline), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(RadiusTokens.md), - borderSide: const BorderSide(color: AppColors.primaryGreen), + borderSide: BorderSide(color: Theme.of(context).colorScheme.primary), ), filled: true, - fillColor: AppColors.lightSurface, + fillColor: Theme.of(context).colorScheme.surfaceContainerHighest, contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), ), onSubmitted: (v) => context.read().add( @@ -131,6 +134,7 @@ class _UserManagementViewState extends State<_UserManagementView> { ), ], ), + ), ); } @@ -168,11 +172,11 @@ class _UserManagementViewState extends State<_UserManagementView> { ], ), ), - const Icon( + Icon( Icons.chevron_right, size: 16, - color: AppColors.textSecondaryLight, - ), + color: Theme.of(context).colorScheme.onSurfaceVariant, + ), ], ), ); diff --git a/lib/features/authentication/data/datasources/keycloak_auth_service.dart b/lib/features/authentication/data/datasources/keycloak_auth_service.dart index f0626cc..2aa12d8 100644 --- a/lib/features/authentication/data/datasources/keycloak_auth_service.dart +++ b/lib/features/authentication/data/datasources/keycloak_auth_service.dart @@ -167,9 +167,56 @@ class KeycloakAuthService { return null; } + /// Logout : révoque la session SSO côté Keycloak via le back-channel + /// (POST /logout silencieux, sans navigateur), puis purge le stockage local. + /// + /// Conforme OIDC RP-Initiated Logout. Ne lève jamais d'exception : la purge + /// locale est garantie même si Keycloak est injoignable. Le statut du + /// back-channel est tracé dans les logs pour diagnostic. Future logout() async { + final refresh = await _storage.read(key: _refreshK); + + if (refresh == null || refresh.isEmpty) { + AppLogger.info('KeycloakAuthService: no refresh token, skipping backchannel logout'); + } else { + try { + final response = await _dio.post( + KeycloakConfig.logoutEndpoint, + data: { + 'client_id': KeycloakConfig.clientId, + 'refresh_token': refresh, + }, + options: Options( + contentType: Headers.formUrlEncodedContentType, + // Accepte tout statut < 600 — on interprète nous-mêmes ci-dessous. + validateStatus: (_) => true, + ), + ); + final code = response.statusCode ?? 0; + if (code == 200 || code == 204) { + AppLogger.info('KeycloakAuthService: SSO session revoked at Keycloak (HTTP $code)'); + } else if (code == 400) { + // Refresh token déjà invalide côté Keycloak → idempotent, OK. + AppLogger.info('KeycloakAuthService: refresh token already invalid (HTTP 400) — session considered revoked'); + } else { + AppLogger.error( + 'KeycloakAuthService: backchannel logout returned HTTP $code — ' + 'SSO session may still be active. Body: ${response.data}', + ); + } + } catch (e, st) { + AppLogger.error( + 'KeycloakAuthService: backchannel logout network error — local logout will still proceed', + error: e, + stackTrace: st, + ); + } + } + + // Purge locale toujours effectuée — l'utilisateur "se sent" déconnecté + // immédiatement même si le serveur n'a pas pu être notifié. await _storage.deleteAll(); - AppLogger.info('KeycloakAuthService: session cleared'); + AppLogger.info('KeycloakAuthService: local session cleared'); } Future _saveTokens(Map data) async { diff --git a/lib/features/authentication/data/models/user_role.dart b/lib/features/authentication/data/models/user_role.dart index bee69d7..9f2fb9a 100644 --- a/lib/features/authentication/data/models/user_role.dart +++ b/lib/features/authentication/data/models/user_role.dart @@ -15,7 +15,7 @@ enum UserRole { level: 100, displayName: 'Super Administrateur', description: 'Accès complet système et multi-organisations', - color: 0xFF6C5CE7, // Violet sophistiqué + color: 0xFF7616E8, // Violet UnionFlow (super admin) permissions: _superAdminPermissions, ), @@ -25,7 +25,7 @@ enum UserRole { level: 80, displayName: 'Administrateur', description: 'Gestion complète de l\'organisation', - color: 0xFF0984E3, // Bleu corporate + color: 0xFF2563EB, // Bleu primaire UnionFlow permissions: _orgAdminPermissions, ), @@ -45,7 +45,7 @@ enum UserRole { level: 58, displayName: 'Consultant', description: 'Accès consultant et conseil', - color: 0xFF6C5CE7, // Violet + color: 0xFF5297FF, // Bleu intermédiaire (consultant) permissions: _consultantPermissions, ), @@ -55,7 +55,7 @@ enum UserRole { level: 52, displayName: 'Gestionnaire RH', description: 'Gestion des ressources humaines', - color: 0xFF0984E3, // Bleu + color: 0xFF1D4ED8, // Bleu foncé (RH) permissions: _hrManagerPermissions, ), @@ -65,7 +65,7 @@ enum UserRole { level: 40, displayName: 'Membre Actif', description: 'Participation active aux activités', - color: 0xFF00B894, // Vert communauté + color: 0xFF22C55E, // Vert succès (membre actif) permissions: _activeMemberPermissions, ), @@ -75,7 +75,7 @@ enum UserRole { level: 20, displayName: 'Membre', description: 'Accès aux informations de base', - color: 0xFF00CEC9, // Teal simple + color: 0xFF60A5FA, // Bleu clair (membre simple) permissions: _simpleMemberPermissions, ), @@ -85,7 +85,7 @@ enum UserRole { level: 0, displayName: 'Visiteur', description: 'Accès aux informations publiques', - color: 0xFF6C5CE7, // Indigo accueillant + color: 0xFF94A3B8, // Gris neutre (visiteur) permissions: _visitorPermissions, ); diff --git a/lib/features/contributions/presentation/pages/contributions_page.dart b/lib/features/contributions/presentation/pages/contributions_page.dart index 96b3bbb..eab5209 100644 --- a/lib/features/contributions/presentation/pages/contributions_page.dart +++ b/lib/features/contributions/presentation/pages/contributions_page.dart @@ -60,9 +60,10 @@ class _ContributionsPageState extends State @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: ColorTokens.background, + backgroundColor: Theme.of(context).scaffoldBackgroundColor, appBar: UFAppBar( title: 'Cotisations', + moduleGradient: ModuleColors.cotisationsGradient, actions: [ IconButton( icon: const Icon(Icons.bar_chart, size: 20), @@ -76,15 +77,17 @@ class _ContributionsPageState extends State bottom: TabBar( controller: _tabController, onTap: (_) => _loadContributions(), - labelColor: ColorTokens.onPrimary, - unselectedLabelColor: ColorTokens.onPrimary.withOpacity(0.7), - indicatorColor: ColorTokens.onPrimary, - labelStyle: AppTypography.badgeText.copyWith(fontWeight: FontWeight.bold), + isScrollable: true, + labelColor: Colors.white, + unselectedLabelColor: Colors.white70, + indicatorColor: Colors.white, + indicatorSize: TabBarIndicatorSize.label, + labelStyle: AppTypography.actionText.copyWith(fontSize: 10, fontWeight: FontWeight.bold), tabs: const [ - Tab(text: 'Toutes'), - Tab(text: 'Payées'), - Tab(text: 'Dues'), - Tab(text: 'Retard'), + Tab(child: Text('TOUTES')), + Tab(child: Text('PAYÉES')), + Tab(child: Text('DUES')), + Tab(child: Text('RETARD')), ], ), ), @@ -257,18 +260,19 @@ class _ContributionsPageState extends State } Widget _buildStatutBadge(ContributionStatus statut, bool enRetard) { + final scheme = Theme.of(context).colorScheme; if (enRetard && statut != ContributionStatus.payee) { - return const InfoBadge(text: 'RETARD', backgroundColor: Color(0xFFFFEBEB), textColor: ColorTokens.error); + return InfoBadge(text: 'RETARD', backgroundColor: scheme.errorContainer, textColor: ColorTokens.error); } switch (statut) { case ContributionStatus.payee: - return const InfoBadge(text: 'PAYÉE', backgroundColor: Color(0xFFE3F9E5), textColor: ColorTokens.success); + return InfoBadge(text: 'PAYÉE', backgroundColor: ColorTokens.successContainer, textColor: ColorTokens.success); case ContributionStatus.nonPayee: case ContributionStatus.enAttente: - return const InfoBadge(text: 'DUE', backgroundColor: Color(0xFFFFF4E5), textColor: ColorTokens.warning); + return InfoBadge(text: 'DUE', backgroundColor: ColorTokens.warningContainer, textColor: ColorTokens.warning); case ContributionStatus.partielle: - return const InfoBadge(text: 'PARTIELLE', backgroundColor: Color(0xFFE5F1FF), textColor: ColorTokens.info); + return InfoBadge(text: 'PARTIELLE', backgroundColor: ColorTokens.infoContainer, textColor: ColorTokens.info); case ContributionStatus.annulee: return InfoBadge.neutral('ANNULÉE'); default: @@ -279,7 +283,7 @@ class _ContributionsPageState extends State void _showContributionDetails(ContributionModel contribution) { showModalBottomSheet( context: context, - backgroundColor: ColorTokens.surface, + backgroundColor: Theme.of(context).colorScheme.surface, shape: const RoundedRectangleBorder(borderRadius: BorderRadius.vertical(top: Radius.circular(RadiusTokens.lg))), builder: (context) => Padding( padding: const EdgeInsets.all(SpacingTokens.xl), diff --git a/lib/features/contributions/presentation/pages/mes_statistiques_cotisations_page.dart b/lib/features/contributions/presentation/pages/mes_statistiques_cotisations_page.dart index 4420446..290db56 100644 --- a/lib/features/contributions/presentation/pages/mes_statistiques_cotisations_page.dart +++ b/lib/features/contributions/presentation/pages/mes_statistiques_cotisations_page.dart @@ -38,11 +38,10 @@ class _MesStatistiquesCotisationsPageState extends State( listener: (context, state) { @@ -158,7 +157,7 @@ class _MesStatistiquesCotisationsPageState extends State[]; if (paye > 0) { sections.add(PieChartSectionData( - color: AppColors.primaryGreen, + color: AppColors.primary, value: paye, title: 'Payé', radius: 60, @@ -345,7 +344,7 @@ class _MesStatistiquesCotisationsPageState extends State { enabled: false, // Lecture seule ) else - const Text('Impossible de récupérer votre profil', style: TextStyle(color: Colors.red)), + const Text('Impossible de récupérer votre profil', style: TextStyle(color: AppColors.error)), const SizedBox(height: 16), // Type de contribution @@ -225,7 +226,7 @@ class _CreateContributionDialogState extends State { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Profil non chargé'), - backgroundColor: Colors.red, + backgroundColor: AppColors.error, ), ); return; @@ -247,7 +248,7 @@ class _CreateContributionDialogState extends State { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Aucune organisation disponible. Le membre et l\'utilisateur connecté doivent être rattachés à une organisation.'), - backgroundColor: Colors.red, + backgroundColor: AppColors.error, ), ); setState(() => _isLoading = false); @@ -276,7 +277,7 @@ class _CreateContributionDialogState extends State { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Contribution créée avec succès'), - backgroundColor: Colors.green, + backgroundColor: AppColors.success, ), ); } diff --git a/lib/features/contributions/presentation/widgets/payment_dialog.dart b/lib/features/contributions/presentation/widgets/payment_dialog.dart index 3826ca1..8d87065 100644 --- a/lib/features/contributions/presentation/widgets/payment_dialog.dart +++ b/lib/features/contributions/presentation/widgets/payment_dialog.dart @@ -5,6 +5,8 @@ library payment_dialog; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import '../../../../shared/design_system/tokens/color_tokens.dart'; +import '../../../../shared/design_system/tokens/app_colors.dart'; import 'package:intl/intl.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:unionflow_mobile_apps/core/di/injection.dart'; @@ -65,8 +67,8 @@ class _PaymentDialogState extends State { // En-tête Container( padding: const EdgeInsets.all(16), - decoration: const BoxDecoration( - color: Color(0xFF10B981), + decoration: BoxDecoration( + color: ColorTokens.successLight, borderRadius: BorderRadius.only( topLeft: Radius.circular(4), topRight: Radius.circular(4), @@ -96,15 +98,16 @@ class _PaymentDialogState extends State { // Informations de la cotisation Container( padding: const EdgeInsets.all(16), - color: Colors.grey[100], + color: Theme.of(context).colorScheme.surfaceContainerHighest, child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( widget.cotisation.membreNomComplet, - style: const TextStyle( + style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, + color: Theme.of(context).colorScheme.onSurface, ), ), const SizedBox(height: 4), @@ -112,7 +115,7 @@ class _PaymentDialogState extends State { widget.cotisation.libellePeriode, style: TextStyle( fontSize: 14, - color: Colors.grey[600], + color: Theme.of(context).colorScheme.onSurfaceVariant, ), ), const SizedBox(height: 8), @@ -121,11 +124,14 @@ class _PaymentDialogState extends State { children: [ Text( 'Montant total:', - style: TextStyle(color: Colors.grey[600]), + style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant), ), Text( '${NumberFormat('#,###').format(widget.cotisation.montant)} ${widget.cotisation.devise}', - style: const TextStyle(fontWeight: FontWeight.bold), + style: TextStyle( + fontWeight: FontWeight.bold, + color: Theme.of(context).colorScheme.onSurface, + ), ), ], ), @@ -134,11 +140,11 @@ class _PaymentDialogState extends State { children: [ Text( 'Déjà payé:', - style: TextStyle(color: Colors.grey[600]), + style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant), ), Text( '${NumberFormat('#,###').format(widget.cotisation.montantPaye ?? 0)} ${widget.cotisation.devise}', - style: const TextStyle(color: Colors.green), + style: TextStyle(color: ColorTokens.success), ), ], ), @@ -147,13 +153,13 @@ class _PaymentDialogState extends State { children: [ Text( 'Restant:', - style: TextStyle(color: Colors.grey[600]), + style: TextStyle(color: Theme.of(context).colorScheme.onSurfaceVariant), ), Text( '${NumberFormat('#,###').format(widget.cotisation.montantRestant)} ${widget.cotisation.devise}', - style: const TextStyle( + style: TextStyle( fontWeight: FontWeight.bold, - color: Colors.red, + color: ColorTokens.error, ), ), ], @@ -297,8 +303,8 @@ class _PaymentDialogState extends State { Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: Colors.grey[100], - border: Border(top: BorderSide(color: Colors.grey[300]!)), + color: Theme.of(context).colorScheme.surfaceContainerHighest, + border: Border(top: BorderSide(color: Theme.of(context).colorScheme.outline)), ), child: Row( mainAxisAlignment: MainAxisAlignment.end, @@ -311,7 +317,7 @@ class _PaymentDialogState extends State { ElevatedButton( onPressed: _waveLoading ? null : _submitForm, style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF10B981), + backgroundColor: ColorTokens.successLight, foregroundColor: Colors.white, ), child: _waveLoading @@ -415,7 +421,7 @@ class _PaymentDialogState extends State { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Paiement enregistré avec succès'), - backgroundColor: Colors.green, + backgroundColor: ColorTokens.success, ), ); } @@ -426,7 +432,7 @@ class _PaymentDialogState extends State { final phone = _wavePhoneController.text.replaceAll(RegExp(r'\D'), ''); if (phone.length < 9) { ScaffoldMessenger.of(context).showSnackBar( - const SnackBar(content: Text('Indiquez votre numéro Wave (9 chiffres)'), backgroundColor: Colors.orange), + const SnackBar(content: Text('Indiquez votre numéro Wave (9 chiffres)'), backgroundColor: AppColors.warning), ); return; } @@ -453,7 +459,7 @@ class _PaymentDialogState extends State { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(result.message), - backgroundColor: Colors.green, + backgroundColor: AppColors.success, ), ); context.read().add(const LoadContributions()); @@ -462,7 +468,7 @@ class _PaymentDialogState extends State { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Wave: ${e.toString().replaceFirst('Exception: ', '')}'), - backgroundColor: Colors.red, + backgroundColor: AppColors.error, ), ); } finally { diff --git a/lib/features/dashboard/presentation/pages/advanced_dashboard_page.dart b/lib/features/dashboard/presentation/pages/advanced_dashboard_page.dart index a8d4628..3acd9e8 100644 --- a/lib/features/dashboard/presentation/pages/advanced_dashboard_page.dart +++ b/lib/features/dashboard/presentation/pages/advanced_dashboard_page.dart @@ -59,7 +59,7 @@ class _AdvancedDashboardPageState extends State return BlocProvider( create: (context) => _dashboardBloc, child: Scaffold( - backgroundColor: AppColors.lightBackground, + backgroundColor: Theme.of(context).scaffoldBackgroundColor, appBar: const UFAppBar( title: 'DASHBOARD AVANCÉ', ), @@ -92,7 +92,7 @@ class _AdvancedDashboardPageState extends State background: Container( decoration: BoxDecoration( gradient: LinearGradient( - colors: [AppColors.primaryGreen, AppColors.brandGreen], + colors: [AppColors.primary, AppColors.primaryDark], begin: Alignment.topLeft, end: Alignment.bottomRight, ), @@ -258,13 +258,13 @@ class _AdvancedDashboardPageState extends State return Container( decoration: BoxDecoration( color: Theme.of(context).cardColor, - border: Border(bottom: BorderSide(color: AppColors.lightBorder, width: 1)), + border: Border(bottom: BorderSide(color: AppColors.border, width: 1)), ), child: TabBar( controller: _tabController, - labelColor: AppColors.primaryGreen, - unselectedLabelColor: AppColors.textSecondaryLight, - indicatorColor: AppColors.primaryGreen, + labelColor: AppColors.primary, + unselectedLabelColor: AppColors.textSecondary, + indicatorColor: AppColors.primary, indicatorWeight: 3, labelStyle: AppTypography.actionText.copyWith(fontSize: 10, fontWeight: FontWeight.bold, letterSpacing: 1), tabs: const [ @@ -279,7 +279,7 @@ class _AdvancedDashboardPageState extends State Widget _buildOverviewTab() { return RefreshIndicator( onRefresh: () async => _refreshDashboardData(), - color: AppColors.primaryGreen, + color: AppColors.primary, child: SingleChildScrollView( padding: const EdgeInsets.all(12), child: Column( @@ -367,7 +367,7 @@ class _AdvancedDashboardPageState extends State 'Rapport Mensuel', 'Synthèse complète des activités du mois', Icons.calendar_month_outlined, - AppColors.primaryGreen, + AppColors.primary, ), const SizedBox(height: 12), _buildReportCard( @@ -458,7 +458,7 @@ class _AdvancedDashboardPageState extends State ], ), ), - const Icon(Icons.download_outlined, color: AppColors.textSecondaryLight, size: 18), + const Icon(Icons.download_outlined, color: AppColors.textSecondary, size: 18), ], ), ); @@ -469,7 +469,7 @@ class _AdvancedDashboardPageState extends State onPressed: () { // Actions rapides }, - backgroundColor: AppColors.primaryGreen, + backgroundColor: AppColors.primary, child: const Icon(Icons.add, color: Colors.white), ); } diff --git a/lib/features/dashboard/presentation/pages/role_dashboards/active_member_dashboard.dart b/lib/features/dashboard/presentation/pages/role_dashboards/active_member_dashboard.dart index b9a163b..e61eda6 100644 --- a/lib/features/dashboard/presentation/pages/role_dashboards/active_member_dashboard.dart +++ b/lib/features/dashboard/presentation/pages/role_dashboards/active_member_dashboard.dart @@ -17,8 +17,8 @@ class ActiveMemberDashboard extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: UnionFlowColors.background, - appBar: _buildAppBar(), + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + appBar: _buildAppBar(context), drawer: DashboardDrawer( onLogout: () => context.read().add(const AuthLogoutRequested()), ), @@ -229,10 +229,12 @@ class ActiveMemberDashboard extends StatelessWidget { ); } - PreferredSizeWidget _buildAppBar() { + PreferredSizeWidget _buildAppBar(BuildContext context) { return AppBar( - backgroundColor: UnionFlowColors.surface, + backgroundColor: Theme.of(context).colorScheme.surface, elevation: 0, + scrolledUnderElevation: 1, + shadowColor: Theme.of(context).colorScheme.outline.withOpacity(0.2), title: Row( children: [ Container( @@ -246,16 +248,16 @@ class ActiveMemberDashboard extends StatelessWidget { child: const Text('U', style: TextStyle(color: Colors.white, fontWeight: FontWeight.w900, fontSize: 18)), ), const SizedBox(width: 12), - const Column( + Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('UnionFlow', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w700, color: UnionFlowColors.textPrimary)), - Text('Membre Actif', style: TextStyle(fontSize: 11, fontWeight: FontWeight.w400, color: UnionFlowColors.textSecondary)), + Text('UnionFlow', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w700, color: Theme.of(context).colorScheme.onSurface)), + Text('Membre Actif', style: TextStyle(fontSize: 11, fontWeight: FontWeight.w400, color: Theme.of(context).colorScheme.onSurfaceVariant)), ], ), ], ), - iconTheme: const IconThemeData(color: UnionFlowColors.textPrimary), + iconTheme: IconThemeData(color: Theme.of(context).colorScheme.onSurface), ); } diff --git a/lib/features/dashboard/presentation/pages/role_dashboards/consultant_dashboard.dart b/lib/features/dashboard/presentation/pages/role_dashboards/consultant_dashboard.dart index 2238e14..d5e98e2 100644 --- a/lib/features/dashboard/presentation/pages/role_dashboards/consultant_dashboard.dart +++ b/lib/features/dashboard/presentation/pages/role_dashboards/consultant_dashboard.dart @@ -17,7 +17,7 @@ class ConsultantDashboard extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: UnionFlowColors.background, + backgroundColor: Theme.of(context).scaffoldBackgroundColor, appBar: _buildAppBar(context), drawer: DashboardDrawer( onLogout: () => context.read().add(const AuthLogoutRequested()), @@ -205,8 +205,10 @@ class ConsultantDashboard extends StatelessWidget { PreferredSizeWidget _buildAppBar(BuildContext context) { return AppBar( - backgroundColor: UnionFlowColors.surface, + backgroundColor: Theme.of(context).colorScheme.surface, elevation: 0, + scrolledUnderElevation: 1, + shadowColor: Theme.of(context).colorScheme.outline.withOpacity(0.2), title: Row( children: [ Container( @@ -224,16 +226,16 @@ class ConsultantDashboard extends StatelessWidget { child: const Text('C', style: TextStyle(color: Colors.white, fontWeight: FontWeight.w900, fontSize: 18)), ), const SizedBox(width: 12), - const Column( + Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('UnionFlow', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w700, color: UnionFlowColors.textPrimary)), - Text('Consultant', style: TextStyle(fontSize: 11, fontWeight: FontWeight.w400, color: UnionFlowColors.textSecondary)), + Text('UnionFlow', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w700, color: Theme.of(context).colorScheme.onSurface)), + Text('Consultant', style: TextStyle(fontSize: 11, fontWeight: FontWeight.w400, color: Theme.of(context).colorScheme.onSurfaceVariant)), ], ), ], ), - iconTheme: const IconThemeData(color: UnionFlowColors.textPrimary), + iconTheme: IconThemeData(color: Theme.of(context).colorScheme.onSurface), actions: [ UnionExportButton( onExport: (_) => Navigator.of(context).push( diff --git a/lib/features/dashboard/presentation/pages/role_dashboards/hr_manager_dashboard.dart b/lib/features/dashboard/presentation/pages/role_dashboards/hr_manager_dashboard.dart index 3d350c2..b1de4b2 100644 --- a/lib/features/dashboard/presentation/pages/role_dashboards/hr_manager_dashboard.dart +++ b/lib/features/dashboard/presentation/pages/role_dashboards/hr_manager_dashboard.dart @@ -17,7 +17,7 @@ class HRManagerDashboard extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: UnionFlowColors.background, + backgroundColor: Theme.of(context).scaffoldBackgroundColor, appBar: _buildAppBar(context), drawer: DashboardDrawer( onLogout: () => context.read().add(const AuthLogoutRequested()), @@ -250,8 +250,10 @@ class HRManagerDashboard extends StatelessWidget { PreferredSizeWidget _buildAppBar(BuildContext context) { return AppBar( - backgroundColor: UnionFlowColors.surface, + backgroundColor: Theme.of(context).colorScheme.surface, elevation: 0, + scrolledUnderElevation: 1, + shadowColor: Theme.of(context).colorScheme.outline.withOpacity(0.2), title: Row( children: [ Container( @@ -269,16 +271,16 @@ class HRManagerDashboard extends StatelessWidget { child: const Text('H', style: TextStyle(color: Colors.white, fontWeight: FontWeight.w900, fontSize: 18)), ), const SizedBox(width: 12), - const Column( + Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('UnionFlow', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w700, color: UnionFlowColors.textPrimary)), - Text('RH Manager', style: TextStyle(fontSize: 11, fontWeight: FontWeight.w400, color: UnionFlowColors.textSecondary)), + Text('UnionFlow', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w700, color: Theme.of(context).colorScheme.onSurface)), + Text('RH Manager', style: TextStyle(fontSize: 11, fontWeight: FontWeight.w400, color: Theme.of(context).colorScheme.onSurfaceVariant)), ], ), ], ), - iconTheme: const IconThemeData(color: UnionFlowColors.textPrimary), + iconTheme: IconThemeData(color: Theme.of(context).colorScheme.onSurface), actions: [ UnionExportButton( onExport: (_) => Navigator.of(context).push( diff --git a/lib/features/dashboard/presentation/pages/role_dashboards/moderator_dashboard.dart b/lib/features/dashboard/presentation/pages/role_dashboards/moderator_dashboard.dart index 7280eb5..19c8492 100644 --- a/lib/features/dashboard/presentation/pages/role_dashboards/moderator_dashboard.dart +++ b/lib/features/dashboard/presentation/pages/role_dashboards/moderator_dashboard.dart @@ -20,8 +20,8 @@ class ModeratorDashboard extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: UnionFlowColors.background, - appBar: _buildAppBar(), + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + appBar: _buildAppBar(context), drawer: DashboardDrawer( onLogout: () => context.read().add(const AuthLogoutRequested()), ), @@ -359,10 +359,12 @@ class ModeratorDashboard extends StatelessWidget { ); } - PreferredSizeWidget _buildAppBar() { + PreferredSizeWidget _buildAppBar(BuildContext context) { return AppBar( - backgroundColor: UnionFlowColors.surface, + backgroundColor: Theme.of(context).colorScheme.surface, elevation: 0, + scrolledUnderElevation: 1, + shadowColor: Theme.of(context).colorScheme.outline.withOpacity(0.2), title: Row( children: [ Container( @@ -376,16 +378,16 @@ class ModeratorDashboard extends StatelessWidget { child: const Text('U', style: TextStyle(color: Colors.white, fontWeight: FontWeight.w900, fontSize: 18)), ), const SizedBox(width: 12), - const Column( + Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('UnionFlow', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w700, color: UnionFlowColors.textPrimary)), - Text('Modérateur', style: TextStyle(fontSize: 11, fontWeight: FontWeight.w400, color: UnionFlowColors.textSecondary)), + Text('UnionFlow', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w700, color: Theme.of(context).colorScheme.onSurface)), + Text('Modérateur', style: TextStyle(fontSize: 11, fontWeight: FontWeight.w400, color: Theme.of(context).colorScheme.onSurfaceVariant)), ], ), ], ), - iconTheme: const IconThemeData(color: UnionFlowColors.textPrimary), + iconTheme: IconThemeData(color: Theme.of(context).colorScheme.onSurface), ); } diff --git a/lib/features/dashboard/presentation/pages/role_dashboards/org_admin_dashboard_loader.dart b/lib/features/dashboard/presentation/pages/role_dashboards/org_admin_dashboard_loader.dart index 8720dd6..80f745e 100644 --- a/lib/features/dashboard/presentation/pages/role_dashboards/org_admin_dashboard_loader.dart +++ b/lib/features/dashboard/presentation/pages/role_dashboards/org_admin_dashboard_loader.dart @@ -24,7 +24,7 @@ class OrgAdminDashboardLoader extends StatelessWidget { builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return Scaffold( - backgroundColor: UnionFlowColors.background, + backgroundColor: Theme.of(context).scaffoldBackgroundColor, body: const Center( child: CircularProgressIndicator(color: UnionFlowColors.gold), ), @@ -32,7 +32,7 @@ class OrgAdminDashboardLoader extends StatelessWidget { } if (snapshot.hasError) { return Scaffold( - backgroundColor: UnionFlowColors.background, + backgroundColor: Theme.of(context).scaffoldBackgroundColor, body: Center( child: Padding( padding: const EdgeInsets.all(24), @@ -69,7 +69,7 @@ class OrgAdminDashboardLoader extends StatelessWidget { final orgsWithId = orgs.where((o) => o.id != null && o.id!.isNotEmpty).toList(); if (orgsWithId.isEmpty) { return Scaffold( - backgroundColor: UnionFlowColors.background, + backgroundColor: Theme.of(context).scaffoldBackgroundColor, body: Center( child: Padding( padding: const EdgeInsets.all(24), diff --git a/lib/features/dashboard/presentation/pages/role_dashboards/simple_member_dashboard.dart b/lib/features/dashboard/presentation/pages/role_dashboards/simple_member_dashboard.dart index e375215..1f085a2 100644 --- a/lib/features/dashboard/presentation/pages/role_dashboards/simple_member_dashboard.dart +++ b/lib/features/dashboard/presentation/pages/role_dashboards/simple_member_dashboard.dart @@ -16,8 +16,8 @@ class SimpleMemberDashboard extends StatelessWidget { @override Widget build(BuildContext context) { return Scaffold( - backgroundColor: UnionFlowColors.background, - appBar: _buildAppBar(), + backgroundColor: Theme.of(context).scaffoldBackgroundColor, + appBar: _buildAppBar(context), drawer: DashboardDrawer( onLogout: () => context.read().add(const AuthLogoutRequested()), ), @@ -189,10 +189,12 @@ class SimpleMemberDashboard extends StatelessWidget { ); } - PreferredSizeWidget _buildAppBar() { + PreferredSizeWidget _buildAppBar(BuildContext context) { return AppBar( - backgroundColor: UnionFlowColors.surface, + backgroundColor: Theme.of(context).colorScheme.surface, elevation: 0, + scrolledUnderElevation: 1, + shadowColor: Theme.of(context).colorScheme.outline.withOpacity(0.2), title: Row( children: [ Container( @@ -206,16 +208,16 @@ class SimpleMemberDashboard extends StatelessWidget { child: const Text('U', style: TextStyle(color: Colors.white, fontWeight: FontWeight.w900, fontSize: 18)), ), const SizedBox(width: 12), - const Column( + Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Text('UnionFlow', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w700, color: UnionFlowColors.textPrimary)), - Text('Membre Simple', style: TextStyle(fontSize: 11, fontWeight: FontWeight.w400, color: UnionFlowColors.textSecondary)), + Text('UnionFlow', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w700, color: Theme.of(context).colorScheme.onSurface)), + Text('Membre Simple', style: TextStyle(fontSize: 11, fontWeight: FontWeight.w400, color: Theme.of(context).colorScheme.onSurfaceVariant)), ], ), ], ), - iconTheme: const IconThemeData(color: UnionFlowColors.textPrimary), + iconTheme: IconThemeData(color: Theme.of(context).colorScheme.onSurface), ); } diff --git a/lib/features/dashboard/presentation/widgets/charts/dashboard_chart_widget.dart b/lib/features/dashboard/presentation/widgets/charts/dashboard_chart_widget.dart index 1ab356d..a7b024a 100644 --- a/lib/features/dashboard/presentation/widgets/charts/dashboard_chart_widget.dart +++ b/lib/features/dashboard/presentation/widgets/charts/dashboard_chart_widget.dart @@ -56,7 +56,7 @@ class DashboardChartWidget extends StatelessWidget { children: [ Icon( _getChartIcon(), - color: AppColors.primaryGreen, + color: AppColors.primary, size: 18, ), const SizedBox(width: 8), @@ -100,12 +100,12 @@ class DashboardChartWidget extends StatelessWidget { ), ), PieChartSectionData( - color: AppColors.lightBorder, + color: AppColors.border, value: (stats.totalMembers - stats.activeMembers).toDouble(), title: '${stats.totalMembers - stats.activeMembers}', radius: 45, titleStyle: AppTypography.badgeText.copyWith( - color: AppColors.textSecondaryLight, + color: AppColors.textSecondary, fontWeight: FontWeight.bold, ), ), @@ -123,7 +123,7 @@ class DashboardChartWidget extends StatelessWidget { horizontalInterval: stats.totalContributionAmount / 4, getDrawingHorizontalLine: (value) { return const FlLine( - color: AppColors.lightBorder, + color: AppColors.border, strokeWidth: 1, ); }, @@ -174,8 +174,8 @@ class DashboardChartWidget extends StatelessWidget { isCurved: true, gradient: const LinearGradient( colors: [ - AppColors.brandGreen, - AppColors.primaryGreen, + AppColors.primaryDark, + AppColors.primary, ], ), barWidth: 3, @@ -185,8 +185,8 @@ class DashboardChartWidget extends StatelessWidget { show: true, gradient: LinearGradient( colors: [ - AppColors.brandGreen.withOpacity(0.3), - AppColors.primaryGreen.withOpacity(0.1), + AppColors.primaryDark.withOpacity(0.3), + AppColors.primary.withOpacity(0.1), ], begin: Alignment.topCenter, end: Alignment.bottomCenter, @@ -314,13 +314,13 @@ class DashboardChartWidget extends StatelessWidget { Widget _buildLoadingChart() { return Container( decoration: BoxDecoration( - color: AppColors.lightBorder.withOpacity(0.5), + color: AppColors.border.withOpacity(0.5), borderRadius: BorderRadius.circular(12), ), child: const Center( child: CircularProgressIndicator( strokeWidth: 2, - valueColor: AlwaysStoppedAnimation(AppColors.primaryGreen), + valueColor: AlwaysStoppedAnimation(AppColors.primary), ), ), ); @@ -359,7 +359,7 @@ class DashboardChartWidget extends StatelessWidget { Widget _buildEmptyChart() { return Container( decoration: BoxDecoration( - color: AppColors.lightBorder.withOpacity(0.2), + color: AppColors.border.withOpacity(0.2), borderRadius: BorderRadius.circular(12), ), child: Center( @@ -368,7 +368,7 @@ class DashboardChartWidget extends StatelessWidget { children: [ const Icon( Icons.bar_chart_outlined, - color: AppColors.textSecondaryLight, + color: AppColors.textSecondary, size: 24, ), const SizedBox(height: 8), diff --git a/lib/features/dashboard/presentation/widgets/common/activity_item.dart b/lib/features/dashboard/presentation/widgets/common/activity_item.dart index 767a4f7..e05ca14 100644 --- a/lib/features/dashboard/presentation/widgets/common/activity_item.dart +++ b/lib/features/dashboard/presentation/widgets/common/activity_item.dart @@ -10,28 +10,28 @@ import '../../../../../shared/design_system/unionflow_design_system.dart'; class ActivityItem extends StatelessWidget { /// Titre principal de l'activité final String title; - + /// Description ou détails de l'activité final String? description; - + /// Horodatage de l'activité final String timestamp; - + /// Icône représentative de l'activité final IconData? icon; - + /// Couleur thématique de l'activité final Color? color; - + /// Type d'activité pour le style automatique final ActivityType? type; - + /// Callback lors du tap sur l'élément final VoidCallback? onTap; - + /// Style de l'élément d'activité final ActivityItemStyle style; - + /// Afficher ou non l'indicateur de statut final bool showStatusIndicator; @@ -95,7 +95,7 @@ class ActivityItem extends StatelessWidget { required this.timestamp, this.onTap, }) : icon = Icons.error, - color = Colors.red, + color = ColorTokens.error, type = ActivityType.error, style = ActivityItemStyle.alert, showStatusIndicator = true; @@ -108,7 +108,7 @@ class ActivityItem extends StatelessWidget { required this.timestamp, this.onTap, }) : icon = Icons.check_circle, - color = const Color(0xFF00B894), + color = ColorTokens.successLight, type = ActivityType.success, style = ActivityItemStyle.normal, showStatusIndicator = true; @@ -123,28 +123,29 @@ class ActivityItem extends StatelessWidget { child: Container( margin: const EdgeInsets.only(bottom: 8), padding: _getPadding(), - decoration: _getDecoration(effectiveColor), - child: _buildContent(effectiveColor, effectiveIcon), + decoration: _getDecoration(context, effectiveColor), + child: _buildContent(context, effectiveColor, effectiveIcon), ), ); } /// Contenu principal de l'élément - Widget _buildContent(Color effectiveColor, IconData effectiveIcon) { + Widget _buildContent(BuildContext context, Color effectiveColor, IconData effectiveIcon) { switch (style) { case ActivityItemStyle.minimal: - return _buildMinimalContent(effectiveColor, effectiveIcon); + return _buildMinimalContent(context, effectiveColor, effectiveIcon); case ActivityItemStyle.normal: - return _buildNormalContent(effectiveColor, effectiveIcon); + return _buildNormalContent(context, effectiveColor, effectiveIcon); case ActivityItemStyle.detailed: - return _buildDetailedContent(effectiveColor, effectiveIcon); + return _buildDetailedContent(context, effectiveColor, effectiveIcon); case ActivityItemStyle.alert: - return _buildAlertContent(effectiveColor, effectiveIcon); + return _buildAlertContent(context, effectiveColor, effectiveIcon); } } /// Contenu minimal (ligne simple) - Widget _buildMinimalContent(Color effectiveColor, IconData effectiveIcon) { + Widget _buildMinimalContent(BuildContext context, Color effectiveColor, IconData effectiveIcon) { + final scheme = Theme.of(context).colorScheme; return Row( children: [ if (showStatusIndicator) @@ -160,16 +161,17 @@ class ActivityItem extends StatelessWidget { Expanded( child: Text( title, - style: const TextStyle( + style: TextStyle( fontSize: 12, fontWeight: FontWeight.w500, + color: scheme.onSurface, ), ), ), Text( timestamp, - style: const TextStyle( - color: Colors.grey, + style: TextStyle( + color: scheme.onSurfaceVariant, fontSize: 10, ), ), @@ -178,7 +180,8 @@ class ActivityItem extends StatelessWidget { } /// Contenu normal avec icône - Widget _buildNormalContent(Color effectiveColor, IconData effectiveIcon) { + Widget _buildNormalContent(BuildContext context, Color effectiveColor, IconData effectiveIcon) { + final scheme = Theme.of(context).colorScheme; return Row( children: [ if (showStatusIndicator) ...[ @@ -202,10 +205,10 @@ class ActivityItem extends StatelessWidget { children: [ Text( title, - style: const TextStyle( + style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: Color(0xFF1F2937), + color: scheme.onSurface, ), ), if (description != null) ...[ @@ -214,7 +217,7 @@ class ActivityItem extends StatelessWidget { description!, style: TextStyle( fontSize: 12, - color: Colors.grey[600], + color: scheme.onSurfaceVariant, ), ), ], @@ -225,7 +228,7 @@ class ActivityItem extends StatelessWidget { Text( timestamp, style: TextStyle( - color: Colors.grey[500], + color: scheme.onSurfaceVariant, fontSize: 11, fontWeight: FontWeight.w500, ), @@ -235,7 +238,8 @@ class ActivityItem extends StatelessWidget { } /// Contenu détaillé avec plus d'informations - Widget _buildDetailedContent(Color effectiveColor, IconData effectiveIcon) { + Widget _buildDetailedContent(BuildContext context, Color effectiveColor, IconData effectiveIcon) { + final scheme = Theme.of(context).colorScheme; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -257,17 +261,17 @@ class ActivityItem extends StatelessWidget { Expanded( child: Text( title, - style: const TextStyle( + style: TextStyle( fontSize: 13, fontWeight: FontWeight.w600, - color: Color(0xFF1F2937), + color: scheme.onSurface, ), ), ), Text( timestamp, style: TextStyle( - color: Colors.grey[500], + color: scheme.onSurfaceVariant, fontSize: 12, fontWeight: FontWeight.w500, ), @@ -282,7 +286,7 @@ class ActivityItem extends StatelessWidget { description!, style: TextStyle( fontSize: 14, - color: Colors.grey[700], + color: scheme.onSurfaceVariant, height: 1.4, ), ), @@ -293,7 +297,8 @@ class ActivityItem extends StatelessWidget { } /// Contenu pour les alertes avec style spécial - Widget _buildAlertContent(Color effectiveColor, IconData effectiveIcon) { + Widget _buildAlertContent(BuildContext context, Color effectiveColor, IconData effectiveIcon) { + final scheme = Theme.of(context).colorScheme; return Row( children: [ Icon( @@ -320,7 +325,7 @@ class ActivityItem extends StatelessWidget { description!, style: TextStyle( fontSize: 12, - color: Colors.grey[600], + color: scheme.onSurfaceVariant, ), ), ], @@ -331,7 +336,7 @@ class ActivityItem extends StatelessWidget { Text( timestamp, style: TextStyle( - color: Colors.grey[500], + color: scheme.onSurfaceVariant, fontSize: 11, ), ), @@ -366,7 +371,7 @@ class ActivityItem extends StatelessWidget { /// Icône effective selon le type IconData _getEffectiveIcon() { if (icon != null) return icon!; - + switch (type) { case ActivityType.system: return Icons.settings; @@ -401,19 +406,20 @@ class ActivityItem extends StatelessWidget { } } - /// Décoration selon le style - BoxDecoration _getDecoration(Color effectiveColor) { + /// Décoration selon le style — toujours theme-aware + BoxDecoration _getDecoration(BuildContext context, Color effectiveColor) { + final scheme = Theme.of(context).colorScheme; switch (style) { case ActivityItemStyle.minimal: return const BoxDecoration(); case ActivityItemStyle.normal: return BoxDecoration( - color: Colors.white, + color: scheme.surface, borderRadius: BorderRadius.circular(6), ); case ActivityItemStyle.detailed: return BoxDecoration( - color: Colors.white, + color: scheme.surface, borderRadius: BorderRadius.circular(8), ); case ActivityItemStyle.alert: diff --git a/lib/features/dashboard/presentation/widgets/common/section_header.dart b/lib/features/dashboard/presentation/widgets/common/section_header.dart index c4aad4a..26fec50 100644 --- a/lib/features/dashboard/presentation/widgets/common/section_header.dart +++ b/lib/features/dashboard/presentation/widgets/common/section_header.dart @@ -10,25 +10,25 @@ import '../../../../../shared/design_system/unionflow_design_system.dart'; class SectionHeader extends StatelessWidget { /// Titre principal de la section final String title; - + /// Sous-titre optionnel final String? subtitle; - + /// Widget d'action à droite (bouton, icône, etc.) final Widget? action; - + /// Icône optionnelle à gauche du titre final IconData? icon; - - /// Couleur du titre et de l'icône + + /// Couleur du titre et de l'icône (null = adaptatif selon le thème) final Color? color; - + /// Taille du titre final double? fontSize; - + /// Style de l'en-tête final SectionHeaderStyle style; - + /// Espacement en bas de l'en-tête final double bottomSpacing; @@ -68,14 +68,14 @@ class SectionHeader extends StatelessWidget { style = SectionHeaderStyle.normal, bottomSpacing = 8; - /// Constructeur pour un en-tête de sous-section + /// Constructeur pour un en-tête de sous-section (couleur adaptative) const SectionHeader.subsection({ super.key, required this.title, this.subtitle, this.action, this.icon, - }) : color = const Color(0xFF374151), + }) : color = null, // null → adaptatif via Theme.of(context).colorScheme.onSurface fontSize = 14, style = SectionHeaderStyle.minimal, bottomSpacing = 8; @@ -84,25 +84,26 @@ class SectionHeader extends StatelessWidget { Widget build(BuildContext context) { return Padding( padding: EdgeInsets.only(bottom: bottomSpacing), - child: _buildContent(), + child: _buildContent(context), ); } - Widget _buildContent() { + Widget _buildContent(BuildContext context) { switch (style) { case SectionHeaderStyle.primary: - return _buildPrimaryHeader(); + return _buildPrimaryHeader(context); case SectionHeaderStyle.normal: - return _buildNormalHeader(); + return _buildNormalHeader(context); case SectionHeaderStyle.minimal: - return _buildMinimalHeader(); + return _buildMinimalHeader(context); case SectionHeaderStyle.card: - return _buildCardHeader(); + return _buildCardHeader(context); } } - /// En-tête principal avec fond coloré - Widget _buildPrimaryHeader() { + /// En-tête principal avec fond coloré (gradient sur couleur thématique) + /// Colors.white est correct ici : texte sur fond coloré opaque + Widget _buildPrimaryHeader(BuildContext context) { final effectiveColor = color ?? ColorTokens.primary; return Container( @@ -167,13 +168,15 @@ class SectionHeader extends StatelessWidget { } /// En-tête normal avec icône et action - Widget _buildNormalHeader() { + Widget _buildNormalHeader(BuildContext context) { + final scheme = Theme.of(context).colorScheme; + final effectiveColor = color ?? scheme.primary; return Row( children: [ if (icon != null) ...[ Icon( icon, - color: color ?? ColorTokens.primary, + color: effectiveColor, size: 20, ), const SizedBox(width: SpacingTokens.md), @@ -187,7 +190,7 @@ class SectionHeader extends StatelessWidget { style: TextStyle( fontSize: fontSize ?? 13, fontWeight: FontWeight.bold, - color: color ?? ColorTokens.primary, + color: effectiveColor, ), ), if (subtitle != null) ...[ @@ -196,7 +199,7 @@ class SectionHeader extends StatelessWidget { subtitle!, style: TextStyle( fontSize: 12, - color: Colors.grey[600], + color: scheme.onSurfaceVariant, ), ), ], @@ -208,14 +211,16 @@ class SectionHeader extends StatelessWidget { ); } - /// En-tête minimal simple - Widget _buildMinimalHeader() { + /// En-tête minimal simple — couleur totalement adaptative + Widget _buildMinimalHeader(BuildContext context) { + final scheme = Theme.of(context).colorScheme; + final effectiveColor = color ?? scheme.onSurface; return Row( children: [ if (icon != null) ...[ Icon( icon, - color: color ?? const Color(0xFF374151), + color: effectiveColor, size: 16, ), const SizedBox(width: 6), @@ -226,7 +231,7 @@ class SectionHeader extends StatelessWidget { style: TextStyle( fontSize: fontSize ?? 14, fontWeight: FontWeight.w600, - color: color ?? const Color(0xFF374151), + color: effectiveColor, ), ), ), @@ -235,20 +240,23 @@ class SectionHeader extends StatelessWidget { ); } - /// En-tête avec fond de carte - Widget _buildCardHeader() { + /// En-tête avec fond de carte — surface theme-aware + Widget _buildCardHeader(BuildContext context) { + final scheme = Theme.of(context).colorScheme; + final effectiveColor = color ?? scheme.primary; return Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( - color: Colors.white, + color: scheme.surface, borderRadius: BorderRadius.circular(8), + border: Border.all(color: scheme.outline, width: 0.5), ), child: Row( children: [ if (icon != null) ...[ Icon( icon, - color: color ?? ColorTokens.primary, + color: effectiveColor, size: 20, ), const SizedBox(width: SpacingTokens.md), @@ -262,7 +270,7 @@ class SectionHeader extends StatelessWidget { style: TextStyle( fontSize: fontSize ?? 13, fontWeight: FontWeight.bold, - color: color ?? ColorTokens.primary, + color: effectiveColor, ), ), if (subtitle != null) ...[ @@ -271,7 +279,7 @@ class SectionHeader extends StatelessWidget { subtitle!, style: TextStyle( fontSize: 12, - color: Colors.grey[600], + color: scheme.onSurfaceVariant, ), ), ], diff --git a/lib/features/dashboard/presentation/widgets/common/stat_card.dart b/lib/features/dashboard/presentation/widgets/common/stat_card.dart index 30617de..0a91309 100644 --- a/lib/features/dashboard/presentation/widgets/common/stat_card.dart +++ b/lib/features/dashboard/presentation/widgets/common/stat_card.dart @@ -1,31 +1,31 @@ import 'package:flutter/material.dart'; /// Widget réutilisable pour afficher une carte de statistique -/// +/// /// Composant générique utilisé dans tous les dashboards pour afficher /// des métriques avec icône, valeur, titre et sous-titre. class StatCard extends StatelessWidget { /// Titre principal de la statistique final String title; - + /// Valeur numérique ou textuelle à afficher final String value; - + /// Sous-titre ou description complémentaire final String subtitle; - + /// Icône représentative de la métrique final IconData icon; - + /// Couleur thématique de la carte final Color color; - + /// Callback optionnel lors du tap sur la carte final VoidCallback? onTap; - + /// Taille de la carte (compact, normal, large) final StatCardSize size; - + /// Style de la carte (minimal, elevated, outlined) final StatCardStyle style; @@ -71,26 +71,27 @@ class StatCard extends StatelessWidget { onTap: onTap, child: Container( padding: _getPadding(), - decoration: _getDecoration(), - child: _buildContent(), + decoration: _getDecoration(context), + child: _buildContent(context), ), ); } /// Contenu principal de la carte - Widget _buildContent() { + Widget _buildContent(BuildContext context) { switch (size) { case StatCardSize.compact: - return _buildCompactContent(); + return _buildCompactContent(context); case StatCardSize.normal: - return _buildNormalContent(); + return _buildNormalContent(context); case StatCardSize.large: - return _buildLargeContent(); + return _buildLargeContent(context); } } /// Contenu compact pour les KPIs - Widget _buildCompactContent() { + Widget _buildCompactContent(BuildContext context) { + final scheme = Theme.of(context).colorScheme; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -111,16 +112,16 @@ class StatCard extends StatelessWidget { const SizedBox(height: 4), Text( title, - style: const TextStyle( + style: TextStyle( fontWeight: FontWeight.w600, - color: Colors.black87, + color: scheme.onSurface, fontSize: 12, ), ), Text( subtitle, - style: const TextStyle( - color: Colors.grey, + style: TextStyle( + color: scheme.onSurfaceVariant, fontSize: 10, ), ), @@ -129,7 +130,8 @@ class StatCard extends StatelessWidget { } /// Contenu normal pour les métriques - Widget _buildNormalContent() { + Widget _buildNormalContent(BuildContext context) { + final scheme = Theme.of(context).colorScheme; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -159,7 +161,7 @@ class StatCard extends StatelessWidget { Text( subtitle, style: TextStyle( - color: Colors.grey[600], + color: scheme.onSurfaceVariant, fontSize: 10, ), ), @@ -170,9 +172,9 @@ class StatCard extends StatelessWidget { const SizedBox(height: 8), Text( title, - style: const TextStyle( + style: TextStyle( fontWeight: FontWeight.w600, - color: Color(0xFF1F2937), + color: scheme.onSurface, fontSize: 13, ), ), @@ -181,7 +183,8 @@ class StatCard extends StatelessWidget { } /// Contenu large pour les dashboards principaux - Widget _buildLargeContent() { + Widget _buildLargeContent(BuildContext context) { + final scheme = Theme.of(context).colorScheme; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ @@ -211,7 +214,7 @@ class StatCard extends StatelessWidget { Text( subtitle, style: TextStyle( - color: Colors.grey[600], + color: scheme.onSurfaceVariant, fontSize: 12, ), ), @@ -222,9 +225,9 @@ class StatCard extends StatelessWidget { const SizedBox(height: 8), Text( title, - style: const TextStyle( + style: TextStyle( fontWeight: FontWeight.w600, - color: Color(0xFF1F2937), + color: scheme.onSurface, fontSize: 13, ), ), @@ -244,22 +247,19 @@ class StatCard extends StatelessWidget { } } - /// Décoration selon le style - BoxDecoration _getDecoration() { + /// Décoration selon le style — toujours theme-aware + BoxDecoration _getDecoration(BuildContext context) { + final scheme = Theme.of(context).colorScheme; switch (style) { case StatCardStyle.minimal: - return BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(8), - ); case StatCardStyle.elevated: return BoxDecoration( - color: Colors.white, + color: scheme.surface, borderRadius: BorderRadius.circular(8), ); case StatCardStyle.outlined: return BoxDecoration( - color: Colors.white, + color: scheme.surface, borderRadius: BorderRadius.circular(8), border: Border.all( color: color.withOpacity(0.2), diff --git a/lib/features/dashboard/presentation/widgets/connected/connected_stats_card.dart b/lib/features/dashboard/presentation/widgets/connected/connected_stats_card.dart index 2966401..8c5dffa 100644 --- a/lib/features/dashboard/presentation/widgets/connected/connected_stats_card.dart +++ b/lib/features/dashboard/presentation/widgets/connected/connected_stats_card.dart @@ -46,36 +46,37 @@ class ConnectedStatsCard extends StatelessWidget { Widget _buildDataCard(DashboardStatsEntity stats) { final value = valueExtractor(stats); final subtitle = subtitleExtractor?.call(stats); - final color = customColor ?? AppColors.primaryGreen; + final color = customColor ?? AppColors.primary; return CoreCard( onTap: onTap, - padding: const EdgeInsets.all(10), + padding: const EdgeInsets.all(8), child: Column( + mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( - padding: const EdgeInsets.all(7), + padding: const EdgeInsets.all(5), decoration: BoxDecoration( color: color.withOpacity(0.1), - borderRadius: BorderRadius.circular(8), + borderRadius: BorderRadius.circular(6), ), child: Icon( icon, color: color, - size: 16, + size: 14, ), ), - const SizedBox(width: 8), + const SizedBox(width: 6), Expanded( child: Text( title.toUpperCase(), style: AppTypography.subtitleSmall.copyWith( fontWeight: FontWeight.bold, - fontSize: 10, - letterSpacing: 1.1, + fontSize: 9, + letterSpacing: 1.0, ), maxLines: 1, overflow: TextOverflow.ellipsis, @@ -83,22 +84,28 @@ class ConnectedStatsCard extends StatelessWidget { ), ], ), - const SizedBox(height: 8), - Text( - value, - style: AppTypography.headerSmall.copyWith( - color: color, - fontWeight: FontWeight.bold, - fontSize: 18, + const SizedBox(height: 4), + Flexible( + child: Text( + value, + style: AppTypography.headerSmall.copyWith( + color: color, + fontWeight: FontWeight.bold, + fontSize: 16, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, ), ), - if (subtitle != null) ...[ - const SizedBox(height: 4), - Text( - subtitle, - style: AppTypography.subtitleSmall.copyWith(fontSize: 10), + if (subtitle != null) + Flexible( + child: Text( + subtitle, + style: AppTypography.subtitleSmall.copyWith(fontSize: 9), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), ), - ], ], ), ); diff --git a/lib/features/dashboard/presentation/widgets/dashboard_drawer.dart b/lib/features/dashboard/presentation/widgets/dashboard_drawer.dart index 273a49e..e8198bc 100644 --- a/lib/features/dashboard/presentation/widgets/dashboard_drawer.dart +++ b/lib/features/dashboard/presentation/widgets/dashboard_drawer.dart @@ -1,18 +1,28 @@ /// Widget de menu latéral (drawer) du dashboard library dashboard_drawer; +import 'dart:math' as math; + import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:provider/provider.dart'; +import '../../../../core/theme/theme_provider.dart'; import '../../../../shared/design_system/unionflow_design_system.dart'; import '../../../../shared/widgets/core_card.dart'; import '../../../../shared/widgets/mini_avatar.dart'; import '../../../authentication/presentation/bloc/auth_bloc.dart'; +import '../../../authentication/data/models/user_role.dart'; import '../../../profile/presentation/pages/profile_page_wrapper.dart'; import '../../../notifications/presentation/pages/notifications_page_wrapper.dart'; import '../../../help/presentation/pages/help_support_page.dart'; import '../../../about/presentation/pages/about_page.dart'; +import '../../../admin/presentation/pages/user_management_page.dart'; +import '../../../settings/presentation/pages/system_settings_page.dart'; +import '../../../backup/presentation/pages/backup_page.dart'; +import '../../../logs/presentation/pages/logs_page.dart'; + /// Drawer principal — Mon Espace /// Profil · Notifications · Aide · À propos · Déconnexion class DashboardDrawer extends StatelessWidget { @@ -30,7 +40,7 @@ class DashboardDrawer extends StatelessWidget { return Drawer( backgroundColor: - isDark ? AppColors.darkSurface : AppColors.lightBackground, + isDark ? AppColors.surfaceDark : AppColors.background, child: SafeArea( child: SingleChildScrollView( padding: const EdgeInsets.all(10), @@ -39,6 +49,9 @@ class DashboardDrawer extends StatelessWidget { children: [ _buildUserProfile(context, authState), const SizedBox(height: SpacingTokens.md), + _buildSectionTitle(context, 'Apparence'), + const _ThemeToggleTile(), + const SizedBox(height: SpacingTokens.md), _buildSectionTitle(context, 'Mon Espace'), _buildOptionTile( context: context, @@ -77,6 +90,53 @@ class DashboardDrawer extends StatelessWidget { MaterialPageRoute(builder: (_) => const AboutPage()), ), ), + + // ── Section SYSTÈME (super admin uniquement) ── + if (authState.effectiveRole == UserRole.superAdmin) ...[ + const SizedBox(height: SpacingTokens.md), + _buildSectionTitle(context, 'Système'), + _buildOptionTile( + context: context, + icon: Icons.people, + title: 'Gestion des utilisateurs', + subtitle: 'Utilisateurs Keycloak et rôles', + accentColor: ModuleColors.membres, + onTap: () => Navigator.of(context).push( + MaterialPageRoute(builder: (_) => const UserManagementPage()), + ), + ), + _buildOptionTile( + context: context, + icon: Icons.settings, + title: 'Paramètres Système', + subtitle: 'Configuration globale', + accentColor: ModuleColors.parametres, + onTap: () => Navigator.of(context).push( + MaterialPageRoute(builder: (_) => const SystemSettingsPage()), + ), + ), + _buildOptionTile( + context: context, + icon: Icons.backup, + title: 'Sauvegarde & Restauration', + subtitle: 'Gestion des sauvegardes', + accentColor: ModuleColors.backup, + onTap: () => Navigator.of(context).push( + MaterialPageRoute(builder: (_) => const BackupPage()), + ), + ), + _buildOptionTile( + context: context, + icon: Icons.article, + title: 'Logs & Monitoring', + subtitle: 'Surveillance et journaux', + accentColor: ModuleColors.logs, + onTap: () => Navigator.of(context).push( + MaterialPageRoute(builder: (_) => const LogsPage()), + ), + ), + ], + const SizedBox(height: SpacingTokens.md), _buildOptionTile( context: context, @@ -101,11 +161,11 @@ class DashboardDrawer extends StatelessWidget { Widget _buildUserProfile(BuildContext context, AuthAuthenticated state) { final isDark = Theme.of(context).brightness == Brightness.dark; final nameColor = - isDark ? AppColors.textPrimaryDark : AppColors.textPrimaryLight; + isDark ? AppColors.textPrimaryDark : AppColors.textPrimary; final emailColor = - isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight; + isDark ? AppColors.textSecondaryDark : AppColors.textSecondary; final roleColor = - isDark ? AppColors.brandGreenLight : AppColors.primaryGreen; + isDark ? AppColors.primaryLight : AppColors.primary; return CoreCard( child: Row( @@ -154,7 +214,7 @@ class DashboardDrawer extends StatelessWidget { style: AppTypography.subtitleSmall.copyWith( fontWeight: FontWeight.bold, letterSpacing: 1.1, - color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight, + color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondary, ), ), ); @@ -169,14 +229,14 @@ class DashboardDrawer extends StatelessWidget { Color? accentColor, }) { final isDark = Theme.of(context).brightness == Brightness.dark; - final accent = accentColor ?? AppColors.primaryGreen; + final accent = accentColor ?? AppColors.primary; final titleColor = accentColor != null ? accentColor - : (isDark ? AppColors.textPrimaryDark : AppColors.textPrimaryLight); + : (isDark ? AppColors.textPrimaryDark : AppColors.textPrimary); final subtitleColor = - isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight; + isDark ? AppColors.textSecondaryDark : AppColors.textSecondary; final chevronColor = - isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight; + isDark ? AppColors.textSecondaryDark : AppColors.textSecondary; return CoreCard( margin: const EdgeInsets.only(bottom: 8), @@ -213,3 +273,181 @@ class DashboardDrawer extends StatelessWidget { ); } } + +// ───────────────────────────────────────────────────────────────────────────── +// Toggle thème jour / nuit avec animation soleil ↔ lune +// ───────────────────────────────────────────────────────────────────────────── + +class _ThemeToggleTile extends StatefulWidget { + const _ThemeToggleTile(); + + @override + State<_ThemeToggleTile> createState() => _ThemeToggleTileState(); +} + +class _ThemeToggleTileState extends State<_ThemeToggleTile> + with SingleTickerProviderStateMixin { + late final AnimationController _ctrl; + // Rotation complète sur toute la durée + late final Animation _rotation; + // Scale : 1→0 sur la première moitié, 0→1 sur la seconde + late final Animation _scale; + + bool _isAnimating = false; + + @override + void initState() { + super.initState(); + _ctrl = AnimationController( + vsync: this, + duration: const Duration(milliseconds: 480), + ); + _rotation = Tween(begin: 0, end: 2 * math.pi).animate( + CurvedAnimation(parent: _ctrl, curve: Curves.easeInOut), + ); + _scale = TweenSequence([ + TweenSequenceItem( + tween: Tween(begin: 1.0, end: 0.0) + .chain(CurveTween(curve: Curves.easeIn)), + weight: 50, + ), + TweenSequenceItem( + tween: Tween(begin: 0.0, end: 1.0) + .chain(CurveTween(curve: Curves.elasticOut)), + weight: 50, + ), + ]).animate(_ctrl); + } + + @override + void dispose() { + _ctrl.dispose(); + super.dispose(); + } + + Future _toggle() async { + if (_isAnimating) return; + _isAnimating = true; + + // Première moitié : rotation + disparition de l'icône + await _ctrl.animateTo(0.5); + + // Bascule le thème au moment où l'icône est invisible (scale ≈ 0) + if (mounted) { + final tp = context.read(); + tp.setMode(tp.mode == ThemeMode.dark ? ThemeMode.light : ThemeMode.dark); + } + + // Seconde moitié : réapparition avec la nouvelle icône + fin de rotation + if (mounted) await _ctrl.animateTo(1.0); + + _ctrl.reset(); + _isAnimating = false; + } + + @override + Widget build(BuildContext context) { + final tp = context.watch(); + final isDark = tp.mode == ThemeMode.dark; + final isDarkBrightness = Theme.of(context).brightness == Brightness.dark; + + // Soleil = jaune ambré / Lune = indigo + final accent = + isDark ? const Color(0xFFFBBF24) : const Color(0xFF6366F1); + final titleColor = + isDarkBrightness ? AppColors.textPrimaryDark : AppColors.textPrimary; + final subtitleColor = isDarkBrightness + ? AppColors.textSecondaryDark + : AppColors.textSecondary; + + return CoreCard( + margin: const EdgeInsets.only(bottom: 8), + onTap: _toggle, + child: Row( + children: [ + // Icône animée + AnimatedBuilder( + animation: _ctrl, + builder: (_, __) => Transform.rotate( + angle: _rotation.value, + child: Transform.scale( + scale: _scale.value.clamp(0.01, 1.0), + child: Container( + padding: const EdgeInsets.all(6), + decoration: BoxDecoration( + color: accent.withOpacity(isDarkBrightness ? 0.22 : 0.12), + borderRadius: BorderRadius.circular(8), + ), + child: Icon( + isDark ? Icons.light_mode_rounded : Icons.dark_mode_rounded, + color: accent, + size: 16, + ), + ), + ), + ), + ), + const SizedBox(width: 10), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Thème', + style: AppTypography.actionText.copyWith(color: titleColor), + ), + AnimatedSwitcher( + duration: const Duration(milliseconds: 200), + child: Text( + isDark ? 'Mode sombre' : 'Mode clair', + key: ValueKey(isDark), + style: AppTypography.subtitleSmall + .copyWith(color: subtitleColor), + ), + ), + ], + ), + ), + // Pill toggle animée + GestureDetector( + onTap: _toggle, + child: AnimatedContainer( + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut, + width: 40, + height: 22, + decoration: BoxDecoration( + borderRadius: BorderRadius.circular(11), + color: isDark + ? const Color(0xFF6366F1) + : AppColors.borderStrong, + ), + child: AnimatedAlign( + duration: const Duration(milliseconds: 300), + curve: Curves.easeInOut, + alignment: + isDark ? Alignment.centerRight : Alignment.centerLeft, + child: Container( + margin: const EdgeInsets.all(2), + width: 18, + height: 18, + decoration: BoxDecoration( + shape: BoxShape.circle, + color: Colors.white, + boxShadow: const [ + BoxShadow( + color: Colors.black26, + blurRadius: 4, + offset: Offset(0, 1), + ), + ], + ), + ), + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/features/dashboard/presentation/widgets/dashboard_widgets.dart b/lib/features/dashboard/presentation/widgets/dashboard_widgets.dart index bac3993..d8cbdd9 100644 --- a/lib/features/dashboard/presentation/widgets/dashboard_widgets.dart +++ b/lib/features/dashboard/presentation/widgets/dashboard_widgets.dart @@ -28,14 +28,14 @@ class DashboardStat extends StatelessWidget { children: [ Icon( icon, - color: color ?? AppColors.primaryGreen, + color: color ?? AppColors.primary, size: 20, ), const Spacer(), Text( value, style: AppTypography.headerSmall.copyWith( - color: color ?? AppColors.primaryGreen, + color: color ?? AppColors.primary, fontSize: 18, ), ), @@ -130,12 +130,12 @@ class DashboardQuickAction extends StatelessWidget { Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( - color: (color ?? AppColors.primaryGreen).withOpacity(0.1), + color: (color ?? AppColors.primary).withOpacity(0.1), shape: BoxShape.circle, ), child: Icon( icon, - color: color ?? AppColors.primaryGreen, + color: color ?? AppColors.primary, size: 24, ), ), @@ -211,12 +211,12 @@ class DashboardActivity extends StatelessWidget { Container( padding: const EdgeInsets.all(6), decoration: BoxDecoration( - color: (color ?? AppColors.primaryGreen).withOpacity(0.1), + color: (color ?? AppColors.primary).withOpacity(0.1), borderRadius: BorderRadius.circular(4), ), child: Icon( icon, - color: color ?? AppColors.primaryGreen, + color: color ?? AppColors.primary, size: 14, ), ), @@ -245,7 +245,7 @@ class DashboardActivity extends StatelessWidget { return Text( time, style: AppTypography.subtitleSmall.copyWith( - color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight, + color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondary, fontSize: 9, ), ); diff --git a/lib/features/dashboard/presentation/widgets/metrics/real_time_metrics_widget.dart b/lib/features/dashboard/presentation/widgets/metrics/real_time_metrics_widget.dart index 1b0de7a..42b48a1 100644 --- a/lib/features/dashboard/presentation/widgets/metrics/real_time_metrics_widget.dart +++ b/lib/features/dashboard/presentation/widgets/metrics/real_time_metrics_widget.dart @@ -86,7 +86,7 @@ class _RealTimeMetricsWidgetState extends State gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, - colors: [AppColors.brandGreen, AppColors.primaryGreen], + colors: [AppColors.primaryDark, AppColors.primary], ), borderRadius: BorderRadius.circular(10), ), diff --git a/lib/features/dashboard/presentation/widgets/monitoring/performance_monitor_widget.dart b/lib/features/dashboard/presentation/widgets/monitoring/performance_monitor_widget.dart index badca7f..d51bf24 100644 --- a/lib/features/dashboard/presentation/widgets/monitoring/performance_monitor_widget.dart +++ b/lib/features/dashboard/presentation/widgets/monitoring/performance_monitor_widget.dart @@ -158,7 +158,7 @@ class _PerformanceMonitorWidgetState extends State height: 20, child: CircularProgressIndicator( strokeWidth: 2, - valueColor: AlwaysStoppedAnimation(AppColors.primaryGreen), + valueColor: AlwaysStoppedAnimation(AppColors.primary), ), ), const SizedBox(width: 12), @@ -213,7 +213,7 @@ class _PerformanceMonitorWidgetState extends State fontSize: 10, fontWeight: FontWeight.bold, letterSpacing: 1.1, - color: AppColors.textPrimaryLight, + color: AppColors.textPrimary, ), ), ), @@ -221,7 +221,7 @@ class _PerformanceMonitorWidgetState extends State const SizedBox(width: 8), Icon( _isExpanded ? Icons.expand_less : Icons.expand_more, - color: AppColors.textSecondaryLight, + color: AppColors.textSecondary, size: 20, ), ], @@ -263,7 +263,7 @@ class _PerformanceMonitorWidgetState extends State label, style: const TextStyle( fontSize: 9, - color: AppColors.textSecondaryLight, + color: AppColors.textSecondary, fontWeight: FontWeight.w500, ), ), @@ -352,7 +352,7 @@ class _PerformanceMonitorWidgetState extends State borderRadius: BorderRadius.circular(2), child: LinearProgressIndicator( value: progress.clamp(0.0, 1.0), - backgroundColor: AppColors.lightBorder, + backgroundColor: AppColors.border, valueColor: AlwaysStoppedAnimation(color), minHeight: 4, ), @@ -410,7 +410,7 @@ class _PerformanceMonitorWidgetState extends State alert.message, style: const TextStyle( fontSize: 11, - color: AppColors.textPrimaryLight, + color: AppColors.textPrimary, ), ), ), @@ -418,7 +418,7 @@ class _PerformanceMonitorWidgetState extends State _formatTime(alert.timestamp), style: const TextStyle( fontSize: 10, - color: AppColors.textSecondaryLight, + color: AppColors.textSecondary, ), ), ], @@ -427,7 +427,7 @@ class _PerformanceMonitorWidgetState extends State } Color _getOverallHealthColor() { - if (_currentMetrics == null) return AppColors.textSecondaryLight; + if (_currentMetrics == null) return AppColors.textSecondary; final metrics = _currentMetrics!; diff --git a/lib/features/dashboard/presentation/widgets/search/dashboard_search_widget.dart b/lib/features/dashboard/presentation/widgets/search/dashboard_search_widget.dart index b9baba7..74230a4 100644 --- a/lib/features/dashboard/presentation/widgets/search/dashboard_search_widget.dart +++ b/lib/features/dashboard/presentation/widgets/search/dashboard_search_widget.dart @@ -125,9 +125,9 @@ class _DashboardSearchWidgetState extends State decoration: BoxDecoration( color: Theme.of(context).cardColor, borderRadius: BorderRadius.circular(12), - boxShadow: _isExpanded - ? [BoxShadow(color: Colors.black.withOpacity(0.1), blurRadius: 10, offset: const Offset(0, 4))] - : [BoxShadow(color: Colors.black.withOpacity(0.05), blurRadius: 5, offset: const Offset(0, 2))], + boxShadow: _isExpanded + ? [BoxShadow(color: AppColors.shadowMedium, blurRadius: 10, offset: const Offset(0, 4))] + : [BoxShadow(color: AppColors.shadow, blurRadius: 5, offset: const Offset(0, 2))], ), child: TextField( controller: _searchController, @@ -141,11 +141,11 @@ class _DashboardSearchWidgetState extends State decoration: InputDecoration( hintText: widget.hintText ?? 'Rechercher...', hintStyle: AppTypography.bodyTextSmall.copyWith( - color: AppColors.textSecondaryLight, + color: AppColors.textSecondary, ), prefixIcon: Icon( Icons.search_outlined, - color: _isExpanded ? AppColors.primaryGreen : AppColors.textSecondaryLight, + color: _isExpanded ? AppColors.primary : AppColors.textSecondary, size: 20, ), suffixIcon: _searchController.text.isNotEmpty @@ -156,7 +156,7 @@ class _DashboardSearchWidgetState extends State }, icon: const Icon( Icons.close_outlined, - color: AppColors.textSecondaryLight, + color: AppColors.textSecondary, size: 18, ), ) @@ -168,7 +168,7 @@ class _DashboardSearchWidgetState extends State focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide( - color: AppColors.primaryGreen, + color: AppColors.primary, width: 1.5, ), ), @@ -195,7 +195,7 @@ class _DashboardSearchWidgetState extends State borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.1), + color: AppColors.shadowMedium, blurRadius: 10, offset: const Offset(0, 4), ), @@ -227,7 +227,7 @@ class _DashboardSearchWidgetState extends State ? null : const Border( bottom: BorderSide( - color: AppColors.lightBorder, + color: AppColors.border, width: 1, ), ), @@ -263,7 +263,7 @@ class _DashboardSearchWidgetState extends State Text( suggestion.subtitle, style: AppTypography.subtitleSmall.copyWith( - color: AppColors.textSecondaryLight, + color: AppColors.textSecondary, fontSize: 10, ), ), @@ -273,7 +273,7 @@ class _DashboardSearchWidgetState extends State ), const Icon( Icons.chevron_right_outlined, - color: AppColors.textSecondaryLight, + color: AppColors.textSecondary, size: 16, ), ], @@ -288,14 +288,14 @@ class _DashboardSearchWidgetState extends State title: 'Membres', subtitle: 'Rechercher des membres', icon: Icons.people_outline, - color: AppColors.primaryGreen, + color: AppColors.primary, onTap: () => Navigator.of(context).push(MaterialPageRoute(builder: (_) => const MembersPageWrapper())), ), SearchSuggestion( title: 'Événements', subtitle: 'Trouver des événements', icon: Icons.event_outlined, - color: AppColors.brandGreen, + color: AppColors.primaryDark, onTap: () => Navigator.of(context).push(MaterialPageRoute(builder: (_) => const EventsPageWrapper())), ), SearchSuggestion( @@ -316,7 +316,7 @@ class _DashboardSearchWidgetState extends State title: 'Paramètres', subtitle: 'Configuration système', icon: Icons.settings_outlined, - color: AppColors.textSecondaryLight, + color: AppColors.textSecondary, onTap: () => Navigator.of(context).push(MaterialPageRoute(builder: (_) => const SystemSettingsPage())), ), ]; diff --git a/lib/features/dashboard/presentation/widgets/settings/theme_selector_widget.dart b/lib/features/dashboard/presentation/widgets/settings/theme_selector_widget.dart index e830277..f1ae677 100644 --- a/lib/features/dashboard/presentation/widgets/settings/theme_selector_widget.dart +++ b/lib/features/dashboard/presentation/widgets/settings/theme_selector_widget.dart @@ -40,7 +40,7 @@ class _ThemeSelectorWidgetState extends State { children: [ Icon( Icons.palette, - color: AppColors.primaryGreen, + color: AppColors.primary, size: 24, ), SizedBox(width: 8), diff --git a/lib/features/dashboard/presentation/widgets/shortcuts/dashboard_shortcuts_widget.dart b/lib/features/dashboard/presentation/widgets/shortcuts/dashboard_shortcuts_widget.dart index 11418e3..22e54e8 100644 --- a/lib/features/dashboard/presentation/widgets/shortcuts/dashboard_shortcuts_widget.dart +++ b/lib/features/dashboard/presentation/widgets/shortcuts/dashboard_shortcuts_widget.dart @@ -41,7 +41,7 @@ class DashboardShortcutsWidget extends StatelessWidget { children: [ const Icon( Icons.flash_on_outlined, - color: AppColors.primaryGreen, + color: AppColors.primary, size: 18, ), const SizedBox(width: 8), @@ -97,7 +97,7 @@ class DashboardShortcutsWidget extends StatelessWidget { Text( shortcut.title, style: AppTypography.subtitleSmall.copyWith( - color: AppColors.textPrimaryLight, + color: AppColors.textPrimary, fontSize: 9, fontWeight: FontWeight.w500, ), @@ -127,7 +127,7 @@ class DashboardShortcutsWidget extends StatelessWidget { DashboardShortcut( title: 'Créer\nÉvénement', icon: Icons.event_available_outlined, - color: AppColors.primaryGreen, + color: AppColors.primary, onTap: () { Navigator.of(context).push( MaterialPageRoute( @@ -139,7 +139,7 @@ class DashboardShortcutsWidget extends StatelessWidget { DashboardShortcut( title: 'Ajouter\nContribution', icon: Icons.account_balance_wallet_outlined, - color: AppColors.brandGreen, + color: AppColors.primaryDark, onTap: () { Navigator.of(context).push( MaterialPageRoute( @@ -163,7 +163,7 @@ class DashboardShortcutsWidget extends StatelessWidget { DashboardShortcut( title: 'Paramètres', icon: Icons.settings_outlined, - color: AppColors.textSecondaryLight, + color: AppColors.textSecondary, onTap: () { Navigator.of(context).push( MaterialPageRoute( diff --git a/lib/features/epargne/presentation/pages/epargne_detail_page.dart b/lib/features/epargne/presentation/pages/epargne_detail_page.dart index e8c4357..da17202 100644 --- a/lib/features/epargne/presentation/pages/epargne_detail_page.dart +++ b/lib/features/epargne/presentation/pages/epargne_detail_page.dart @@ -181,11 +181,9 @@ class _EpargneDetailPageState extends State { final actif = c.statut == 'ACTIF'; return Scaffold( - appBar: AppBar( - title: const Text('Détail du compte'), - backgroundColor: Colors.transparent, - elevation: 0, - foregroundColor: ColorTokens.onSurface, + appBar: UFAppBar( + title: 'Détail du compte', + moduleGradient: ModuleColors.epargneGradient, actions: [ IconButton( icon: const Icon(Icons.history), @@ -197,7 +195,7 @@ class _EpargneDetailPageState extends State { body: Container( width: double.infinity, decoration: const BoxDecoration( - color: AppColors.lightBackground, + color: AppColors.background, ), child: SafeArea( child: RefreshIndicator( diff --git a/lib/features/epargne/presentation/widgets/depot_epargne_dialog.dart b/lib/features/epargne/presentation/widgets/depot_epargne_dialog.dart index 2a24988..f6dc730 100644 --- a/lib/features/epargne/presentation/widgets/depot_epargne_dialog.dart +++ b/lib/features/epargne/presentation/widgets/depot_epargne_dialog.dart @@ -7,6 +7,7 @@ import 'package:file_picker/file_picker.dart'; import '../../../../core/constants/lcb_ft_constants.dart'; import '../../../../core/data/repositories/parametres_lcb_ft_repository.dart'; import '../../../../core/utils/error_formatter.dart'; +import '../../../../shared/design_system/tokens/app_colors.dart'; import '../../data/models/transaction_epargne_request.dart'; import '../../data/repositories/transaction_epargne_repository.dart'; import '../../data/services/document_upload_service.dart'; @@ -116,7 +117,7 @@ class _DepotEpargneDialogState extends State { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('✓ Pièce justificative uploadée avec succès'), - backgroundColor: Colors.green, + backgroundColor: AppColors.success, ), ); } catch (e) { @@ -128,7 +129,7 @@ class _DepotEpargneDialogState extends State { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text('Erreur upload : ${e.toString()}'), - backgroundColor: Colors.red, + backgroundColor: AppColors.error, ), ); } @@ -334,7 +335,7 @@ class _DepotEpargneDialogState extends State { ) : Icon( _pieceJustificativeId != null ? Icons.check_circle : Icons.attach_file, - color: _pieceJustificativeId != null ? Colors.green : null, + color: _pieceJustificativeId != null ? AppColors.success : null, ), label: Text( _pieceJustificativeId != null @@ -344,7 +345,7 @@ class _DepotEpargneDialogState extends State { style: OutlinedButton.styleFrom( minimumSize: const Size(double.infinity, 48), side: _pieceJustificativeId != null - ? const BorderSide(color: Colors.green) + ? const BorderSide(color: AppColors.success) : null, ), ), @@ -362,7 +363,7 @@ class _DepotEpargneDialogState extends State { child: Text( 'Photo ou PDF (max 5 MB)', style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: Colors.grey, + color: AppColors.textTertiary, ), ), ), diff --git a/lib/features/epargne/presentation/widgets/retrait_epargne_dialog.dart b/lib/features/epargne/presentation/widgets/retrait_epargne_dialog.dart index 710a2aa..6581ffe 100644 --- a/lib/features/epargne/presentation/widgets/retrait_epargne_dialog.dart +++ b/lib/features/epargne/presentation/widgets/retrait_epargne_dialog.dart @@ -257,7 +257,7 @@ class _RetraitEpargneDialogState extends State { ) : Icon( _pieceJustificativeId != null ? Icons.check_circle : Icons.attach_file, - color: _pieceJustificativeId != null ? Colors.green : null, + color: _pieceJustificativeId != null ? AppColors.success : null, ), label: Text( _pieceJustificativeId != null @@ -267,7 +267,7 @@ class _RetraitEpargneDialogState extends State { style: OutlinedButton.styleFrom( minimumSize: const Size(double.infinity, 48), side: _pieceJustificativeId != null - ? const BorderSide(color: Colors.green) + ? const BorderSide(color: AppColors.success) : null, ), ), @@ -285,7 +285,7 @@ class _RetraitEpargneDialogState extends State { child: Text( 'Photo ou PDF (max 5 MB)', style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: Colors.grey, + color: AppColors.textTertiary, ), ), ), diff --git a/lib/features/epargne/presentation/widgets/transfert_epargne_dialog.dart b/lib/features/epargne/presentation/widgets/transfert_epargne_dialog.dart index b417dd5..17a8421 100644 --- a/lib/features/epargne/presentation/widgets/transfert_epargne_dialog.dart +++ b/lib/features/epargne/presentation/widgets/transfert_epargne_dialog.dart @@ -323,7 +323,7 @@ class _TransfertEpargneDialogState extends State { ) : Icon( _pieceJustificativeId != null ? Icons.check_circle : Icons.attach_file, - color: _pieceJustificativeId != null ? Colors.green : null, + color: _pieceJustificativeId != null ? AppColors.success : null, ), label: Text( _pieceJustificativeId != null @@ -333,7 +333,7 @@ class _TransfertEpargneDialogState extends State { style: OutlinedButton.styleFrom( minimumSize: const Size(double.infinity, 48), side: _pieceJustificativeId != null - ? const BorderSide(color: Colors.green) + ? const BorderSide(color: AppColors.success) : null, ), ), @@ -351,7 +351,7 @@ class _TransfertEpargneDialogState extends State { child: Text( 'Photo ou PDF (max 5 MB)', style: Theme.of(context).textTheme.bodySmall?.copyWith( - color: Colors.grey, + color: AppColors.textTertiary, ), ), ), diff --git a/lib/features/events/presentation/pages/events_page_connected.dart b/lib/features/events/presentation/pages/events_page_connected.dart index 051ad5d..a457340 100644 --- a/lib/features/events/presentation/pages/events_page_connected.dart +++ b/lib/features/events/presentation/pages/events_page_connected.dart @@ -3,8 +3,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:intl/intl.dart'; import '../../../../core/constants/app_constants.dart'; -import '../../../../shared/design_system/unionflow_design_v2.dart'; -import '../../../../shared/design_system/components/uf_app_bar.dart'; +import '../../../../shared/design_system/unionflow_design_system.dart'; import '../../../authentication/data/models/user_role.dart'; import '../../../authentication/presentation/bloc/auth_bloc.dart'; import '../../bloc/evenements_bloc.dart'; @@ -68,39 +67,52 @@ class _EventsPageWithDataState extends State with TickerProv final canManageEvents = state.effectiveRole.level >= UserRole.moderator.level; return Scaffold( - backgroundColor: UnionFlowColors.background, + backgroundColor: Theme.of(context).scaffoldBackgroundColor, appBar: UFAppBar( title: 'Événements', - backgroundColor: UnionFlowColors.surface, - foregroundColor: UnionFlowColors.textPrimary, + moduleGradient: ModuleColors.evenementsGradient, actions: [ if (canManageEvents && widget.onAddEvent != null) IconButton( icon: const Icon(Icons.add_circle_outline), - color: UnionFlowColors.unionGreen, + color: Colors.white, onPressed: widget.onAddEvent, tooltip: 'Créer un événement', ), const SizedBox(width: 8), ], + bottom: TabBar( + controller: _tabController, + isScrollable: true, + labelColor: Colors.white, + unselectedLabelColor: Colors.white70, + indicatorColor: Colors.white, + indicatorSize: TabBarIndicatorSize.label, + labelStyle: AppTypography.actionText.copyWith(fontSize: 10, fontWeight: FontWeight.bold), + tabs: const [ + Tab(child: Text('TOUS')), + Tab(child: Text('À VENIR')), + Tab(child: Text('EN COURS')), + Tab(child: Text('PASSÉS')), + ], + ), ), body: Column( children: [ - _buildHeader(), - _buildSearchBar(), - _buildTabs(), + _buildHeader(context), + _buildSearchBar(context), Expanded( child: TabBarView( controller: _tabController, children: [ - _buildEventsList(widget.events, 'tous'), - _buildEventsList(widget.events.where((e) => e.estAVenir).toList(), 'à venir'), - _buildEventsList(widget.events.where((e) => e.estEnCours).toList(), 'en cours'), - _buildEventsList(widget.events.where((e) => e.estPasse).toList(), 'passés'), + _buildEventsList(context, widget.events, 'tous'), + _buildEventsList(context, widget.events.where((e) => e.estAVenir).toList(), 'à venir'), + _buildEventsList(context, widget.events.where((e) => e.estEnCours).toList(), 'en cours'), + _buildEventsList(context, widget.events.where((e) => e.estPasse).toList(), 'passés'), ], ), ), - if (widget.totalPages > 1) _buildPagination(), + if (widget.totalPages > 1) _buildPagination(context), ], ), ); @@ -108,7 +120,8 @@ class _EventsPageWithDataState extends State with TickerProv ); } - Widget _buildHeader() { + Widget _buildHeader(BuildContext context) { + final scheme = Theme.of(context).colorScheme; final upcoming = widget.events.where((e) => e.estAVenir).length; final ongoing = widget.events.where((e) => e.estEnCours).length; final total = widget.totalCount; @@ -116,28 +129,28 @@ class _EventsPageWithDataState extends State with TickerProv return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( - color: UnionFlowColors.surface, - border: Border(bottom: BorderSide(color: UnionFlowColors.border.withOpacity(0.5), width: 1)), + color: scheme.surface, + border: Border(bottom: BorderSide(color: scheme.outlineVariant.withOpacity(0.5))), ), child: Row( children: [ - Expanded(child: _buildStatCard(Icons.event_available, 'À venir', upcoming.toString(), UnionFlowColors.success)), + Expanded(child: _buildStatCard(context, Icons.event_available, 'À venir', upcoming.toString(), ColorTokens.success)), const SizedBox(width: 12), - Expanded(child: _buildStatCard(Icons.play_circle_outline, 'En cours', ongoing.toString(), UnionFlowColors.amber)), + Expanded(child: _buildStatCard(context, Icons.play_circle_outline, 'En cours', ongoing.toString(), ColorTokens.warningLight)), const SizedBox(width: 12), - Expanded(child: _buildStatCard(Icons.calendar_today, 'Total', total.toString(), UnionFlowColors.unionGreen)), + Expanded(child: _buildStatCard(context, Icons.calendar_today, 'Total', total.toString(), ModuleColors.evenements)), ], ), ); } - Widget _buildStatCard(IconData icon, String label, String value, Color color) { + Widget _buildStatCard(BuildContext context, IconData icon, String label, String value, Color color) { return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(12), - border: Border.all(color: color.withOpacity(0.3), width: 1), + border: Border.all(color: color.withOpacity(0.3)), ), child: Column( children: [ @@ -151,12 +164,13 @@ class _EventsPageWithDataState extends State with TickerProv ); } - Widget _buildSearchBar() { + Widget _buildSearchBar(BuildContext context) { + final scheme = Theme.of(context).colorScheme; return Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: BoxDecoration( - color: UnionFlowColors.surface, - border: Border(bottom: BorderSide(color: UnionFlowColors.border.withOpacity(0.5), width: 1)), + color: scheme.surface, + border: Border(bottom: BorderSide(color: scheme.outlineVariant.withOpacity(0.5))), ), child: TextField( controller: _searchController, @@ -167,14 +181,14 @@ class _EventsPageWithDataState extends State with TickerProv widget.onSearch?.call(v.isEmpty ? null : v); }); }, - style: const TextStyle(fontSize: 14, color: UnionFlowColors.textPrimary), + style: TextStyle(fontSize: 14, color: scheme.onSurface), decoration: InputDecoration( hintText: 'Rechercher un événement...', - hintStyle: const TextStyle(fontSize: 13, color: UnionFlowColors.textTertiary), - prefixIcon: const Icon(Icons.search, size: 20, color: UnionFlowColors.textSecondary), + hintStyle: TextStyle(fontSize: 13, color: scheme.onSurfaceVariant), + prefixIcon: Icon(Icons.search, size: 20, color: scheme.onSurfaceVariant), suffixIcon: _searchQuery.isNotEmpty ? IconButton( - icon: const Icon(Icons.clear, size: 18, color: UnionFlowColors.textSecondary), + icon: Icon(Icons.clear, size: 18, color: scheme.onSurfaceVariant), onPressed: () { _searchDebounce?.cancel(); _searchController.clear(); @@ -184,95 +198,81 @@ class _EventsPageWithDataState extends State with TickerProv ) : null, contentPadding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16), - border: OutlineInputBorder(borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: UnionFlowColors.border.withOpacity(0.3))), - enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: UnionFlowColors.border.withOpacity(0.3))), - focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: UnionFlowColors.unionGreen, width: 1.5)), + border: OutlineInputBorder(borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: scheme.outlineVariant)), + enabledBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: scheme.outlineVariant)), + focusedBorder: OutlineInputBorder(borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: ModuleColors.evenements, width: 1.5)), filled: true, - fillColor: UnionFlowColors.surfaceVariant.withOpacity(0.3), + fillColor: scheme.surfaceContainerHigh.withOpacity(0.5), ), ), ); } - Widget _buildTabs() { - return Container( - decoration: BoxDecoration( - color: UnionFlowColors.surface, - border: Border(bottom: BorderSide(color: UnionFlowColors.border.withOpacity(0.5), width: 1)), - ), - child: TabBar( - controller: _tabController, - labelColor: UnionFlowColors.unionGreen, - unselectedLabelColor: UnionFlowColors.textSecondary, - indicatorColor: UnionFlowColors.unionGreen, - indicatorWeight: 3, - labelStyle: const TextStyle(fontSize: 13, fontWeight: FontWeight.w700), - unselectedLabelStyle: const TextStyle(fontSize: 13, fontWeight: FontWeight.w600), - tabs: const [Tab(text: 'Tous'), Tab(text: 'À venir'), Tab(text: 'En cours'), Tab(text: 'Passés')], - ), - ); - } + // _buildTabs() supprimé : migré dans UFAppBar.bottom (pattern Adhésions) - Widget _buildEventsList(List events, String type) { + Widget _buildEventsList(BuildContext context, List events, String type) { final filtered = _searchQuery.isEmpty ? events : events.where((e) => e.titre.toLowerCase().contains(_searchQuery.toLowerCase()) || (e.lieu?.toLowerCase().contains(_searchQuery.toLowerCase()) ?? false)).toList(); - if (filtered.isEmpty) return _buildEmptyState(type); + if (filtered.isEmpty) return _buildEmptyState(context, type); return RefreshIndicator( onRefresh: () async => context.read().add(const LoadEvenements()), - color: UnionFlowColors.unionGreen, + color: ModuleColors.evenements, child: ListView.separated( padding: const EdgeInsets.all(12), itemCount: filtered.length, separatorBuilder: (_, __) => const SizedBox(height: 6), - itemBuilder: (context, index) => _buildEventCard(filtered[index]), + itemBuilder: (context, index) => _buildEventCard(context, filtered[index]), ), ); } - Widget _buildEventCard(EvenementModel event) { + Widget _buildEventCard(BuildContext context, EvenementModel event) { + final scheme = Theme.of(context).colorScheme; final df = DateFormat('dd MMM yyyy, HH:mm'); + final statutColor = _getStatutColor(event.statut); return GestureDetector( - onTap: () => _showEventDetails(event), + onTap: () => _showEventDetails(context, event), child: Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 7), decoration: BoxDecoration( - color: UnionFlowColors.surface, + color: scheme.surface, borderRadius: BorderRadius.circular(10), - border: Border(left: BorderSide(color: _getStatutColor(event.statut), width: 4)), + border: Border(left: BorderSide(color: statutColor, width: 4)), + boxShadow: [BoxShadow(color: scheme.shadow.withOpacity(0.04), blurRadius: 4, offset: const Offset(0, 2))], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ - _buildBadge(_mapStatut(event.statut), _getStatutColor(event.statut)), + _buildBadge(_mapStatut(event.statut), statutColor), const SizedBox(width: 8), - _buildBadge(_mapType(event.type), UnionFlowColors.textSecondary), + _buildBadge(_mapType(event.type), scheme.onSurfaceVariant), const Spacer(), - const Icon(Icons.chevron_right, size: 18, color: UnionFlowColors.textTertiary), + Icon(Icons.chevron_right, size: 18, color: scheme.onSurfaceVariant), ], ), const SizedBox(height: 12), - Text(event.titre, style: const TextStyle(fontSize: 15, fontWeight: FontWeight.w700, color: UnionFlowColors.textPrimary), maxLines: 2, overflow: TextOverflow.ellipsis), + Text(event.titre, style: TextStyle(fontSize: 15, fontWeight: FontWeight.w700, color: scheme.onSurface), maxLines: 2, overflow: TextOverflow.ellipsis), const SizedBox(height: 12), Row( children: [ - const Icon(Icons.access_time, size: 14, color: UnionFlowColors.textSecondary), + Icon(Icons.access_time, size: 14, color: scheme.onSurfaceVariant), const SizedBox(width: 6), - Text(df.format(event.dateDebut), style: const TextStyle(fontSize: 12, color: UnionFlowColors.textSecondary)), + Text(df.format(event.dateDebut), style: TextStyle(fontSize: 12, color: scheme.onSurfaceVariant)), ], ), if (event.lieu != null) ...[ const SizedBox(height: 6), Row( children: [ - const Icon(Icons.location_on_outlined, size: 14, color: UnionFlowColors.textSecondary), + Icon(Icons.location_on_outlined, size: 14, color: scheme.onSurfaceVariant), const SizedBox(width: 6), - Expanded(child: Text(event.lieu!, style: const TextStyle(fontSize: 12, color: UnionFlowColors.textSecondary), maxLines: 1, overflow: TextOverflow.ellipsis)), + Expanded(child: Text(event.lieu!, style: TextStyle(fontSize: 12, color: scheme.onSurfaceVariant), maxLines: 1, overflow: TextOverflow.ellipsis)), ], ), ], @@ -282,25 +282,28 @@ class _EventsPageWithDataState extends State with TickerProv Container( width: 24, height: 24, - decoration: const BoxDecoration(gradient: UnionFlowColors.primaryGradient, shape: BoxShape.circle), + decoration: BoxDecoration( + gradient: LinearGradient(colors: ModuleColors.evenementsGradient, begin: Alignment.topLeft, end: Alignment.bottomRight), + shape: BoxShape.circle, + ), alignment: Alignment.center, child: Text(event.organisateurNom?[0] ?? 'O', style: const TextStyle(color: Colors.white, fontWeight: FontWeight.w700, fontSize: 12)), ), const SizedBox(width: 8), - Expanded(child: Text(event.organisateurNom ?? 'Organisateur', style: const TextStyle(fontSize: 11, color: UnionFlowColors.textSecondary), maxLines: 1, overflow: TextOverflow.ellipsis)), + Expanded(child: Text(event.organisateurNom ?? 'Organisateur', style: TextStyle(fontSize: 11, color: scheme.onSurfaceVariant), maxLines: 1, overflow: TextOverflow.ellipsis)), Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( - color: UnionFlowColors.goldPale, + color: ModuleColors.membres.withOpacity(0.1), borderRadius: BorderRadius.circular(12), - border: Border.all(color: UnionFlowColors.gold.withOpacity(0.3), width: 1), + border: Border.all(color: ModuleColors.membres.withOpacity(0.3)), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ - const Icon(Icons.people_outline, size: 12, color: UnionFlowColors.gold), + const Icon(Icons.people_outline, size: 12, color: ModuleColors.membres), const SizedBox(width: 4), - Text('${event.participantsActuels}/${event.maxParticipants ?? "∞"}', style: const TextStyle(fontSize: 11, fontWeight: FontWeight.w600, color: UnionFlowColors.gold)), + Text('${event.participantsActuels}/${event.maxParticipants ?? "∞"}', style: const TextStyle(fontSize: 11, fontWeight: FontWeight.w600, color: ModuleColors.membres)), ], ), ), @@ -318,56 +321,64 @@ class _EventsPageWithDataState extends State with TickerProv decoration: BoxDecoration( color: color.withOpacity(0.1), borderRadius: BorderRadius.circular(8), - border: Border.all(color: color.withOpacity(0.3), width: 1), + border: Border.all(color: color.withOpacity(0.3)), ), child: Text(text, style: TextStyle(fontSize: 10, fontWeight: FontWeight.w600, color: color)), ); } - Widget _buildEmptyState(String type) { + Widget _buildEmptyState(BuildContext context, String type) { + final scheme = Theme.of(context).colorScheme; return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( padding: const EdgeInsets.all(16), - decoration: const BoxDecoration(color: UnionFlowColors.goldPale, shape: BoxShape.circle), - child: const Icon(Icons.event_busy, size: 40, color: UnionFlowColors.gold), + decoration: BoxDecoration(color: ModuleColors.evenements.withOpacity(0.1), shape: BoxShape.circle), + child: Icon(Icons.event_busy, size: 40, color: ModuleColors.evenements), ), const SizedBox(height: 12), - Text('Aucun événement $type', style: const TextStyle(fontSize: 16, fontWeight: FontWeight.w700, color: UnionFlowColors.textPrimary)), + Text('Aucun événement $type', style: TextStyle(fontSize: 16, fontWeight: FontWeight.w700, color: scheme.onSurface)), const SizedBox(height: 8), - Text(_searchQuery.isEmpty ? 'La liste est vide pour le moment' : 'Essayez une autre recherche', style: const TextStyle(fontSize: 13, color: UnionFlowColors.textSecondary)), + Text( + _searchQuery.isEmpty ? 'La liste est vide pour le moment' : 'Essayez une autre recherche', + style: TextStyle(fontSize: 13, color: scheme.onSurfaceVariant), + ), ], ), ); } - Widget _buildPagination() { + Widget _buildPagination(BuildContext context) { + final scheme = Theme.of(context).colorScheme; return Container( padding: const EdgeInsets.symmetric(vertical: 12), decoration: BoxDecoration( - color: UnionFlowColors.surface, - border: Border(top: BorderSide(color: UnionFlowColors.border.withOpacity(0.5), width: 1)), + color: scheme.surface, + border: Border(top: BorderSide(color: scheme.outlineVariant.withOpacity(0.5))), ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ IconButton( icon: const Icon(Icons.chevron_left, size: 24), - color: widget.currentPage > 0 ? UnionFlowColors.unionGreen : UnionFlowColors.textTertiary, + color: widget.currentPage > 0 ? ModuleColors.evenements : scheme.onSurfaceVariant.withOpacity(0.4), onPressed: widget.currentPage > 0 && widget.onPageChanged != null ? () => widget.onPageChanged!(widget.currentPage - 1, _searchQuery.isEmpty ? null : _searchQuery) : null, ), Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), - decoration: BoxDecoration(gradient: UnionFlowColors.primaryGradient, borderRadius: BorderRadius.circular(20)), + decoration: BoxDecoration( + gradient: LinearGradient(colors: ModuleColors.evenementsGradient, begin: Alignment.topLeft, end: Alignment.bottomRight), + borderRadius: BorderRadius.circular(20), + ), child: Text('Page ${widget.currentPage + 1} / ${widget.totalPages}', style: const TextStyle(fontSize: 13, fontWeight: FontWeight.w600, color: Colors.white)), ), IconButton( icon: const Icon(Icons.chevron_right, size: 24), - color: widget.currentPage < widget.totalPages - 1 ? UnionFlowColors.unionGreen : UnionFlowColors.textTertiary, + color: widget.currentPage < widget.totalPages - 1 ? ModuleColors.evenements : scheme.onSurfaceVariant.withOpacity(0.4), onPressed: widget.currentPage < widget.totalPages - 1 && widget.onPageChanged != null ? () => widget.onPageChanged!(widget.currentPage + 1, _searchQuery.isEmpty ? null : _searchQuery) : null, @@ -379,64 +390,42 @@ class _EventsPageWithDataState extends State with TickerProv String _mapStatut(StatutEvenement s) { switch (s) { - case StatutEvenement.planifie: - return 'Planifié'; - case StatutEvenement.confirme: - return 'Confirmé'; - case StatutEvenement.enCours: - return 'En cours'; - case StatutEvenement.termine: - return 'Terminé'; - case StatutEvenement.annule: - return 'Annulé'; - case StatutEvenement.reporte: - return 'Reporté'; + case StatutEvenement.planifie: return 'Planifié'; + case StatutEvenement.confirme: return 'Confirmé'; + case StatutEvenement.enCours: return 'En cours'; + case StatutEvenement.termine: return 'Terminé'; + case StatutEvenement.annule: return 'Annulé'; + case StatutEvenement.reporte: return 'Reporté'; } } Color _getStatutColor(StatutEvenement s) { switch (s) { - case StatutEvenement.planifie: - return UnionFlowColors.info; - case StatutEvenement.confirme: - return UnionFlowColors.success; - case StatutEvenement.enCours: - return UnionFlowColors.amber; - case StatutEvenement.termine: - return UnionFlowColors.textSecondary; - case StatutEvenement.annule: - return UnionFlowColors.error; - case StatutEvenement.reporte: - return UnionFlowColors.warning; + case StatutEvenement.planifie: return ColorTokens.info; + case StatutEvenement.confirme: return ColorTokens.success; + case StatutEvenement.enCours: return ColorTokens.warningLight; + case StatutEvenement.termine: return ColorTokens.textSecondary; + case StatutEvenement.annule: return ColorTokens.error; + case StatutEvenement.reporte: return ColorTokens.warning; } } String _mapType(TypeEvenement t) { switch (t) { - case TypeEvenement.assembleeGenerale: - return 'AG'; - case TypeEvenement.reunion: - return 'Réunion'; - case TypeEvenement.formation: - return 'Formation'; - case TypeEvenement.conference: - return 'Conférence'; - case TypeEvenement.atelier: - return 'Atelier'; - case TypeEvenement.seminaire: - return 'Séminaire'; - case TypeEvenement.evenementSocial: - return 'Social'; - case TypeEvenement.manifestation: - return 'Manif.'; - case TypeEvenement.celebration: - return 'Célébr.'; - case TypeEvenement.autre: - return 'Autre'; + case TypeEvenement.assembleeGenerale: return 'AG'; + case TypeEvenement.reunion: return 'Réunion'; + case TypeEvenement.formation: return 'Formation'; + case TypeEvenement.conference: return 'Conférence'; + case TypeEvenement.atelier: return 'Atelier'; + case TypeEvenement.seminaire: return 'Séminaire'; + case TypeEvenement.evenementSocial: return 'Social'; + case TypeEvenement.manifestation: return 'Manif.'; + case TypeEvenement.celebration: return 'Célébr.'; + case TypeEvenement.autre: return 'Autre'; } } - void _showEventDetails(EvenementModel event) { + void _showEventDetails(BuildContext context, EvenementModel event) { final bloc = context.read(); Navigator.of(context).push( MaterialPageRoute( diff --git a/lib/features/events/presentation/pages/events_page_connected.dart.backup b/lib/features/events/presentation/pages/events_page_connected.dart.backup deleted file mode 100644 index 3aa68ce..0000000 --- a/lib/features/events/presentation/pages/events_page_connected.dart.backup +++ /dev/null @@ -1,830 +0,0 @@ -import 'package:flutter/material.dart'; -import 'package:flutter_bloc/flutter_bloc.dart'; -import 'package:intl/intl.dart'; -import '../../../../core/utils/logger.dart'; -import '../../../../shared/widgets/info_badge.dart'; -import '../../../../shared/widgets/mini_avatar.dart'; -import '../../../../shared/design_system/unionflow_design_system.dart'; -import '../../../../shared/design_system/tokens/app_typography.dart'; -import '../../../authentication/data/models/user_role.dart'; -import '../../../authentication/presentation/bloc/auth_bloc.dart'; -import '../../bloc/evenements_bloc.dart'; -import '../../bloc/evenements_event.dart'; -import '../../data/models/evenement_model.dart'; - -/// Page de gestion des événements - UI/UX Premium -class EventsPageWithData extends StatefulWidget { - final List events; - final int totalCount; - final int currentPage; - final int totalPages; - - const EventsPageWithData({ - super.key, - required this.events, - required this.totalCount, - required this.currentPage, - required this.totalPages, - }); - - @override - State createState() => _EventsPageWithDataState(); -} - -class _EventsPageWithDataState extends State - with TickerProviderStateMixin { - final TextEditingController _searchController = TextEditingController(); - late TabController _tabController; - String _searchQuery = ''; - bool _isSearchExpanded = false; - - @override - void initState() { - super.initState(); - _tabController = TabController(length: 5, vsync: this); - } - - @override - void dispose() { - _searchController.dispose(); - _tabController.dispose(); - super.dispose(); - } - - @override - Widget build(BuildContext context) { - return BlocBuilder( - builder: (context, state) { - if (state is! AuthAuthenticated) { - return const Scaffold(body: Center(child: CircularProgressIndicator())); - } - - final canManageEvents = state.effectiveRole.level >= UserRole.moderator.level; - - return Scaffold( - backgroundColor: ColorTokens.background, - appBar: UFAppBar( - title: 'ÉVÉNEMENTS', - automaticallyImplyLeading: false, - actions: [ - // Search toggle button - IconButton( - icon: Icon( - _isSearchExpanded ? Icons.close : Icons.search, - color: ColorTokens.onSurface, - size: 20, - ), - onPressed: () { - setState(() { - _isSearchExpanded = !_isSearchExpanded; - if (!_isSearchExpanded) { - _searchController.clear(); - _searchQuery = ''; - } - }); - }, - ), - if (canManageEvents) - IconButton( - icon: const Icon( - Icons.add_circle_outline, - color: ColorTokens.primary, - size: 22, - ), - onPressed: () => AppLogger.info('Add Event clicked'), - ), - const SizedBox(width: SpacingTokens.xs), - ], - ), - body: Column( - children: [ - // Header avec stats compactes - _buildCompactHeader(), - - // Search bar (expandable) - if (_isSearchExpanded) _buildSearchBar(), - - // Tabs avec badges - _buildEnhancedTabBar(), - - // Liste d'événements - Expanded( - child: TabBarView( - controller: _tabController, - children: [ - _buildEventsList(widget.events, 'tous'), - _buildEventsList(widget.events.where((e) => e.estAVenir).toList(), 'à venir'), - _buildEventsList(widget.events.where((e) => e.estEnCours).toList(), 'en cours'), - _buildEventsList(widget.events.where((e) => e.estPasse).toList(), 'passés'), - _buildCalendarView(), - ], - ), - ), - - // Pagination - if (widget.totalPages > 1) _buildEnhancedPagination(), - ], - ), - ); - }, - ); - } - - /// Header compact avec 3 métriques en ligne - Widget _buildCompactHeader() { - final upcoming = widget.events.where((e) => e.estAVenir).length; - final ongoing = widget.events.where((e) => e.estEnCours).length; - final total = widget.totalCount; - - return Container( - padding: const EdgeInsets.symmetric( - horizontal: SpacingTokens.md, - vertical: SpacingTokens.sm, - ), - decoration: BoxDecoration( - color: ColorTokens.surface, - border: Border( - bottom: BorderSide( - color: ColorTokens.outlineVariant.withOpacity(0.5), - width: 1, - ), - ), - ), - child: Row( - children: [ - _buildCompactMetric( - icon: Icons.event_available, - label: 'À venir', - value: upcoming.toString(), - color: ColorTokens.success, - ), - const SizedBox(width: SpacingTokens.md), - _buildCompactMetric( - icon: Icons.play_circle_outline, - label: 'En cours', - value: ongoing.toString(), - color: ColorTokens.primary, - ), - const Spacer(), - _buildTotalBadge(total), - ], - ), - ); - } - - Widget _buildCompactMetric({ - required IconData icon, - required String label, - required String value, - required Color color, - }) { - return Row( - mainAxisSize: MainAxisSize.min, - children: [ - Container( - padding: const EdgeInsets.all(6), - decoration: BoxDecoration( - color: color.withOpacity(0.1), - borderRadius: BorderRadius.circular(RadiusTokens.sm), - ), - child: Icon(icon, size: 14, color: color), - ), - const SizedBox(width: SpacingTokens.xs), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Text( - value, - style: AppTypography.headerSmall.copyWith( - fontSize: 16, - height: 1.2, - ), - ), - Text( - label, - style: TypographyTokens.labelSmall.copyWith( - fontSize: 10, - color: ColorTokens.onSurfaceVariant, - ), - ), - ], - ), - ], - ); - } - - Widget _buildTotalBadge(int total) { - return Container( - padding: const EdgeInsets.symmetric( - horizontal: SpacingTokens.sm, - vertical: SpacingTokens.xs, - ), - decoration: BoxDecoration( - color: ColorTokens.secondaryContainer, - borderRadius: BorderRadius.circular(RadiusTokens.round), - border: Border.all( - color: ColorTokens.secondary.withOpacity(0.3), - width: 1, - ), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text( - total.toString(), - style: AppTypography.actionText.copyWith( - color: ColorTokens.secondary, - fontWeight: FontWeight.bold, - fontSize: 14, - ), - ), - const SizedBox(width: 4), - Text( - 'TOTAL', - style: TypographyTokens.labelSmall.copyWith( - color: ColorTokens.secondary, - fontSize: 10, - fontWeight: FontWeight.w600, - ), - ), - ], - ), - ); - } - - /// Search bar avec animation - Widget _buildSearchBar() { - return AnimatedContainer( - duration: const Duration(milliseconds: 200), - padding: const EdgeInsets.symmetric( - horizontal: SpacingTokens.md, - vertical: SpacingTokens.sm, - ), - decoration: BoxDecoration( - color: ColorTokens.surface, - border: Border( - bottom: BorderSide( - color: ColorTokens.outlineVariant.withOpacity(0.5), - width: 1, - ), - ), - ), - child: TextField( - controller: _searchController, - autofocus: true, - onChanged: (v) => setState(() => _searchQuery = v), - style: TypographyTokens.bodyMedium, - decoration: InputDecoration( - hintText: 'Rechercher un événement...', - hintStyle: TypographyTokens.bodySmall.copyWith( - color: ColorTokens.onSurfaceVariant, - ), - prefixIcon: const Icon( - Icons.search, - size: 18, - color: ColorTokens.onSurfaceVariant, - ), - suffixIcon: _searchQuery.isNotEmpty - ? IconButton( - icon: const Icon( - Icons.clear, - size: 18, - color: ColorTokens.onSurfaceVariant, - ), - onPressed: () { - setState(() { - _searchController.clear(); - _searchQuery = ''; - }); - }, - ) - : null, - contentPadding: const EdgeInsets.symmetric( - vertical: SpacingTokens.sm, - horizontal: SpacingTokens.xs, - ), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(RadiusTokens.md), - borderSide: BorderSide( - color: ColorTokens.outline.withOpacity(0.3), - ), - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(RadiusTokens.md), - borderSide: BorderSide( - color: ColorTokens.outline.withOpacity(0.3), - ), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(RadiusTokens.md), - borderSide: const BorderSide( - color: ColorTokens.primary, - width: 1.5, - ), - ), - filled: true, - fillColor: ColorTokens.surfaceVariant.withOpacity(0.3), - ), - ), - ); - } - - /// TabBar amélioré avec badges de comptage - Widget _buildEnhancedTabBar() { - final allCount = widget.events.length; - final upcomingCount = widget.events.where((e) => e.estAVenir).length; - final ongoingCount = widget.events.where((e) => e.estEnCours).length; - final pastCount = widget.events.where((e) => e.estPasse).length; - - return Container( - decoration: BoxDecoration( - color: ColorTokens.surface, - border: Border( - bottom: BorderSide( - color: ColorTokens.outlineVariant.withOpacity(0.5), - width: 1, - ), - ), - ), - child: TabBar( - controller: _tabController, - labelColor: ColorTokens.primary, - unselectedLabelColor: ColorTokens.onSurfaceVariant, - indicatorColor: ColorTokens.primary, - indicatorWeight: 2.5, - labelStyle: TypographyTokens.labelMedium.copyWith( - fontWeight: FontWeight.bold, - ), - unselectedLabelStyle: TypographyTokens.labelMedium, - tabs: [ - _buildTabWithBadge('Tous', allCount), - _buildTabWithBadge('À venir', upcomingCount), - _buildTabWithBadge('En cours', ongoingCount), - _buildTabWithBadge('Passés', pastCount), - const Tab( - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Icon(Icons.calendar_month, size: 14), - SizedBox(width: 4), - Text('Calendrier'), - ], - ), - ), - ], - ), - ); - } - - Widget _buildTabWithBadge(String label, int count) { - return Tab( - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - Text(label), - if (count > 0) ...[ - const SizedBox(width: 4), - Container( - padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), - decoration: BoxDecoration( - color: ColorTokens.primary.withOpacity(0.15), - borderRadius: BorderRadius.circular(RadiusTokens.round), - ), - child: Text( - count.toString(), - style: TypographyTokens.labelSmall.copyWith( - fontSize: 9, - fontWeight: FontWeight.bold, - color: ColorTokens.primary, - ), - ), - ), - ], - ], - ), - ); - } - - /// Liste d'événements optimisée - Widget _buildEventsList(List events, String type) { - final filtered = _searchQuery.isEmpty - ? events - : events.where((e) => - e.titre.toLowerCase().contains(_searchQuery.toLowerCase()) || - (e.lieu?.toLowerCase().contains(_searchQuery.toLowerCase()) ?? false) - ).toList(); - - if (filtered.isEmpty) { - return _buildEmptyState(type); - } - - return RefreshIndicator( - onRefresh: () async => context.read().add(const LoadEvenements()), - color: ColorTokens.primary, - child: ListView.separated( - padding: const EdgeInsets.all(SpacingTokens.md), - itemCount: filtered.length, - separatorBuilder: (_, __) => const SizedBox(height: SpacingTokens.sm), - itemBuilder: (context, index) => _buildEnhancedEventCard(filtered[index]), - ), - ); - } - - /// Empty state élégant - Widget _buildEmptyState(String type) { - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - padding: const EdgeInsets.all(SpacingTokens.lg), - decoration: BoxDecoration( - color: ColorTokens.surfaceVariant.withOpacity(0.3), - shape: BoxShape.circle, - ), - child: Icon( - Icons.event_busy, - size: 48, - color: ColorTokens.onSurfaceVariant.withOpacity(0.5), - ), - ), - const SizedBox(height: SpacingTokens.md), - Text( - 'Aucun événement $type', - style: AppTypography.headerSmall.copyWith( - color: ColorTokens.onSurfaceVariant, - ), - ), - const SizedBox(height: SpacingTokens.xs), - Text( - _searchQuery.isEmpty - ? 'La liste est vide pour le moment' - : 'Aucun résultat pour "$_searchQuery"', - style: TypographyTokens.bodySmall.copyWith( - color: ColorTokens.onSurfaceVariant.withOpacity(0.7), - ), - ), - ], - ), - ); - } - - /// Card événement améliorée - Widget _buildEnhancedEventCard(EvenementModel event) { - final df = DateFormat('dd MMM yyyy, HH:mm'); - final isUrgent = event.estAVenir && - event.dateDebut.difference(DateTime.now()).inDays <= 2; - - return UFCard( - margin: EdgeInsets.zero, - onTap: () => _showEventDetails(event), - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Header avec badges - Row( - children: [ - InfoBadge( - text: _mapStatut(event.statut), - backgroundColor: _getStatutColor(event.statut).withOpacity(0.12), - textColor: _getStatutColor(event.statut), - ), - const SizedBox(width: SpacingTokens.xs), - InfoBadge.neutral(_mapType(event.type)), - if (isUrgent) ...[ - const SizedBox(width: SpacingTokens.xs), - InfoBadge( - text: 'URGENT', - backgroundColor: ColorTokens.error.withOpacity(0.12), - textColor: ColorTokens.error, - ), - ], - const Spacer(), - Icon( - Icons.chevron_right, - size: 16, - color: ColorTokens.onSurfaceVariant.withOpacity(0.5), - ), - ], - ), - const SizedBox(height: SpacingTokens.sm), - - // Titre - Text( - event.titre, - style: AppTypography.headerSmall.copyWith(fontSize: 15), - maxLines: 2, - overflow: TextOverflow.ellipsis, - ), - - // Date et lieu - const SizedBox(height: SpacingTokens.sm), - Row( - children: [ - Icon( - Icons.access_time, - size: 12, - color: ColorTokens.onSurfaceVariant, - ), - const SizedBox(width: 4), - Expanded( - child: Text( - df.format(event.dateDebut), - style: AppTypography.subtitleSmall, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ), - ], - ), - - if (event.lieu != null) ...[ - const SizedBox(height: 4), - Row( - children: [ - const Icon( - Icons.location_on_outlined, - size: 12, - color: ColorTokens.onSurfaceVariant, - ), - const SizedBox(width: 4), - Expanded( - child: Text( - event.lieu!, - style: AppTypography.subtitleSmall, - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ), - ], - ), - ], - - // Footer avec organisateur et participants - const SizedBox(height: SpacingTokens.md), - Row( - children: [ - MiniAvatar( - fallbackText: event.organisateurNom?[0] ?? 'O', - size: 20, - ), - const SizedBox(width: SpacingTokens.xs), - Expanded( - child: Text( - event.organisateurNom ?? 'Organisateur', - style: TypographyTokens.labelSmall.copyWith(fontSize: 11), - maxLines: 1, - overflow: TextOverflow.ellipsis, - ), - ), - Container( - padding: const EdgeInsets.symmetric( - horizontal: SpacingTokens.xs, - vertical: 2, - ), - decoration: BoxDecoration( - color: ColorTokens.surfaceVariant, - borderRadius: BorderRadius.circular(RadiusTokens.sm), - ), - child: Row( - mainAxisSize: MainAxisSize.min, - children: [ - const Icon( - Icons.people_outline, - size: 11, - color: ColorTokens.onSurfaceVariant, - ), - const SizedBox(width: 4), - Text( - '${event.participantsActuels}/${event.maxParticipants ?? "∞"}', - style: AppTypography.badgeText.copyWith(fontSize: 10), - ), - ], - ), - ), - ], - ), - ], - ), - ); - } - - /// Vue calendrier placeholder - Widget _buildCalendarView() { - return Center( - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - Container( - padding: const EdgeInsets.all(SpacingTokens.xl), - decoration: BoxDecoration( - color: ColorTokens.surfaceVariant.withOpacity(0.3), - shape: BoxShape.circle, - ), - child: Icon( - Icons.calendar_month, - size: 64, - color: ColorTokens.onSurfaceVariant.withOpacity(0.5), - ), - ), - const SizedBox(height: SpacingTokens.lg), - Text( - 'Vue Calendrier', - style: AppTypography.headerSmall.copyWith(fontSize: 16), - ), - const SizedBox(height: SpacingTokens.xs), - Text( - 'Bientôt disponible', - style: TypographyTokens.bodySmall.copyWith( - color: ColorTokens.onSurfaceVariant, - ), - ), - ], - ), - ); - } - - /// Pagination améliorée - Widget _buildEnhancedPagination() { - return Container( - padding: const EdgeInsets.symmetric(vertical: SpacingTokens.sm), - decoration: BoxDecoration( - color: ColorTokens.surface, - border: Border( - top: BorderSide( - color: ColorTokens.outlineVariant.withOpacity(0.5), - width: 1, - ), - ), - ), - child: Row( - mainAxisAlignment: MainAxisAlignment.center, - children: [ - IconButton( - icon: const Icon(Icons.chevron_left, size: 20), - color: widget.currentPage > 0 - ? ColorTokens.primary - : ColorTokens.onSurfaceVariant.withOpacity(0.3), - onPressed: widget.currentPage > 0 - ? () => context.read().add( - LoadEvenements(page: widget.currentPage - 1), - ) - : null, - ), - Container( - padding: const EdgeInsets.symmetric( - horizontal: SpacingTokens.md, - vertical: SpacingTokens.xs, - ), - decoration: BoxDecoration( - color: ColorTokens.primaryContainer.withOpacity(0.3), - borderRadius: BorderRadius.circular(RadiusTokens.md), - ), - child: Text( - 'Page ${widget.currentPage + 1} / ${widget.totalPages}', - style: TypographyTokens.labelMedium.copyWith( - fontWeight: FontWeight.bold, - color: ColorTokens.primary, - ), - ), - ), - IconButton( - icon: const Icon(Icons.chevron_right, size: 20), - color: widget.currentPage < widget.totalPages - 1 - ? ColorTokens.primary - : ColorTokens.onSurfaceVariant.withOpacity(0.3), - onPressed: widget.currentPage < widget.totalPages - 1 - ? () => context.read().add( - LoadEvenements(page: widget.currentPage + 1), - ) - : null, - ), - ], - ), - ); - } - - String _mapStatut(StatutEvenement s) { - switch(s) { - case StatutEvenement.planifie: return 'Planifié'; - case StatutEvenement.confirme: return 'Confirmé'; - case StatutEvenement.enCours: return 'En cours'; - case StatutEvenement.termine: return 'Terminé'; - case StatutEvenement.annule: return 'Annulé'; - case StatutEvenement.reporte: return 'Reporté'; - } - } - - Color _getStatutColor(StatutEvenement s) { - switch(s) { - case StatutEvenement.planifie: return ColorTokens.info; - case StatutEvenement.confirme: return ColorTokens.success; - case StatutEvenement.enCours: return ColorTokens.primary; - case StatutEvenement.termine: return ColorTokens.secondary; - case StatutEvenement.annule: return ColorTokens.error; - case StatutEvenement.reporte: return ColorTokens.warning; - } - } - - String _mapType(TypeEvenement t) { - switch(t) { - case TypeEvenement.assembleeGenerale: return 'AG'; - case TypeEvenement.reunion: return 'Réunion'; - case TypeEvenement.formation: return 'Formation'; - case TypeEvenement.conference: return 'Conférence'; - case TypeEvenement.atelier: return 'Atelier'; - case TypeEvenement.seminaire: return 'Séminaire'; - case TypeEvenement.evenementSocial: return 'Social'; - case TypeEvenement.manifestation: return 'Manifestation'; - case TypeEvenement.celebration: return 'Célébration'; - case TypeEvenement.autre: return 'Autre'; - } - } - - void _showEventDetails(EvenementModel event) { - showModalBottomSheet( - context: context, - backgroundColor: ColorTokens.surface, - shape: const RoundedRectangleBorder( - borderRadius: BorderRadius.vertical(top: Radius.circular(RadiusTokens.lg)), - ), - builder: (context) => Container( - padding: const EdgeInsets.all(SpacingTokens.xl), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - // Header - Row( - children: [ - Expanded( - child: Text( - event.titre, - style: AppTypography.headerSmall.copyWith(fontSize: 16), - maxLines: 2, - overflow: TextOverflow.ellipsis, - ), - ), - IconButton( - icon: const Icon(Icons.close, size: 20), - onPressed: () => Navigator.pop(context), - ), - ], - ), - const SizedBox(height: SpacingTokens.md), - - // Badges - Wrap( - spacing: SpacingTokens.xs, - runSpacing: SpacingTokens.xs, - children: [ - InfoBadge( - text: _mapStatut(event.statut), - backgroundColor: _getStatutColor(event.statut).withOpacity(0.12), - textColor: _getStatutColor(event.statut), - ), - InfoBadge.neutral(_mapType(event.type)), - ], - ), - - const SizedBox(height: SpacingTokens.lg), - - // Description - if (event.description != null && event.description!.isNotEmpty) ...[ - Text( - 'Description', - style: AppTypography.actionText.copyWith(fontSize: 12), - ), - const SizedBox(height: SpacingTokens.xs), - Text( - event.description!, - style: TypographyTokens.bodySmall, - ), - const SizedBox(height: SpacingTokens.lg), - ], - - // Actions - Row( - children: [ - Expanded( - child: UFSecondaryButton( - label: 'Partager', - onPressed: () => AppLogger.info('Share event'), - ), - ), - const SizedBox(width: SpacingTokens.sm), - Expanded( - child: UFPrimaryButton( - label: 'Détails', - onPressed: () => AppLogger.info('View details'), - ), - ), - ], - ), - ], - ), - ), - ); - } -} diff --git a/lib/features/events/presentation/pages/events_page_wrapper.dart b/lib/features/events/presentation/pages/events_page_wrapper.dart index 4c76cc9..c416c2d 100644 --- a/lib/features/events/presentation/pages/events_page_wrapper.dart +++ b/lib/features/events/presentation/pages/events_page_wrapper.dart @@ -8,6 +8,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:get_it/get_it.dart'; +import '../../../../shared/design_system/tokens/color_tokens.dart'; import '../../../../shared/widgets/error_widget.dart'; import '../../../../shared/widgets/loading_widget.dart'; import '../../../../core/utils/logger.dart'; @@ -57,7 +58,7 @@ class EventsPageConnected extends StatelessWidget { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(state.message), - backgroundColor: Colors.red, + backgroundColor: ColorTokens.error, duration: const Duration(seconds: 4), action: SnackBarAction( label: 'Réessayer', @@ -76,40 +77,28 @@ class EventsPageConnected extends StatelessWidget { // État initial if (state is EvenementsInitial) { - return Container( - color: const Color(0xFFF8F9FA), - child: const Center( - child: AppLoadingWidget(message: 'Initialisation...'), - ), + return const Center( + child: AppLoadingWidget(message: 'Initialisation...'), ); } // État de chargement if (state is EvenementsLoading) { - return Container( - color: const Color(0xFFF8F9FA), - child: const Center( - child: AppLoadingWidget(message: 'Chargement des événements...'), - ), + return const Center( + child: AppLoadingWidget(message: 'Chargement des événements...'), ); } // État de rafraîchissement if (state is EvenementsRefreshing) { - return Container( - color: const Color(0xFFF8F9FA), - child: const Center( - child: AppLoadingWidget(message: 'Actualisation...'), - ), + return const Center( + child: AppLoadingWidget(message: 'Actualisation...'), ); } if (state is EvenementCreated) { - return Container( - color: const Color(0xFFF8F9FA), - child: const Center( - child: AppLoadingWidget(message: 'Actualisation...'), - ), + return const Center( + child: AppLoadingWidget(message: 'Actualisation...'), ); } @@ -140,39 +129,30 @@ class EventsPageConnected extends StatelessWidget { // État d'erreur réseau if (state is EvenementsNetworkError) { AppLogger.error('EventsPageConnected: Erreur réseau', error: state.message); - return Container( - color: const Color(0xFFF8F9FA), - child: NetworkErrorWidget( - onRetry: () { - AppLogger.userAction('Retry load evenements after network error'); - context.read().add(const LoadEvenements()); - }, - ), + return NetworkErrorWidget( + onRetry: () { + AppLogger.userAction('Retry load evenements after network error'); + context.read().add(const LoadEvenements()); + }, ); } // État d'erreur générale if (state is EvenementsError) { AppLogger.error('EventsPageConnected: Erreur', error: state.message); - return Container( - color: const Color(0xFFF8F9FA), - child: AppErrorWidget( - message: state.message, - onRetry: () { - AppLogger.userAction('Retry load evenements after error'); - context.read().add(const LoadEvenements()); - }, - ), + return AppErrorWidget( + message: state.message, + onRetry: () { + AppLogger.userAction('Retry load evenements after error'); + context.read().add(const LoadEvenements()); + }, ); } // État par défaut AppLogger.warning('EventsPageConnected: État non géré: ${state.runtimeType}'); - return Container( - color: const Color(0xFFF8F9FA), - child: const Center( - child: AppLoadingWidget(message: 'Chargement...'), - ), + return const Center( + child: AppLoadingWidget(message: 'Chargement...'), ); }, ), diff --git a/lib/features/events/presentation/widgets/create_event_dialog.dart b/lib/features/events/presentation/widgets/create_event_dialog.dart index b8cd94b..c641109 100644 --- a/lib/features/events/presentation/widgets/create_event_dialog.dart +++ b/lib/features/events/presentation/widgets/create_event_dialog.dart @@ -3,6 +3,8 @@ library create_event_dialog; import 'package:flutter/material.dart'; +import '../../../../shared/design_system/tokens/module_colors.dart'; +import '../../../../shared/design_system/tokens/color_tokens.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:intl/intl.dart'; import '../../bloc/evenements_bloc.dart'; @@ -55,7 +57,7 @@ class _CreateEventDialogState extends State { if (state is EvenementsError) { setState(() => _createSent = false); ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(state.message), backgroundColor: Colors.red), + SnackBar(content: Text(state.message), backgroundColor: ColorTokens.error), ); return; } @@ -65,7 +67,7 @@ class _CreateEventDialogState extends State { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Événement créé avec succès'), - backgroundColor: Colors.green, + backgroundColor: ColorTokens.success, ), ); } @@ -81,7 +83,7 @@ class _CreateEventDialogState extends State { Container( padding: const EdgeInsets.all(16), decoration: const BoxDecoration( - color: Color(0xFF3B82F6), + color: ModuleColors.evenements, borderRadius: BorderRadius.only( topLeft: Radius.circular(4), topRight: Radius.circular(4), @@ -152,25 +154,32 @@ class _CreateEventDialogState extends State { const SizedBox(height: 12), // Type d'événement - DropdownButtonFormField( - value: _selectedType, - decoration: const InputDecoration( - labelText: 'Type d\'événement *', - border: OutlineInputBorder(), - prefixIcon: Icon(Icons.category), - ), - items: TypeEvenement.values.map((type) { - return DropdownMenuItem( - value: type, - child: Text(_getTypeLabel(type)), - ); - }).toList(), - onChanged: (value) { - setState(() { - _selectedType = value!; - }); - }, - ), + Builder(builder: (context) { + final scheme = Theme.of(context).colorScheme; + return DropdownButtonFormField( + value: _selectedType, + isExpanded: true, + menuMaxHeight: 220, + dropdownColor: scheme.surface, + style: Theme.of(context).textTheme.bodyLarge?.copyWith(color: scheme.onSurface), + decoration: const InputDecoration( + labelText: 'Type d\'événement *', + border: OutlineInputBorder(), + prefixIcon: Icon(Icons.category), + ), + items: TypeEvenement.values.map((type) { + return DropdownMenuItem( + value: type, + child: Text(_getTypeLabel(type), overflow: TextOverflow.ellipsis, style: TextStyle(color: scheme.onSurface)), + ); + }).toList(), + onChanged: (value) { + setState(() { + _selectedType = value!; + }); + }, + ); + }), const SizedBox(height: 16), // Dates @@ -296,8 +305,8 @@ class _CreateEventDialogState extends State { Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: Colors.grey[100], - border: Border(top: BorderSide(color: Colors.grey[300]!)), + color: Theme.of(context).colorScheme.surfaceContainerHighest, + border: Border(top: BorderSide(color: Theme.of(context).colorScheme.outline)), ), child: Row( mainAxisAlignment: MainAxisAlignment.end, @@ -310,7 +319,7 @@ class _CreateEventDialogState extends State { ElevatedButton( onPressed: _submitForm, style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF3B82F6), + backgroundColor: ModuleColors.evenements, foregroundColor: Colors.white, ), child: const Text('Créer l\'événement'), @@ -331,7 +340,7 @@ class _CreateEventDialogState extends State { style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, - color: Color(0xFF3B82F6), + color: ModuleColors.evenements, ), ); } diff --git a/lib/features/events/presentation/widgets/edit_event_dialog.dart b/lib/features/events/presentation/widgets/edit_event_dialog.dart index 08a0f14..0c6b199 100644 --- a/lib/features/events/presentation/widgets/edit_event_dialog.dart +++ b/lib/features/events/presentation/widgets/edit_event_dialog.dart @@ -3,6 +3,8 @@ library edit_event_dialog; import 'package:flutter/material.dart'; +import '../../../../shared/design_system/tokens/module_colors.dart'; +import '../../../../shared/design_system/tokens/color_tokens.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:intl/intl.dart'; import '../../bloc/evenements_bloc.dart'; @@ -83,7 +85,7 @@ class _EditEventDialogState extends State { if (state is EvenementsError) { setState(() => _updateSent = false); ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(state.message), backgroundColor: Colors.red), + SnackBar(content: Text(state.message), backgroundColor: ColorTokens.error), ); return; } @@ -93,7 +95,7 @@ class _EditEventDialogState extends State { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text('Événement modifié avec succès'), - backgroundColor: Colors.green, + backgroundColor: ColorTokens.success, ), ); } @@ -109,7 +111,7 @@ class _EditEventDialogState extends State { Container( padding: const EdgeInsets.all(16), decoration: const BoxDecoration( - color: Color(0xFF3B82F6), + color: ModuleColors.evenements, borderRadius: BorderRadius.only( topLeft: Radius.circular(4), topRight: Radius.circular(4), @@ -180,53 +182,63 @@ class _EditEventDialogState extends State { const SizedBox(height: 12), // Type et statut - Row( - children: [ - Expanded( - child: DropdownButtonFormField( - value: _selectedType, - decoration: const InputDecoration( - labelText: 'Type *', - border: OutlineInputBorder(), - prefixIcon: Icon(Icons.category), + Builder(builder: (context) { + final scheme = Theme.of(context).colorScheme; + return Row( + children: [ + Expanded( + child: DropdownButtonFormField( + value: _selectedType, + isExpanded: true, + menuMaxHeight: 220, + dropdownColor: scheme.surface, + style: Theme.of(context).textTheme.bodyLarge?.copyWith(color: scheme.onSurface), + decoration: const InputDecoration( + labelText: 'Type *', + border: OutlineInputBorder(), + prefixIcon: Icon(Icons.category), + ), + items: TypeEvenement.values.map((type) { + return DropdownMenuItem( + value: type, + child: Text(_getTypeLabel(type), overflow: TextOverflow.ellipsis, style: TextStyle(color: scheme.onSurface)), + ); + }).toList(), + onChanged: (value) { + setState(() { + _selectedType = value!; + }); + }, ), - items: TypeEvenement.values.map((type) { - return DropdownMenuItem( - value: type, - child: Text(_getTypeLabel(type)), - ); - }).toList(), - onChanged: (value) { - setState(() { - _selectedType = value!; - }); - }, ), - ), - const SizedBox(width: 12), - Expanded( - child: DropdownButtonFormField( - value: _selectedStatut, - decoration: const InputDecoration( - labelText: 'Statut *', - border: OutlineInputBorder(), - prefixIcon: Icon(Icons.flag), + const SizedBox(width: 12), + Expanded( + child: DropdownButtonFormField( + value: _selectedStatut, + isExpanded: true, + dropdownColor: scheme.surface, + style: Theme.of(context).textTheme.bodyLarge?.copyWith(color: scheme.onSurface), + decoration: const InputDecoration( + labelText: 'Statut *', + border: OutlineInputBorder(), + prefixIcon: Icon(Icons.flag), + ), + items: StatutEvenement.values.map((statut) { + return DropdownMenuItem( + value: statut, + child: Text(_getStatutLabel(statut), overflow: TextOverflow.ellipsis, style: TextStyle(color: scheme.onSurface)), + ); + }).toList(), + onChanged: (value) { + setState(() { + _selectedStatut = value!; + }); + }, ), - items: StatutEvenement.values.map((statut) { - return DropdownMenuItem( - value: statut, - child: Text(_getStatutLabel(statut)), - ); - }).toList(), - onChanged: (value) { - setState(() { - _selectedStatut = value!; - }); - }, ), - ), - ], - ), + ], + ); + }), const SizedBox(height: 16), // Dates @@ -355,8 +367,8 @@ class _EditEventDialogState extends State { Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: Colors.grey[100], - border: Border(top: BorderSide(color: Colors.grey[300]!)), + color: Theme.of(context).colorScheme.surfaceContainerHighest, + border: Border(top: BorderSide(color: Theme.of(context).colorScheme.outline)), ), child: Row( mainAxisAlignment: MainAxisAlignment.end, @@ -369,7 +381,7 @@ class _EditEventDialogState extends State { ElevatedButton( onPressed: _submitForm, style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF3B82F6), + backgroundColor: ModuleColors.evenements, foregroundColor: Colors.white, ), child: const Text('Enregistrer'), @@ -390,7 +402,7 @@ class _EditEventDialogState extends State { style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, - color: Color(0xFF3B82F6), + color: ModuleColors.evenements, ), ); } diff --git a/lib/features/events/presentation/widgets/inscription_event_dialog.dart b/lib/features/events/presentation/widgets/inscription_event_dialog.dart index ab69dd3..4a10b73 100644 --- a/lib/features/events/presentation/widgets/inscription_event_dialog.dart +++ b/lib/features/events/presentation/widgets/inscription_event_dialog.dart @@ -2,6 +2,9 @@ library inscription_event_dialog; import 'package:flutter/material.dart'; +import '../../../../shared/design_system/tokens/module_colors.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 '../../bloc/evenements_bloc.dart'; import '../../bloc/evenements_event.dart'; @@ -46,7 +49,7 @@ class _InscriptionEventDialogState extends State { if (state is EvenementsError) { setState(() => _actionSent = false); ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(state.message), backgroundColor: Colors.red), + SnackBar(content: Text(state.message), backgroundColor: ColorTokens.error), ); return; } @@ -61,7 +64,7 @@ class _InscriptionEventDialogState extends State { : 'Désinscription réussie', ), backgroundColor: - state is EvenementInscrit ? Colors.green : Colors.orange, + state is EvenementInscrit ? ColorTokens.success : ColorTokens.warning, ), ); } @@ -108,7 +111,7 @@ class _InscriptionEventDialogState extends State { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: widget.isInscrit ? Colors.red : const Color(0xFF3B82F6), + color: widget.isInscrit ? ColorTokens.error : ModuleColors.evenements, borderRadius: const BorderRadius.only( topLeft: Radius.circular(4), topRight: Radius.circular(4), @@ -144,8 +147,8 @@ class _InscriptionEventDialogState extends State { return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( - color: Colors.blue[50], - border: Border.all(color: Colors.blue[200]!), + color: ColorTokens.infoContainer, + border: Border.all(color: ColorTokens.infoLight), borderRadius: BorderRadius.circular(4), ), child: Column( @@ -153,7 +156,7 @@ class _InscriptionEventDialogState extends State { children: [ Row( children: [ - const Icon(Icons.event, color: Color(0xFF3B82F6)), + const Icon(Icons.event, color: ModuleColors.evenements), const SizedBox(width: 8), Expanded( child: Text( @@ -169,7 +172,7 @@ class _InscriptionEventDialogState extends State { const SizedBox(height: 8), Row( children: [ - const Icon(Icons.calendar_today, size: 16, color: Colors.grey), + const Icon(Icons.calendar_today, size: 16, color: AppColors.textTertiary), const SizedBox(width: 8), Text( _formatDate(widget.evenement.dateDebut), @@ -181,7 +184,7 @@ class _InscriptionEventDialogState extends State { const SizedBox(height: 4), Row( children: [ - const Icon(Icons.location_on, size: 16, color: Colors.grey), + const Icon(Icons.location_on, size: 16, color: AppColors.textTertiary), const SizedBox(width: 8), Expanded( child: Text( @@ -205,9 +208,9 @@ class _InscriptionEventDialogState extends State { return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( - color: isComplet ? Colors.red[50] : Colors.green[50], + color: isComplet ? ColorTokens.errorContainer : ColorTokens.successContainer, border: Border.all( - color: isComplet ? Colors.red[200]! : Colors.green[200]!, + color: isComplet ? ColorTokens.errorLight : ColorTokens.successLight, ), borderRadius: BorderRadius.circular(4), ), @@ -215,7 +218,7 @@ class _InscriptionEventDialogState extends State { children: [ Icon( isComplet ? Icons.warning : Icons.check_circle, - color: isComplet ? Colors.red : Colors.green, + color: isComplet ? ColorTokens.error : ColorTokens.success, ), const SizedBox(width: 12), Expanded( @@ -226,7 +229,7 @@ class _InscriptionEventDialogState extends State { isComplet ? 'Événement complet' : 'Places disponibles', style: TextStyle( fontWeight: FontWeight.bold, - color: isComplet ? Colors.red[900] : Colors.green[900], + color: isComplet ? ColorTokens.errorDark : ColorTokens.successDark, ), ), if (widget.evenement.maxParticipants != null) @@ -264,13 +267,13 @@ class _InscriptionEventDialogState extends State { return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( - color: Colors.orange[50], - border: Border.all(color: Colors.orange[200]!), + color: ColorTokens.warningContainer, + border: Border.all(color: ColorTokens.warningLight), borderRadius: BorderRadius.circular(4), ), child: const Row( children: [ - Icon(Icons.warning, color: Colors.orange), + Icon(Icons.warning, color: ColorTokens.warning), SizedBox(width: 12), Expanded( child: Text( @@ -291,8 +294,8 @@ class _InscriptionEventDialogState extends State { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: Colors.grey[100], - border: Border(top: BorderSide(color: Colors.grey[300]!)), + color: Theme.of(context).colorScheme.surfaceContainerHighest, + border: Border(top: BorderSide(color: Theme.of(context).colorScheme.outline)), ), child: Row( mainAxisAlignment: MainAxisAlignment.end, @@ -305,7 +308,7 @@ class _InscriptionEventDialogState extends State { ElevatedButton( onPressed: (widget.isInscrit || !isComplet) ? _submitForm : null, style: ElevatedButton.styleFrom( - backgroundColor: widget.isInscrit ? Colors.red : const Color(0xFF3B82F6), + backgroundColor: widget.isInscrit ? ColorTokens.error : ModuleColors.evenements, foregroundColor: Colors.white, ), child: Text(widget.isInscrit ? 'Se désinscrire' : 'S\'inscrire'),