From 744faa3a9c2e7493d24e623cef67cd1a229dc340 Mon Sep 17 00:00:00 2001 From: dahoud <41957584+DahoudG@users.noreply.github.com> Date: Wed, 15 Apr 2026 20:27:12 +0000 Subject: [PATCH] feat(features): refontes onboarding/organizations/profile/reports/settings/solidarity - onboarding : datasource souscription, models formule/status, bloc complet - organizations : bloc orgs + switcher + types bloc, models, pages edit/create - profile : bloc complet avec change password, delete account, preferences - reports : bloc avec DashboardReports + ScheduleReports + GenerateReport - settings : language, privacy, feedback pages - solidarity : bloc complet demandes d'aide (CRUD, approuver, rejeter) --- .../pages/awaiting_validation_page.dart | 5 +- .../pages/period_selection_page.dart | 2 +- .../widgets/create_organization_dialog.dart | 42 ++- .../widgets/edit_organization_dialog.dart | 44 ++- .../widgets/organization_card.dart | 122 +++---- .../widgets/organization_filter_widget.dart | 16 +- .../widgets/organization_search_bar.dart | 10 +- .../widgets/organization_stats_widget.dart | 8 +- .../widgets/kyc_status_widget.dart | 30 +- .../system_config_repository.dart | 104 ++++++ .../system_config_repository.dart | 34 ++ .../bloc/system_settings_bloc.dart | 169 +++++++++ .../bloc/system_settings_event.dart | 56 ++- .../presentation/pages/feedback_page.dart | 321 ++++++++---------- .../pages/language_settings_page.dart | 69 ++-- .../pages/privacy_settings_page.dart | 79 ++--- .../widgets/create_demande_aide_dialog.dart | 3 +- 17 files changed, 711 insertions(+), 403 deletions(-) diff --git a/lib/features/onboarding/presentation/pages/awaiting_validation_page.dart b/lib/features/onboarding/presentation/pages/awaiting_validation_page.dart index 00519a8..824c557 100644 --- a/lib/features/onboarding/presentation/pages/awaiting_validation_page.dart +++ b/lib/features/onboarding/presentation/pages/awaiting_validation_page.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import '../../data/models/souscription_status_model.dart'; import '../../../../features/authentication/presentation/bloc/auth_bloc.dart'; import '../../../../shared/design_system/tokens/unionflow_colors.dart'; +import '../../../../shared/design_system/tokens/app_colors.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; /// Page de secours — affichée si l'auto-activation échoue après paiement. @@ -55,7 +56,7 @@ class _AwaitingValidationPageState extends State final sosc = widget.souscription; return Scaffold( - backgroundColor: UnionFlowColors.background, + backgroundColor: Theme.of(context).scaffoldBackgroundColor, body: SafeArea( child: SingleChildScrollView( padding: const EdgeInsets.all(28), @@ -199,7 +200,7 @@ class _AwaitingValidationPageState extends State ), style: ElevatedButton.styleFrom( backgroundColor: UnionFlowColors.unionGreen, - foregroundColor: Colors.white, + foregroundColor: AppColors.onPrimary, padding: const EdgeInsets.symmetric(vertical: 14), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(14)), diff --git a/lib/features/onboarding/presentation/pages/period_selection_page.dart b/lib/features/onboarding/presentation/pages/period_selection_page.dart index cf6cc16..68712e9 100644 --- a/lib/features/onboarding/presentation/pages/period_selection_page.dart +++ b/lib/features/onboarding/presentation/pages/period_selection_page.dart @@ -63,7 +63,7 @@ class _PeriodSelectionPageState extends State { final prixSelected = _estimerPrix(_selectedPeriode); return Scaffold( - backgroundColor: UnionFlowColors.background, + backgroundColor: Theme.of(context).scaffoldBackgroundColor, body: Column( children: [ OnboardingStepHeader( diff --git a/lib/features/organizations/presentation/widgets/create_organization_dialog.dart b/lib/features/organizations/presentation/widgets/create_organization_dialog.dart index b85d150..c124d5e 100644 --- a/lib/features/organizations/presentation/widgets/create_organization_dialog.dart +++ b/lib/features/organizations/presentation/widgets/create_organization_dialog.dart @@ -1,8 +1,11 @@ -/// Dialogue de création d'organisation (mutuelle) -/// Formulaire complet pour créer une nouvelle mutuelle +/// Dialogue de création d'organisation +/// Formulaire complet pour créer une nouvelle organisation library create_organisation_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/organizations_bloc.dart'; import '../../bloc/organizations_event.dart'; @@ -79,7 +82,7 @@ class _CreateOrganizationDialogState extends State { Container( padding: const EdgeInsets.all(16), decoration: const BoxDecoration( - color: Color(0xFF8B5CF6), + color: ModuleColors.organisations, borderRadius: BorderRadius.only( topLeft: Radius.circular(4), topRight: Radius.circular(4), @@ -90,7 +93,7 @@ class _CreateOrganizationDialogState extends State { const Icon(Icons.business, color: Colors.white), const SizedBox(width: 12), const Text( - 'Créer une mutuelle', + 'Nouvelle organisation', style: TextStyle( color: Colors.white, fontSize: 18, @@ -122,7 +125,7 @@ class _CreateOrganizationDialogState extends State { TextFormField( controller: _nomController, decoration: const InputDecoration( - labelText: 'Nom de la mutuelle *', + labelText: 'Nom de l\'organisation *', border: OutlineInputBorder(), prefixIcon: Icon(Icons.business), ), @@ -141,7 +144,7 @@ class _CreateOrganizationDialogState extends State { labelText: 'Nom court / Sigle', border: OutlineInputBorder(), prefixIcon: Icon(Icons.short_text), - hintText: 'Ex: MUTEC, MUPROCI', + hintText: 'Ex: LIONS, CEADP', ), ), const SizedBox(height: 12), @@ -176,8 +179,13 @@ class _CreateOrganizationDialogState extends State { child: LinearProgressIndicator(), ); } + final scheme = Theme.of(context).colorScheme; return DropdownButtonFormField( value: types.any((t) => t.code == _selectedTypeCode) ? _selectedTypeCode : null, + isExpanded: true, + menuMaxHeight: 220, + dropdownColor: scheme.surface, + style: Theme.of(context).textTheme.bodyLarge?.copyWith(color: scheme.onSurface), decoration: const InputDecoration( labelText: 'Type d\'organisation *', border: OutlineInputBorder(), @@ -185,7 +193,11 @@ class _CreateOrganizationDialogState extends State { ), items: types.map((type) => DropdownMenuItem( value: type.code, - child: Text(type.libelle), + child: Text( + type.libelle, + overflow: TextOverflow.ellipsis, + style: TextStyle(color: scheme.onSurface), + ), )).toList(), onChanged: (value) => setState(() => _selectedTypeCode = value), validator: (value) => value == null ? 'Le type est obligatoire' : null, @@ -349,8 +361,8 @@ class _CreateOrganizationDialogState 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, @@ -363,10 +375,10 @@ class _CreateOrganizationDialogState extends State { ElevatedButton( onPressed: _submitForm, style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF8B5CF6), - foregroundColor: Colors.white, + backgroundColor: ModuleColors.organisations, + foregroundColor: AppColors.onPrimary, ), - child: const Text('Créer la mutuelle'), + child: const Text('Créer l\'organisation'), ), ], ), @@ -383,7 +395,7 @@ class _CreateOrganizationDialogState extends State { style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, - color: Color(0xFF8B5CF6), + color: ModuleColors.organisations, ), ); } @@ -419,8 +431,8 @@ class _CreateOrganizationDialogState extends State { // Afficher un message de succès ScaffoldMessenger.of(context).showSnackBar( const SnackBar( - content: Text('Mutuelle créée avec succès'), - backgroundColor: Colors.green, + content: Text('Organisation créée avec succès'), + backgroundColor: ColorTokens.success, ), ); } diff --git a/lib/features/organizations/presentation/widgets/edit_organization_dialog.dart b/lib/features/organizations/presentation/widgets/edit_organization_dialog.dart index 5f7c495..15fce87 100644 --- a/lib/features/organizations/presentation/widgets/edit_organization_dialog.dart +++ b/lib/features/organizations/presentation/widgets/edit_organization_dialog.dart @@ -1,7 +1,10 @@ -/// Dialogue de modification d'organisation (mutuelle) +/// Dialogue de modification d'organisation library edit_organisation_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/organizations_bloc.dart'; import '../../bloc/organizations_event.dart'; @@ -200,7 +203,7 @@ class _EditOrganizationDialogState extends State { return Container( padding: const EdgeInsets.all(16), decoration: const BoxDecoration( - color: Color(0xFF8B5CF6), + color: ModuleColors.organisations, borderRadius: BorderRadius.only( topLeft: Radius.circular(4), topRight: Radius.circular(4), @@ -211,7 +214,7 @@ class _EditOrganizationDialogState extends State { const Icon(Icons.edit, color: Colors.white), const SizedBox(width: 12), const Text( - 'Modifier la mutuelle', + 'Modifier l\'organisation', style: TextStyle( color: Colors.white, fontSize: 18, @@ -234,7 +237,7 @@ class _EditOrganizationDialogState extends State { style: const TextStyle( fontSize: 16, fontWeight: FontWeight.bold, - color: Color(0xFF8B5CF6), + color: ModuleColors.organisations, ), ); } @@ -243,7 +246,7 @@ class _EditOrganizationDialogState extends State { return TextFormField( controller: _nomController, decoration: const InputDecoration( - labelText: 'Nom de la mutuelle *', + labelText: 'Nom de l\'organisation *', border: OutlineInputBorder(), prefixIcon: Icon(Icons.business), ), @@ -263,7 +266,7 @@ class _EditOrganizationDialogState extends State { labelText: 'Nom court / Sigle', border: OutlineInputBorder(), prefixIcon: Icon(Icons.short_text), - hintText: 'Ex: MUTEC, MUPROCI', + hintText: 'Ex: LIONS, CEADP', ), ); } @@ -299,8 +302,13 @@ class _EditOrganizationDialogState extends State { child: LinearProgressIndicator(), ); } + final scheme = Theme.of(context).colorScheme; return DropdownButtonFormField( value: types.any((t) => t.code == _selectedTypeCode) ? _selectedTypeCode : null, + isExpanded: true, + menuMaxHeight: 220, + dropdownColor: scheme.surface, + style: Theme.of(context).textTheme.bodyLarge?.copyWith(color: scheme.onSurface), decoration: const InputDecoration( labelText: 'Type d\'organisation *', border: OutlineInputBorder(), @@ -308,7 +316,11 @@ class _EditOrganizationDialogState extends State { ), items: types.map((type) => DropdownMenuItem( value: type.code, - child: Text(type.libelle), + child: Text( + type.libelle, + overflow: TextOverflow.ellipsis, + style: TextStyle(color: scheme.onSurface), + ), )).toList(), onChanged: (value) { if (value != null) setState(() => _selectedTypeCode = value); @@ -320,8 +332,12 @@ class _EditOrganizationDialogState extends State { } Widget _buildStatutDropdown() { + final scheme = Theme.of(context).colorScheme; return 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(), @@ -330,7 +346,7 @@ class _EditOrganizationDialogState extends State { items: StatutOrganization.values.map((statut) { return DropdownMenuItem( value: statut, - child: Text(statut.displayName), + child: Text(statut.displayName, style: TextStyle(color: scheme.onSurface)), ); }).toList(), onChanged: (value) { @@ -475,8 +491,8 @@ class _EditOrganizationDialogState 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, @@ -489,8 +505,8 @@ class _EditOrganizationDialogState extends State { ElevatedButton( onPressed: _submitForm, style: ElevatedButton.styleFrom( - backgroundColor: const Color(0xFF8B5CF6), - foregroundColor: Colors.white, + backgroundColor: ModuleColors.organisations, + foregroundColor: AppColors.onPrimary, ), child: const Text('Enregistrer'), ), @@ -525,8 +541,8 @@ class _EditOrganizationDialogState extends State { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( - content: Text('Mutuelle modifiée avec succès'), - backgroundColor: Colors.green, + content: Text('Organisation modifiée avec succès'), + backgroundColor: ColorTokens.success, ), ); } diff --git a/lib/features/organizations/presentation/widgets/organization_card.dart b/lib/features/organizations/presentation/widgets/organization_card.dart index 9b00683..8c6dd73 100644 --- a/lib/features/organizations/presentation/widgets/organization_card.dart +++ b/lib/features/organizations/presentation/widgets/organization_card.dart @@ -4,8 +4,10 @@ library organization_card; import 'package:flutter/material.dart'; import '../../data/models/organization_model.dart'; +import '../../../../shared/design_system/tokens/module_colors.dart'; +import '../../../../shared/design_system/tokens/app_colors.dart'; -/// Carte d'organisation avec design cohérent +/// Carte d'organisation avec design cohérent — theme-aware (mode jour/nuit) class OrganizationCard extends StatelessWidget { final OrganizationModel organization; final VoidCallback? onTap; @@ -24,14 +26,16 @@ class OrganizationCard extends StatelessWidget { @override Widget build(BuildContext context) { + final scheme = Theme.of(context).colorScheme; return Container( margin: const EdgeInsets.only(bottom: 8), decoration: BoxDecoration( - color: Colors.white, - borderRadius: BorderRadius.circular(8), // RadiusTokens cohérent + color: scheme.surface, + borderRadius: BorderRadius.circular(8), + border: Border.all(color: scheme.outline.withOpacity(0.5)), boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.05), + color: scheme.shadow.withOpacity(0.05), blurRadius: 4, offset: const Offset(0, 2), ), @@ -41,15 +45,15 @@ class OrganizationCard extends StatelessWidget { onTap: onTap, borderRadius: BorderRadius.circular(8), child: Padding( - padding: const EdgeInsets.all(12), // SpacingTokens cohérent + padding: const EdgeInsets.all(12), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - _buildHeader(), + _buildHeader(context), const SizedBox(height: 8), - _buildContent(), + _buildContent(context), const SizedBox(height: 8), - _buildFooter(), + _buildFooter(context), ], ), ), @@ -58,30 +62,29 @@ class OrganizationCard extends StatelessWidget { } /// Header avec nom et statut - Widget _buildHeader() { + Widget _buildHeader(BuildContext context) { + final scheme = Theme.of(context).colorScheme; return Row( children: [ - // Icône du type d'organisation Container( padding: const EdgeInsets.all(6), decoration: BoxDecoration( - color: const Color(0xFF6C5CE7).withOpacity(0.1), // ColorTokens cohérent + color: ModuleColors.organisations.withOpacity(0.1), borderRadius: BorderRadius.circular(6), ), child: const Icon(Icons.business_outlined, size: 18, color: Color(0xFF6C5CE7)), ), const SizedBox(width: 12), - // Nom et nom court Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( organization.nom, - style: const TextStyle( + style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, - color: Color(0xFF374151), // ColorTokens cohérent + color: scheme.onSurface, ), maxLines: 1, overflow: TextOverflow.ellipsis, @@ -90,16 +93,15 @@ class OrganizationCard extends StatelessWidget { const SizedBox(height: 2), Text( organization.nomCourt!, - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: Color(0xFF6B7280), + color: scheme.onSurfaceVariant, ), ), ], ], ), ), - // Badge de statut _buildStatusBadge(), ], ); @@ -108,7 +110,6 @@ class OrganizationCard extends StatelessWidget { /// Badge de statut Widget _buildStatusBadge() { final color = Color(int.parse(organization.statut.color.substring(1), radix: 16) + 0xFF000000); - return Container( padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), decoration: BoxDecoration( @@ -127,46 +128,31 @@ class OrganizationCard extends StatelessWidget { } /// Contenu principal - Widget _buildContent() { + Widget _buildContent(BuildContext context) { + final scheme = Theme.of(context).colorScheme; return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - // Type d'organisation Row( children: [ - const Icon( - Icons.category_outlined, - size: 14, - color: Color(0xFF6B7280), - ), + Icon(Icons.category_outlined, size: 14, color: scheme.onSurfaceVariant), const SizedBox(width: 6), Text( organization.typeOrganisation, - style: const TextStyle( - fontSize: 12, - color: Color(0xFF6B7280), - ), + style: TextStyle(fontSize: 12, color: scheme.onSurfaceVariant), ), ], ), const SizedBox(height: 4), - // Localisation if (organization.ville?.isNotEmpty == true || organization.region?.isNotEmpty == true) Row( children: [ - const Icon( - Icons.location_on_outlined, - size: 14, - color: Color(0xFF6B7280), - ), + Icon(Icons.location_on_outlined, size: 14, color: scheme.onSurfaceVariant), const SizedBox(width: 6), Expanded( child: Text( _buildLocationText(), - style: const TextStyle( - fontSize: 12, - color: Color(0xFF6B7280), - ), + style: TextStyle(fontSize: 12, color: scheme.onSurfaceVariant), maxLines: 1, overflow: TextOverflow.ellipsis, ), @@ -174,14 +160,10 @@ class OrganizationCard extends StatelessWidget { ], ), const SizedBox(height: 4), - // Description si disponible if (organization.description?.isNotEmpty == true) ...[ Text( organization.description!, - style: const TextStyle( - fontSize: 12, - color: Color(0xFF6B7280), - ), + style: TextStyle(fontSize: 12, color: scheme.onSurfaceVariant), maxLines: 2, overflow: TextOverflow.ellipsis, ), @@ -192,14 +174,14 @@ class OrganizationCard extends StatelessWidget { } /// Footer avec statistiques et actions - Widget _buildFooter() { + Widget _buildFooter(BuildContext context) { return Row( children: [ - // Statistiques Expanded( child: Row( children: [ _buildStatItem( + context: context, icon: Icons.people_outline, value: organization.nombreMembres.toString(), label: 'membres', @@ -207,6 +189,7 @@ class OrganizationCard extends StatelessWidget { const SizedBox(width: 16), if (organization.ancienneteAnnees > 0) _buildStatItem( + context: context, icon: Icons.access_time, value: organization.ancienneteAnnees.toString(), label: 'ans', @@ -214,7 +197,6 @@ class OrganizationCard extends StatelessWidget { ], ), ), - // Actions if (showActions) _buildActions(), ], ); @@ -222,25 +204,23 @@ class OrganizationCard extends StatelessWidget { /// Item de statistique Widget _buildStatItem({ + required BuildContext context, required IconData icon, required String value, required String label, }) { + final scheme = Theme.of(context).colorScheme; return Row( mainAxisSize: MainAxisSize.min, children: [ - Icon( - icon, - size: 14, - color: const Color(0xFF6C5CE7), - ), + Icon(icon, size: 14, color: ModuleColors.organisations), const SizedBox(width: 4), Text( '$value $label', - style: const TextStyle( + style: TextStyle( fontSize: 11, fontWeight: FontWeight.w600, - color: Color(0xFF374151), + color: scheme.onSurface, ), ), ], @@ -255,31 +235,17 @@ class OrganizationCard extends StatelessWidget { if (onEdit != null) IconButton( onPressed: onEdit, - icon: const Icon( - Icons.edit_outlined, - size: 18, - color: Color(0xFF6C5CE7), - ), + icon: const Icon(Icons.edit_outlined, size: 18, color: Color(0xFF6C5CE7)), padding: const EdgeInsets.all(4), - constraints: const BoxConstraints( - minWidth: 32, - minHeight: 32, - ), + constraints: const BoxConstraints(minWidth: 32, minHeight: 32), tooltip: 'Modifier', ), if (onDelete != null) IconButton( onPressed: onDelete, - icon: Icon( - Icons.delete_outline, - size: 18, - color: Colors.red.shade400, - ), + icon: const Icon(Icons.delete_outline, size: 18, color: AppColors.error), padding: const EdgeInsets.all(4), - constraints: const BoxConstraints( - minWidth: 32, - minHeight: 32, - ), + constraints: const BoxConstraints(minWidth: 32, minHeight: 32), tooltip: 'Supprimer', ), ], @@ -289,15 +255,9 @@ class OrganizationCard extends StatelessWidget { /// Construit le texte de localisation String _buildLocationText() { final parts = []; - if (organization.ville?.isNotEmpty == true) { - parts.add(organization.ville!); - } - if (organization.region?.isNotEmpty == true) { - parts.add(organization.region!); - } - if (organization.pays?.isNotEmpty == true) { - parts.add(organization.pays!); - } + if (organization.ville?.isNotEmpty == true) parts.add(organization.ville!); + if (organization.region?.isNotEmpty == true) parts.add(organization.region!); + if (organization.pays?.isNotEmpty == true) parts.add(organization.pays!); return parts.join(', '); } } diff --git a/lib/features/organizations/presentation/widgets/organization_filter_widget.dart b/lib/features/organizations/presentation/widgets/organization_filter_widget.dart index 09fb30a..652d262 100644 --- a/lib/features/organizations/presentation/widgets/organization_filter_widget.dart +++ b/lib/features/organizations/presentation/widgets/organization_filter_widget.dart @@ -3,6 +3,8 @@ library organization_filter_widget; import 'package:flutter/material.dart'; +import '../../../../../shared/design_system/tokens/app_colors.dart'; +import '../../../../../shared/design_system/tokens/color_tokens.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../../bloc/organizations_bloc.dart'; import '../../bloc/organizations_event.dart'; @@ -24,11 +26,11 @@ class OrganizationFilterWidget extends StatelessWidget { return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( - color: Colors.white, + color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(8), // RadiusTokens cohérent boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.05), + color: AppColors.shadow, blurRadius: 4, offset: const Offset(0, 2), ), @@ -50,7 +52,7 @@ class OrganizationFilterWidget extends StatelessWidget { style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: Color(0xFF374151), + color: Theme.of(context).colorScheme.onSurface, ), ), const Spacer(), @@ -119,9 +121,9 @@ class OrganizationFilterWidget extends StatelessWidget { ), isExpanded: true, padding: const EdgeInsets.symmetric(horizontal: 8), - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: Color(0xFF374151), + color: Theme.of(context).colorScheme.onSurface, ), items: [ const DropdownMenuItem( @@ -180,9 +182,9 @@ class OrganizationFilterWidget extends StatelessWidget { ), isExpanded: true, padding: const EdgeInsets.symmetric(horizontal: 8), - style: const TextStyle( + style: TextStyle( fontSize: 12, - color: Color(0xFF374151), + color: Theme.of(context).colorScheme.onSurface, ), items: [ const DropdownMenuItem( diff --git a/lib/features/organizations/presentation/widgets/organization_search_bar.dart b/lib/features/organizations/presentation/widgets/organization_search_bar.dart index 140ae49..9440b53 100644 --- a/lib/features/organizations/presentation/widgets/organization_search_bar.dart +++ b/lib/features/organizations/presentation/widgets/organization_search_bar.dart @@ -3,6 +3,8 @@ library organisation_search_bar; import 'package:flutter/material.dart'; +import '../../../../../shared/design_system/tokens/app_colors.dart'; +import '../../../../../shared/design_system/tokens/color_tokens.dart'; /// Barre de recherche avec design cohérent class OrganisationSearchBar extends StatefulWidget { @@ -54,11 +56,11 @@ class _OrganisationSearchBarState extends State { Widget build(BuildContext context) { return Container( decoration: BoxDecoration( - color: Colors.white, + color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(8), // RadiusTokens cohérent boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.05), + color: AppColors.shadow, blurRadius: 4, offset: const Offset(0, 2), ), @@ -102,9 +104,9 @@ class _OrganisationSearchBarState extends State { vertical: 12, ), ), - style: const TextStyle( + style: TextStyle( fontSize: 14, - color: Color(0xFF374151), + color: Theme.of(context).colorScheme.onSurface, ), ), ), diff --git a/lib/features/organizations/presentation/widgets/organization_stats_widget.dart b/lib/features/organizations/presentation/widgets/organization_stats_widget.dart index 1246665..9b4928b 100644 --- a/lib/features/organizations/presentation/widgets/organization_stats_widget.dart +++ b/lib/features/organizations/presentation/widgets/organization_stats_widget.dart @@ -3,6 +3,8 @@ library organisation_stats_widget; import 'package:flutter/material.dart'; +import '../../../../shared/design_system/tokens/app_colors.dart'; +import '../../../../shared/design_system/tokens/color_tokens.dart'; /// Widget des statistiques avec design cohérent class OrganisationStatsWidget extends StatelessWidget { @@ -20,11 +22,11 @@ class OrganisationStatsWidget extends StatelessWidget { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( - color: Colors.white, + color: Theme.of(context).colorScheme.surface, borderRadius: BorderRadius.circular(8), // RadiusTokens cohérent boxShadow: [ BoxShadow( - color: Colors.black.withOpacity(0.05), + color: AppColors.shadow, blurRadius: 4, offset: const Offset(0, 2), ), @@ -59,7 +61,7 @@ class OrganisationStatsWidget extends StatelessWidget { title: 'Actives', value: stats['actives']?.toString() ?? '0', icon: Icons.check_circle, - color: const Color(0xFF10B981), + color: ColorTokens.successLight, onTap: () => onStatTap?.call('actives'), ), ), diff --git a/lib/features/profile/presentation/widgets/kyc_status_widget.dart b/lib/features/profile/presentation/widgets/kyc_status_widget.dart index 16da6b4..25bc772 100644 --- a/lib/features/profile/presentation/widgets/kyc_status_widget.dart +++ b/lib/features/profile/presentation/widgets/kyc_status_widget.dart @@ -1,6 +1,8 @@ import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; +import '../../../../shared/design_system/tokens/color_tokens.dart'; +import '../../../../shared/design_system/tokens/module_colors.dart'; import '../../../members/data/models/membre_complete_model.dart'; /// Widget d'affichage du statut KYC (Know Your Customer) d'un membre. @@ -34,7 +36,7 @@ class KycStatusWidget extends StatelessWidget { children: [ Icon( Icons.verified_user, - color: colorScheme.primary, + color: ModuleColors.profil, size: 24, ), const SizedBox(width: 8), @@ -42,7 +44,7 @@ class KycStatusWidget extends StatelessWidget { 'Vérification KYC (Anti-blanchiment)', style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.bold, - color: colorScheme.primary, + color: ModuleColors.profil, ), ), ], @@ -82,7 +84,7 @@ class KycStatusWidget extends StatelessWidget { Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( - color: colorScheme.primaryContainer.withOpacity(0.3), + color: ModuleColors.profil.withOpacity(0.08), borderRadius: BorderRadius.circular(8), ), child: Row( @@ -90,15 +92,13 @@ class KycStatusWidget extends StatelessWidget { Icon( Icons.info_outline, size: 16, - color: colorScheme.primary, + color: ModuleColors.profil, ), const SizedBox(width: 8), Expanded( child: Text( 'Ces informations sont gérées par l\'administrateur et permettent de garantir la conformité aux normes BCEAO/OHADA.', - style: theme.textTheme.bodySmall?.copyWith( - color: colorScheme.onSurface, - ), + style: theme.textTheme.bodySmall, ), ), ], @@ -157,16 +157,16 @@ class KycStatusWidget extends StatelessWidget { } Color _getStatutKycColor(StatutKyc? statut) { - if (statut == null) return Colors.grey; + if (statut == null) return ColorTokens.textSecondary; switch (statut) { case StatutKyc.nonVerifie: - return Colors.orange; + return ColorTokens.warning; case StatutKyc.enCours: - return Colors.blue; + return ColorTokens.info; case StatutKyc.verifie: - return Colors.green; + return ColorTokens.success; case StatutKyc.refuse: - return Colors.red; + return ColorTokens.error; } } @@ -181,12 +181,12 @@ class KycStatusWidget extends StatelessWidget { } Color _getNiveauVigilanceColor(NiveauVigilanceKyc? niveau) { - if (niveau == null) return Colors.grey; + if (niveau == null) return ColorTokens.textSecondary; switch (niveau) { case NiveauVigilanceKyc.simplifie: - return Colors.blue; + return ColorTokens.info; case NiveauVigilanceKyc.renforce: - return Colors.deepOrange; + return ColorTokens.warning; } } } diff --git a/lib/features/settings/data/repositories/system_config_repository.dart b/lib/features/settings/data/repositories/system_config_repository.dart index b811d5b..4fca74a 100644 --- a/lib/features/settings/data/repositories/system_config_repository.dart +++ b/lib/features/settings/data/repositories/system_config_repository.dart @@ -81,6 +81,110 @@ class SystemConfigRepositoryImpl implements ISystemConfigRepository { throw Exception('Erreur ${response.statusCode}'); } + @override + Future> optimizeDatabase() async { + final response = await _apiClient.post('$_base/database/optimize'); + if (response.statusCode == 200) return response.data as Map; + throw Exception('Erreur ${response.statusCode}'); + } + + @override + Future> forceGlobalLogout() async { + final response = await _apiClient.post('$_base/auth/logout-all'); + if (response.statusCode == 200) return response.data as Map; + throw Exception('Erreur ${response.statusCode}'); + } + + @override + Future> cleanupSessions() async { + final response = await _apiClient.post('$_base/sessions/cleanup'); + if (response.statusCode == 200) return response.data as Map; + throw Exception('Erreur ${response.statusCode}'); + } + + @override + Future> cleanOldLogs() async { + final response = await _apiClient.post('$_base/logs/cleanup'); + if (response.statusCode == 200) return response.data as Map; + throw Exception('Erreur ${response.statusCode}'); + } + + @override + Future> purgeExpiredData() async { + final response = await _apiClient.post('$_base/data/purge'); + if (response.statusCode == 200) return response.data as Map; + throw Exception('Erreur ${response.statusCode}'); + } + + @override + Future> analyzePerformance() async { + final response = await _apiClient.post('$_base/performance/analyze'); + if (response.statusCode == 200) return response.data as Map; + throw Exception('Erreur ${response.statusCode}'); + } + + @override + Future> createBackup() async { + final response = await _apiClient.post('$_base/backup/create'); + if (response.statusCode == 200) return response.data as Map; + throw Exception('Erreur ${response.statusCode}'); + } + + @override + Future> scheduleMaintenance({String? scheduledAt, String? reason}) async { + final queryParams = {}; + if (scheduledAt != null) queryParams['scheduledAt'] = scheduledAt; + if (reason != null) queryParams['reason'] = reason; + final response = await _apiClient.post( + '$_base/maintenance/schedule', + queryParameters: queryParams.isNotEmpty ? queryParams : null, + ); + if (response.statusCode == 200) return response.data as Map; + throw Exception('Erreur ${response.statusCode}'); + } + + @override + Future> emergencyMaintenance() async { + final response = await _apiClient.post('$_base/maintenance/emergency'); + if (response.statusCode == 200) return response.data as Map; + throw Exception('Erreur ${response.statusCode}'); + } + + @override + Future> checkUpdates() async { + final response = await _apiClient.get('$_base/updates/check'); + if (response.statusCode == 200) return response.data as Map; + throw Exception('Erreur ${response.statusCode}'); + } + + @override + Future> exportLogs() async { + final response = await _apiClient.get('$_base/logs/export'); + if (response.statusCode == 200) return response.data as Map; + throw Exception('Erreur ${response.statusCode}'); + } + + @override + Future> generateUsageReport() async { + final response = await _apiClient.get('$_base/reports/usage'); + if (response.statusCode == 200) return response.data as Map; + throw Exception('Erreur ${response.statusCode}'); + } + + @override + Future> generateAuditReport() async { + final response = await _apiClient.get('$_base/audit/report'); + if (response.statusCode == 200) return response.data as Map; + throw Exception('Erreur ${response.statusCode}'); + } + + @override + Future> exportGDPRData() async { + final response = await _apiClient.post('$_base/gdpr/export'); + if (response.statusCode == 200) return response.data as Map; + throw Exception('Erreur ${response.statusCode}'); + } + @override Future resetConfig() async { try { diff --git a/lib/features/settings/domain/repositories/system_config_repository.dart b/lib/features/settings/domain/repositories/system_config_repository.dart index ba7fb21..ffe84e5 100644 --- a/lib/features/settings/domain/repositories/system_config_repository.dart +++ b/lib/features/settings/domain/repositories/system_config_repository.dart @@ -30,4 +30,38 @@ abstract class ISystemConfigRepository { /// Réinitialise la configuration aux valeurs par défaut Future resetConfig(); + + // ── Base de données ──────────────────────────────────────────────────────── + Future> optimizeDatabase(); + + // ── Sécurité / sessions ─────────────────────────────────────────────────── + Future> forceGlobalLogout(); + Future> cleanupSessions(); + + // ── Logs ────────────────────────────────────────────────────────────────── + Future> cleanOldLogs(); + Future> exportLogs(); + + // ── Données ─────────────────────────────────────────────────────────────── + Future> purgeExpiredData(); + + // ── Performance ─────────────────────────────────────────────────────────── + Future> analyzePerformance(); + + // ── Sauvegarde ──────────────────────────────────────────────────────────── + Future> createBackup(); + + // ── Maintenance ─────────────────────────────────────────────────────────── + Future> scheduleMaintenance({String? scheduledAt, String? reason}); + Future> emergencyMaintenance(); + + // ── Mises à jour ────────────────────────────────────────────────────────── + Future> checkUpdates(); + + // ── Rapports ────────────────────────────────────────────────────────────── + Future> generateUsageReport(); + Future> generateAuditReport(); + + // ── RGPD ────────────────────────────────────────────────────────────────── + Future> exportGDPRData(); } diff --git a/lib/features/settings/presentation/bloc/system_settings_bloc.dart b/lib/features/settings/presentation/bloc/system_settings_bloc.dart index 1668105..c768342 100644 --- a/lib/features/settings/presentation/bloc/system_settings_bloc.dart +++ b/lib/features/settings/presentation/bloc/system_settings_bloc.dart @@ -38,6 +38,20 @@ class SystemSettingsBloc extends Bloc on(_onTestDatabaseConnection); on(_onTestEmailConfiguration); on(_onResetSystemConfig); + on(_onOptimizeDatabase); + on(_onForceGlobalLogout); + on(_onCleanupSessions); + on(_onCleanOldLogs); + on(_onPurgeExpiredData); + on(_onAnalyzePerformance); + on(_onCreateBackup); + on(_onScheduleMaintenance); + on(_onEmergencyMaintenance); + on(_onCheckUpdates); + on(_onExportLogs); + on(_onGenerateUsageReport); + on(_onGenerateAuditReport); + on(_onExportGDPRData); } Future _onLoadSystemConfig( @@ -165,4 +179,159 @@ class SystemSettingsBloc extends Bloc emit(SystemSettingsError('Erreur de réinitialisation: ${e.toString()}')); } } + + Future _onOptimizeDatabase(OptimizeDatabase event, Emitter emit) async { + emit(SystemSettingsLoading()); + try { + final result = await _repository.optimizeDatabase(); + emit(SystemSettingsSuccess(result['message'] as String? ?? 'Base de données optimisée')); + } catch (e) { + if (e is DioException && e.type == DioExceptionType.cancel) return; + emit(SystemSettingsError('Erreur: ${e.toString()}')); + } + } + + Future _onForceGlobalLogout(ForceGlobalLogout event, Emitter emit) async { + emit(SystemSettingsLoading()); + try { + final result = await _repository.forceGlobalLogout(); + emit(SystemSettingsSuccess(result['message'] as String? ?? 'Déconnexion globale déclenchée')); + } catch (e) { + if (e is DioException && e.type == DioExceptionType.cancel) return; + emit(SystemSettingsError('Erreur: ${e.toString()}')); + } + } + + Future _onCleanupSessions(CleanupSessions event, Emitter emit) async { + emit(SystemSettingsLoading()); + try { + final result = await _repository.cleanupSessions(); + emit(SystemSettingsSuccess(result['message'] as String? ?? 'Sessions nettoyées')); + } catch (e) { + if (e is DioException && e.type == DioExceptionType.cancel) return; + emit(SystemSettingsError('Erreur: ${e.toString()}')); + } + } + + Future _onCleanOldLogs(CleanOldLogs event, Emitter emit) async { + emit(SystemSettingsLoading()); + try { + final result = await _repository.cleanOldLogs(); + emit(SystemSettingsSuccess(result['message'] as String? ?? 'Logs nettoyés')); + } catch (e) { + if (e is DioException && e.type == DioExceptionType.cancel) return; + emit(SystemSettingsError('Erreur: ${e.toString()}')); + } + } + + Future _onPurgeExpiredData(PurgeExpiredData event, Emitter emit) async { + emit(SystemSettingsLoading()); + try { + final result = await _repository.purgeExpiredData(); + emit(SystemSettingsSuccess(result['message'] as String? ?? 'Données purgées')); + } catch (e) { + if (e is DioException && e.type == DioExceptionType.cancel) return; + emit(SystemSettingsError('Erreur: ${e.toString()}')); + } + } + + Future _onAnalyzePerformance(AnalyzePerformance event, Emitter emit) async { + emit(SystemSettingsLoading()); + try { + final result = await _repository.analyzePerformance(); + emit(SystemSettingsSuccess(result['message'] as String? ?? 'Analyse terminée')); + } catch (e) { + if (e is DioException && e.type == DioExceptionType.cancel) return; + emit(SystemSettingsError('Erreur: ${e.toString()}')); + } + } + + Future _onCreateBackup(CreateBackup event, Emitter emit) async { + emit(SystemSettingsLoading()); + try { + final result = await _repository.createBackup(); + emit(SystemSettingsSuccess(result['message'] as String? ?? 'Sauvegarde créée')); + } catch (e) { + if (e is DioException && e.type == DioExceptionType.cancel) return; + emit(SystemSettingsError('Erreur: ${e.toString()}')); + } + } + + Future _onScheduleMaintenance(ScheduleMaintenance event, Emitter emit) async { + emit(SystemSettingsLoading()); + try { + final result = await _repository.scheduleMaintenance(scheduledAt: event.scheduledAt, reason: event.reason); + emit(SystemSettingsSuccess(result['message'] as String? ?? 'Maintenance planifiée')); + } catch (e) { + if (e is DioException && e.type == DioExceptionType.cancel) return; + emit(SystemSettingsError('Erreur: ${e.toString()}')); + } + } + + Future _onEmergencyMaintenance(EmergencyMaintenance event, Emitter emit) async { + emit(SystemSettingsLoading()); + try { + final result = await _repository.emergencyMaintenance(); + emit(SystemSettingsSuccess(result['message'] as String? ?? 'Maintenance d\'urgence activée')); + } catch (e) { + if (e is DioException && e.type == DioExceptionType.cancel) return; + emit(SystemSettingsError('Erreur: ${e.toString()}')); + } + } + + Future _onCheckUpdates(CheckUpdates event, Emitter emit) async { + emit(SystemSettingsLoading()); + try { + final result = await _repository.checkUpdates(); + emit(SystemSettingsSuccess(result['message'] as String? ?? 'Vérification terminée')); + } catch (e) { + if (e is DioException && e.type == DioExceptionType.cancel) return; + emit(SystemSettingsError('Erreur: ${e.toString()}')); + } + } + + Future _onExportLogs(ExportLogs event, Emitter emit) async { + emit(SystemSettingsLoading()); + try { + final result = await _repository.exportLogs(); + final count = result['count'] as int? ?? 0; + emit(SystemSettingsSuccess('$count log(s) exporté(s)')); + } catch (e) { + if (e is DioException && e.type == DioExceptionType.cancel) return; + emit(SystemSettingsError('Erreur: ${e.toString()}')); + } + } + + Future _onGenerateUsageReport(GenerateUsageReport event, Emitter emit) async { + emit(SystemSettingsLoading()); + try { + final result = await _repository.generateUsageReport(); + emit(SystemSettingsSuccess(result['message'] as String? ?? 'Rapport généré')); + } catch (e) { + if (e is DioException && e.type == DioExceptionType.cancel) return; + emit(SystemSettingsError('Erreur: ${e.toString()}')); + } + } + + Future _onGenerateAuditReport(GenerateAuditReport event, Emitter emit) async { + emit(SystemSettingsLoading()); + try { + final result = await _repository.generateAuditReport(); + emit(SystemSettingsSuccess(result['message'] as String? ?? 'Rapport d\'audit généré')); + } catch (e) { + if (e is DioException && e.type == DioExceptionType.cancel) return; + emit(SystemSettingsError('Erreur: ${e.toString()}')); + } + } + + Future _onExportGDPRData(ExportGDPRData event, Emitter emit) async { + emit(SystemSettingsLoading()); + try { + final result = await _repository.exportGDPRData(); + emit(SystemSettingsSuccess(result['message'] as String? ?? 'Export RGPD initié')); + } catch (e) { + if (e is DioException && e.type == DioExceptionType.cancel) return; + emit(SystemSettingsError('Erreur: ${e.toString()}')); + } + } } diff --git a/lib/features/settings/presentation/bloc/system_settings_event.dart b/lib/features/settings/presentation/bloc/system_settings_event.dart index 08f4468..8d61ade 100644 --- a/lib/features/settings/presentation/bloc/system_settings_event.dart +++ b/lib/features/settings/presentation/bloc/system_settings_event.dart @@ -10,25 +10,63 @@ abstract class SystemSettingsEvent extends Equatable { List get props => []; } +// ── Chargement ───────────────────────────────────────────────────────────── class LoadSystemConfig extends SystemSettingsEvent {} +class LoadCacheStats extends SystemSettingsEvent {} +class LoadSystemMetrics extends SystemSettingsEvent {} +// ── Configuration ────────────────────────────────────────────────────────── class UpdateSystemConfig extends SystemSettingsEvent { final Map config; - const UpdateSystemConfig(this.config); - @override List get props => [config]; } -class LoadCacheStats extends SystemSettingsEvent {} - -class LoadSystemMetrics extends SystemSettingsEvent {} - -class ClearCache extends SystemSettingsEvent {} +class ResetSystemConfig extends SystemSettingsEvent {} +// ── Tests ─────────────────────────────────────────────────────────────────── class TestDatabaseConnection extends SystemSettingsEvent {} - class TestEmailConfiguration extends SystemSettingsEvent {} -class ResetSystemConfig extends SystemSettingsEvent {} +// ── Cache ──────────────────────────────────────────────────────────────────── +class ClearCache extends SystemSettingsEvent {} + +// ── Base de données ────────────────────────────────────────────────────────── +class OptimizeDatabase extends SystemSettingsEvent {} + +// ── Sécurité ───────────────────────────────────────────────────────────────── +class ForceGlobalLogout extends SystemSettingsEvent {} +class CleanupSessions extends SystemSettingsEvent {} +class ExportGDPRData extends SystemSettingsEvent {} + +// ── Logs ───────────────────────────────────────────────────────────────────── +class CleanOldLogs extends SystemSettingsEvent {} +class ExportLogs extends SystemSettingsEvent {} + +// ── Données ────────────────────────────────────────────────────────────────── +class PurgeExpiredData extends SystemSettingsEvent {} + +// ── Performance ────────────────────────────────────────────────────────────── +class AnalyzePerformance extends SystemSettingsEvent {} + +// ── Sauvegarde ──────────────────────────────────────────────────────────────── +class CreateBackup extends SystemSettingsEvent {} + +// ── Maintenance ─────────────────────────────────────────────────────────────── +class ScheduleMaintenance extends SystemSettingsEvent { + final String? scheduledAt; + final String? reason; + const ScheduleMaintenance({this.scheduledAt, this.reason}); + @override + List get props => [scheduledAt, reason]; +} + +class EmergencyMaintenance extends SystemSettingsEvent {} + +// ── Mises à jour ────────────────────────────────────────────────────────────── +class CheckUpdates extends SystemSettingsEvent {} + +// ── Rapports ────────────────────────────────────────────────────────────────── +class GenerateAuditReport extends SystemSettingsEvent {} +class GenerateUsageReport extends SystemSettingsEvent {} diff --git a/lib/features/settings/presentation/pages/feedback_page.dart b/lib/features/settings/presentation/pages/feedback_page.dart index af50acd..4e7bd3b 100644 --- a/lib/features/settings/presentation/pages/feedback_page.dart +++ b/lib/features/settings/presentation/pages/feedback_page.dart @@ -1,12 +1,30 @@ /// Page dédiée à l'envoi de commentaires / feedback -/// Permet de soumettre des suggestions, signaler des bugs, ou proposer des idées library feedback_page; import 'package:flutter/material.dart'; -import 'package:get_it/get_it.dart'; -import 'package:dio/dio.dart'; +import '../../../../core/di/injection.dart'; +import '../../../../core/network/api_client.dart'; import '../../../../core/utils/logger.dart'; +import '../../../../shared/design_system/components/uf_app_bar.dart'; import '../../../../shared/design_system/unionflow_design_system.dart'; +import '../../../../shared/widgets/core_card.dart'; + +// ───────────────────────────────────────────────────────────────────────────── +// Données statiques +// ───────────────────────────────────────────────────────────────────────────── + +const _kCategories = [ + _FeedbackCategory('suggestion', 'Suggestion', Icons.lightbulb_outline, AppColors.primary), + _FeedbackCategory('bug', 'Bug / Problème', Icons.bug_report_outlined, AppColors.error), + _FeedbackCategory('amelioration', 'Amélioration', Icons.trending_up, AppColors.success), + _FeedbackCategory('autre', 'Autre', Icons.help_outline, AppColors.primaryDark), +]; + +const _kMaxLength = 1000; + +// ───────────────────────────────────────────────────────────────────────────── +// Page +// ───────────────────────────────────────────────────────────────────────────── class FeedbackPage extends StatefulWidget { const FeedbackPage({super.key}); @@ -19,13 +37,15 @@ class _FeedbackPageState extends State { final _messageController = TextEditingController(); String _selectedCategory = 'suggestion'; bool _isSending = false; + int _charCount = 0; - static const _categories = [ - _FeedbackCategory('suggestion', 'Suggestion', Icons.lightbulb, AppColors.primaryGreen), - _FeedbackCategory('bug', 'Bug / Problème', Icons.bug_report, AppColors.error), - _FeedbackCategory('amelioration', 'Amélioration', Icons.trending_up, AppColors.success), - _FeedbackCategory('autre', 'Autre', Icons.help_outline, AppColors.brandGreen), - ]; + @override + void initState() { + super.initState(); + _messageController.addListener( + () => setState(() => _charCount = _messageController.text.length), + ); + } @override void dispose() { @@ -39,16 +59,14 @@ class _FeedbackPageState extends State { _showSnackBar('Veuillez saisir un message.', isError: true); return; } - setState(() => _isSending = true); - try { - await GetIt.I().post( + final cat = _kCategories.firstWhere((c) => c.id == _selectedCategory); + await getIt().post( '/api/feedback', data: { - 'subject': 'Feedback mobile [$_selectedCategory]', + 'subject': '[${cat.label}] Feedback mobile', 'message': message, - 'categorie': _selectedCategory, }, ); if (mounted) { @@ -57,9 +75,7 @@ class _FeedbackPageState extends State { } } catch (e, st) { AppLogger.error('FeedbackPage: envoi feedback échoué', error: e, stackTrace: st); - if (mounted) { - _showSnackBar('Envoi échoué. Réessayez plus tard.', isError: true); - } + if (mounted) _showSnackBar('Envoi échoué. Réessayez plus tard.', isError: true); } finally { if (mounted) setState(() => _isSending = false); } @@ -79,129 +95,56 @@ class _FeedbackPageState extends State { Widget build(BuildContext context) { return Scaffold( backgroundColor: Theme.of(context).scaffoldBackgroundColor, - body: Column( - children: [ - _buildHeader(), - Expanded( - child: SingleChildScrollView( - padding: const EdgeInsets.all(12), - child: Column( - children: [ - const SizedBox(height: 8), - _buildCategorySection(), - const SizedBox(height: 8), - _buildMessageSection(), - const SizedBox(height: 8), - _buildSubmitButton(), - const SizedBox(height: 80), - ], - ), - ), - ), - ], + appBar: UFAppBar( + title: 'Commentaires', + moduleGradient: ModuleColors.supportGradient, ), - ); - } - - Widget _buildHeader() { - return Container( - margin: const EdgeInsets.symmetric(horizontal: SpacingTokens.sm, vertical: SpacingTokens.xs), - padding: const EdgeInsets.all(SpacingTokens.md), - decoration: BoxDecoration( - gradient: const LinearGradient( - colors: [AppColors.brandGreen, AppColors.primaryGreen], - begin: Alignment.topLeft, - end: Alignment.bottomRight, - ), - borderRadius: BorderRadius.circular(SpacingTokens.xl), - boxShadow: [ - BoxShadow( - color: AppColors.primaryGreen.withOpacity(0.3), - blurRadius: 20, - offset: const Offset(0, 8), - ), - ], - ), - child: SafeArea( - bottom: false, - child: Row( + body: SafeArea( + top: false, + child: ListView( + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), children: [ - IconButton( - onPressed: () => Navigator.of(context).pop(), - icon: const Icon(Icons.arrow_back, color: Colors.white), - ), - Container( - padding: const EdgeInsets.all(8), - decoration: BoxDecoration( - color: Colors.white.withOpacity(0.2), - borderRadius: BorderRadius.circular(8), - ), - child: const Icon(Icons.feedback, color: Colors.white, size: 20), - ), - const SizedBox(width: 12), - const Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, - children: [ - Text( - 'Commentaires', - style: TextStyle( - fontSize: 20, - fontWeight: FontWeight.bold, - color: Colors.white, - ), - ), - Text( - 'Aidez-nous à améliorer UnionFlow', - style: TextStyle( - fontSize: 14, - color: Colors.white70, - ), - ), - ], - ), - ), + const SizedBox(height: 4), + _buildCategorySection(), + const SizedBox(height: 8), + _buildMessageSection(), + const SizedBox(height: 12), + _buildSubmitButton(), + const SizedBox(height: 80), ], ), ), ); } + // ── Section catégories ──────────────────────────────────────────────────── + Widget _buildCategorySection() { - final isDark = Theme.of(context).brightness == Brightness.dark; - final textPrimary = isDark ? AppColors.textPrimaryDark : AppColors.textPrimaryLight; - final textSecondary = isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight; - return Container( - padding: const EdgeInsets.all(10), - decoration: BoxDecoration( - color: isDark ? AppColors.darkSurface : Colors.white, - borderRadius: BorderRadius.circular(10), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.05), - blurRadius: 10, - offset: const Offset(0, 2), - ), - ], - ), + final scheme = Theme.of(context).colorScheme; + return CoreCard( + padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ - Icon(Icons.category, color: textSecondary, size: 20), + Icon(Icons.category_outlined, color: scheme.onSurfaceVariant, size: 16), const SizedBox(width: 8), Text( - 'Type de retour', - style: AppTypography.headerSmall.copyWith(fontWeight: FontWeight.w600, color: textPrimary), + 'TYPE DE RETOUR', + style: AppTypography.subtitleSmall.copyWith( + fontWeight: FontWeight.bold, + letterSpacing: 1.1, + color: scheme.onSurfaceVariant, + ), ), ], ), - const SizedBox(height: 8), + const SizedBox(height: 12), Wrap( - spacing: 10, - runSpacing: 10, - children: _categories.map((cat) => _buildCategoryChip(cat)).toList(), + spacing: 8, + runSpacing: 8, + children: _kCategories.map(_buildCategoryChip).toList(), ), ], ), @@ -209,34 +152,37 @@ class _FeedbackPageState extends State { } Widget _buildCategoryChip(_FeedbackCategory cat) { - final isDark = Theme.of(context).brightness == Brightness.dark; + final scheme = Theme.of(context).colorScheme; final isSelected = _selectedCategory == cat.id; - final textSecondary = isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight; return InkWell( onTap: () => setState(() => _selectedCategory = cat.id), - borderRadius: BorderRadius.circular(12), - child: Container( - padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), + borderRadius: BorderRadius.circular(8), + child: AnimatedContainer( + duration: const Duration(milliseconds: 180), + padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), decoration: BoxDecoration( - color: isSelected - ? cat.color.withOpacity(0.12) - : (isDark ? AppColors.darkBackground : Colors.grey[50]), - borderRadius: BorderRadius.circular(12), + color: isSelected ? cat.color.withOpacity(0.1) : scheme.surface, + borderRadius: BorderRadius.circular(8), border: Border.all( - color: isSelected ? cat.color.withOpacity(0.5) : (isDark ? AppColors.darkBorder : Colors.grey[200]!), + color: isSelected ? cat.color.withOpacity(0.5) : scheme.outlineVariant, width: isSelected ? 1.5 : 1, ), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ - Icon(cat.icon, size: 18, color: isSelected ? cat.color : textSecondary), - const SizedBox(width: 8), + Icon( + cat.icon, + size: 15, + color: isSelected ? cat.color : scheme.onSurfaceVariant, + ), + const SizedBox(width: 6), Text( cat.label, - style: AppTypography.bodyTextSmall.copyWith( + style: AppTypography.actionText.copyWith( + fontSize: 12, fontWeight: FontWeight.w600, - color: isSelected ? cat.color : textSecondary, + color: isSelected ? cat.color : scheme.onSurfaceVariant, ), ), ], @@ -245,59 +191,70 @@ class _FeedbackPageState extends State { ); } + // ── Section message ──────────────────────────────────────────────────────── + Widget _buildMessageSection() { - final isDark = Theme.of(context).brightness == Brightness.dark; - final textPrimary = isDark ? AppColors.textPrimaryDark : AppColors.textPrimaryLight; - final textSecondary = isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight; - return Container( - padding: const EdgeInsets.all(10), - decoration: BoxDecoration( - color: isDark ? AppColors.darkSurface : Colors.white, - borderRadius: BorderRadius.circular(10), - boxShadow: [ - BoxShadow( - color: Colors.black.withOpacity(0.05), - blurRadius: 10, - offset: const Offset(0, 2), - ), - ], - ), + final scheme = Theme.of(context).colorScheme; + final isNearLimit = _charCount > _kMaxLength * 0.85; + return CoreCard( + padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ - Icon(Icons.edit_note, color: textSecondary, size: 20), + Icon(Icons.edit_note_outlined, color: scheme.onSurfaceVariant, size: 16), const SizedBox(width: 8), Text( - 'Votre message', - style: AppTypography.headerSmall.copyWith(fontWeight: FontWeight.w600, color: textPrimary), + 'VOTRE MESSAGE', + style: AppTypography.subtitleSmall.copyWith( + fontWeight: FontWeight.bold, + letterSpacing: 1.1, + color: scheme.onSurfaceVariant, + ), + ), + const Spacer(), + Text( + '$_charCount / $_kMaxLength', + style: AppTypography.subtitleSmall.copyWith( + fontSize: 10, + color: isNearLimit ? AppColors.error : scheme.onSurfaceVariant, + fontWeight: isNearLimit ? FontWeight.bold : FontWeight.normal, + ), ), ], ), - const SizedBox(height: 8), + const SizedBox(height: 12), TextField( controller: _messageController, - maxLines: 6, - style: AppTypography.bodyTextSmall.copyWith(color: textPrimary), + maxLines: 7, + maxLength: _kMaxLength, + buildCounter: (_, {required currentLength, required isFocused, maxLength}) => + const SizedBox.shrink(), + style: AppTypography.bodyTextSmall.copyWith( + color: scheme.onSurface, + fontSize: 13, + ), decoration: InputDecoration( hintText: 'Décrivez votre suggestion, problème ou idée...', - hintStyle: AppTypography.subtitleSmall.copyWith(color: textSecondary), - border: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: BorderSide(color: isDark ? AppColors.darkBorder : Colors.grey[300]!), - ), - enabledBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: BorderSide(color: isDark ? AppColors.darkBorder : Colors.grey[300]!), - ), - focusedBorder: OutlineInputBorder( - borderRadius: BorderRadius.circular(12), - borderSide: const BorderSide(color: AppColors.primaryGreen, width: 1.5), + hintStyle: AppTypography.subtitleSmall.copyWith( + color: scheme.onSurfaceVariant, ), filled: true, - fillColor: isDark ? AppColors.darkBackground : Colors.grey[50], - alignLabelWithHint: true, + fillColor: scheme.surfaceContainerHighest.withOpacity(0.4), + border: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide(color: scheme.outlineVariant), + ), + enabledBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide(color: scheme.outlineVariant), + ), + focusedBorder: OutlineInputBorder( + borderRadius: BorderRadius.circular(8), + borderSide: BorderSide(color: ModuleColors.support, width: 1.5), + ), + contentPadding: const EdgeInsets.all(12), ), ), ], @@ -305,6 +262,8 @@ class _FeedbackPageState extends State { ); } + // ── Bouton envoi ────────────────────────────────────────────────────────── + Widget _buildSubmitButton() { return SizedBox( width: double.infinity, @@ -312,32 +271,34 @@ class _FeedbackPageState extends State { onPressed: _isSending ? null : _submitFeedback, icon: _isSending ? const SizedBox( - width: 18, - height: 18, + width: 16, + height: 16, child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white), ) - : const Icon(Icons.send, color: Colors.white), + : const Icon(Icons.send_rounded, color: Colors.white, size: 16), label: Text( _isSending ? 'Envoi en cours...' : 'Envoyer le commentaire', - style: const TextStyle( - fontSize: 16, + style: AppTypography.actionText.copyWith( fontWeight: FontWeight.w600, color: Colors.white, ), ), style: ElevatedButton.styleFrom( - backgroundColor: AppColors.primaryGreen, + backgroundColor: ModuleColors.support, + disabledBackgroundColor: ModuleColors.support.withOpacity(0.5), padding: const EdgeInsets.symmetric(vertical: 10), - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(8), - ), - elevation: 2, + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(8)), + elevation: 0, ), ), ); } } +// ───────────────────────────────────────────────────────────────────────────── +// Modèle de catégorie +// ───────────────────────────────────────────────────────────────────────────── + class _FeedbackCategory { final String id; final String label; diff --git a/lib/features/settings/presentation/pages/language_settings_page.dart b/lib/features/settings/presentation/pages/language_settings_page.dart index a23a9bf..835c915 100644 --- a/lib/features/settings/presentation/pages/language_settings_page.dart +++ b/lib/features/settings/presentation/pages/language_settings_page.dart @@ -54,24 +54,27 @@ class _LanguageSettingsPageState extends State { Widget build(BuildContext context) { return Scaffold( backgroundColor: Theme.of(context).scaffoldBackgroundColor, - body: Column( - children: [ - _buildHeader(), - Expanded( - child: SingleChildScrollView( - padding: const EdgeInsets.all(12), - child: Column( - children: [ - const SizedBox(height: 8), - _buildLanguageList(), - const SizedBox(height: 8), - _buildInfoSection(), - const SizedBox(height: 80), - ], + body: SafeArea( + top: false, + child: Column( + children: [ + _buildHeader(), + Expanded( + child: SingleChildScrollView( + padding: const EdgeInsets.all(12), + child: Column( + children: [ + const SizedBox(height: 8), + _buildLanguageList(), + const SizedBox(height: 8), + _buildInfoSection(), + const SizedBox(height: 80), + ], + ), ), ), - ), - ], + ], + ), ), ); } @@ -81,15 +84,15 @@ class _LanguageSettingsPageState extends State { margin: const EdgeInsets.all(SpacingTokens.lg), padding: const EdgeInsets.all(SpacingTokens.xxl), decoration: BoxDecoration( - gradient: const LinearGradient( - colors: [AppColors.brandGreen, AppColors.primaryGreen], + gradient: LinearGradient( + colors: ModuleColors.parametresGradient, begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(SpacingTokens.xl), boxShadow: [ BoxShadow( - color: AppColors.primaryGreen.withOpacity(0.3), + color: ModuleColors.parametres.withOpacity(0.3), blurRadius: 20, offset: const Offset(0, 8), ), @@ -142,12 +145,12 @@ class _LanguageSettingsPageState extends State { Widget _buildLanguageList() { final isDark = Theme.of(context).brightness == Brightness.dark; - final textPrimary = isDark ? AppColors.textPrimaryDark : AppColors.textPrimaryLight; - final textSecondary = isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight; + final textPrimary = isDark ? AppColors.textPrimaryDark : AppColors.textPrimary; + final textSecondary = isDark ? AppColors.textSecondaryDark : AppColors.textSecondary; return Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( - color: isDark ? AppColors.darkSurface : Colors.white, + color: isDark ? AppColors.surfaceDark : Colors.white, borderRadius: BorderRadius.circular(10), ), child: Column( @@ -173,8 +176,8 @@ class _LanguageSettingsPageState extends State { Widget _buildLanguageTile(_LanguageOption lang) { final isDark = Theme.of(context).brightness == Brightness.dark; final isSelected = _selectedLanguage == lang.name; - final textPrimary = isDark ? AppColors.textPrimaryDark : AppColors.textPrimaryLight; - final textSecondary = isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight; + final textPrimary = isDark ? AppColors.textPrimaryDark : AppColors.textPrimary; + final textSecondary = isDark ? AppColors.textSecondaryDark : AppColors.textSecondary; return Padding( padding: const EdgeInsets.only(bottom: 6), child: InkWell( @@ -184,12 +187,12 @@ class _LanguageSettingsPageState extends State { padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 7), decoration: BoxDecoration( color: isSelected - ? AppColors.primaryGreen.withOpacity(0.08) - : (isDark ? AppColors.darkBackground : Colors.grey[50]), + ? AppColors.primary.withOpacity(0.08) + : (isDark ? AppColors.backgroundSubtleDark : AppColors.backgroundSubtle), borderRadius: BorderRadius.circular(8), border: isSelected - ? Border.all(color: AppColors.primaryGreen.withOpacity(0.4), width: 1.5) - : Border.all(color: isDark ? AppColors.darkBorder : Colors.grey[200]!), + ? Border.all(color: AppColors.primary.withOpacity(0.4), width: 1.5) + : Border.all(color: isDark ? AppColors.surfaceVariantDark : AppColors.surfaceVariant), ), child: Row( children: [ @@ -203,7 +206,7 @@ class _LanguageSettingsPageState extends State { lang.name, style: AppTypography.bodyTextSmall.copyWith( fontWeight: FontWeight.w600, - color: isSelected ? AppColors.primaryGreen : textPrimary, + color: isSelected ? AppColors.primary : textPrimary, ), ), Text( @@ -214,7 +217,7 @@ class _LanguageSettingsPageState extends State { ), ), if (isSelected) - const Icon(Icons.check_circle, color: AppColors.primaryGreen, size: 22), + const Icon(Icons.check_circle, color: AppColors.primary, size: 22), ], ), ), @@ -224,12 +227,12 @@ class _LanguageSettingsPageState extends State { Widget _buildInfoSection() { final isDark = Theme.of(context).brightness == Brightness.dark; - final textPrimary = isDark ? AppColors.textPrimaryDark : AppColors.textPrimaryLight; - final textSecondary = isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight; + final textPrimary = isDark ? AppColors.textPrimaryDark : AppColors.textPrimary; + final textSecondary = isDark ? AppColors.textSecondaryDark : AppColors.textSecondary; return Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( - color: isDark ? AppColors.darkSurface : Colors.white, + color: isDark ? AppColors.surfaceDark : Colors.white, borderRadius: BorderRadius.circular(10), ), child: Column( diff --git a/lib/features/settings/presentation/pages/privacy_settings_page.dart b/lib/features/settings/presentation/pages/privacy_settings_page.dart index 202247c..120d825 100644 --- a/lib/features/settings/presentation/pages/privacy_settings_page.dart +++ b/lib/features/settings/presentation/pages/privacy_settings_page.dart @@ -62,26 +62,29 @@ class _PrivacySettingsPageState extends State { Widget build(BuildContext context) { return Scaffold( backgroundColor: Theme.of(context).scaffoldBackgroundColor, - body: Column( - children: [ - _buildHeader(), - Expanded( - child: SingleChildScrollView( - padding: const EdgeInsets.all(12), - child: Column( - children: [ - const SizedBox(height: 8), - _buildVisibilitySection(), - const SizedBox(height: 8), - _buildDataSection(), - const SizedBox(height: 8), - _buildDangerSection(), - const SizedBox(height: 80), - ], + body: SafeArea( + top: false, + child: Column( + children: [ + _buildHeader(), + Expanded( + child: SingleChildScrollView( + padding: const EdgeInsets.all(12), + child: Column( + children: [ + const SizedBox(height: 8), + _buildVisibilitySection(), + const SizedBox(height: 8), + _buildDataSection(), + const SizedBox(height: 8), + _buildDangerSection(), + const SizedBox(height: 80), + ], + ), ), ), - ), - ], + ], + ), ), ); } @@ -91,15 +94,15 @@ class _PrivacySettingsPageState extends State { margin: const EdgeInsets.all(SpacingTokens.lg), padding: const EdgeInsets.all(SpacingTokens.xxl), decoration: BoxDecoration( - gradient: const LinearGradient( - colors: [AppColors.brandGreen, AppColors.primaryGreen], + gradient: LinearGradient( + colors: ModuleColors.parametresGradient, begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(SpacingTokens.xl), boxShadow: [ BoxShadow( - color: AppColors.primaryGreen.withOpacity(0.3), + color: ModuleColors.parametres.withOpacity(0.3), blurRadius: 20, offset: const Offset(0, 8), ), @@ -222,13 +225,13 @@ class _PrivacySettingsPageState extends State { child: Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 7), decoration: BoxDecoration( - color: Colors.red.withOpacity(0.05), + color: AppColors.error.withOpacity(0.05), borderRadius: BorderRadius.circular(8), - border: Border.all(color: Colors.red.withOpacity(0.2)), + border: Border.all(color: AppColors.error.withOpacity(0.2)), ), child: Row( children: [ - const Icon(Icons.delete_forever, color: Colors.red, size: 20), + const Icon(Icons.delete_forever, color: AppColors.error, size: 20), const SizedBox(width: 12), Expanded( child: Column( @@ -239,7 +242,7 @@ class _PrivacySettingsPageState extends State { style: TextStyle( fontSize: 14, fontWeight: FontWeight.w600, - color: Colors.red, + color: AppColors.error, ), ), Builder( @@ -248,7 +251,7 @@ class _PrivacySettingsPageState extends State { return Text( 'Supprimer définitivement toutes vos données', style: AppTypography.subtitleSmall.copyWith( - color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight, + color: isDark ? AppColors.textSecondaryDark : AppColors.textSecondary, ), ); }, @@ -256,7 +259,7 @@ class _PrivacySettingsPageState extends State { ], ), ), - const Icon(Icons.arrow_forward_ios, color: Colors.red, size: 16), + const Icon(Icons.arrow_forward_ios, color: AppColors.error, size: 16), ], ), ), @@ -271,7 +274,7 @@ class _PrivacySettingsPageState extends State { builder: (context) => AlertDialog( title: const Row( children: [ - Icon(Icons.warning, color: Colors.red), + Icon(Icons.warning, color: AppColors.error), SizedBox(width: 8), Text('Supprimer le compte'), ], @@ -293,8 +296,8 @@ class _PrivacySettingsPageState extends State { await launchUrl(uri, mode: LaunchMode.externalApplication); } }, - style: ElevatedButton.styleFrom(backgroundColor: Colors.red), - child: const Text('Contacter l\'administrateur', style: TextStyle(color: Colors.white)), + style: ElevatedButton.styleFrom(backgroundColor: AppColors.error), + child: const Text('Contacter l\'administrateur', style: TextStyle(color: AppColors.onPrimary)), ), ], ), @@ -303,12 +306,12 @@ class _PrivacySettingsPageState extends State { Widget _buildSection(String title, String subtitle, IconData icon, List children) { final isDark = Theme.of(context).brightness == Brightness.dark; - final textPrimary = isDark ? AppColors.textPrimaryDark : AppColors.textPrimaryLight; - final textSecondary = isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight; + final textPrimary = isDark ? AppColors.textPrimaryDark : AppColors.textPrimary; + final textSecondary = isDark ? AppColors.textSecondaryDark : AppColors.textSecondary; return Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( - color: isDark ? AppColors.darkSurface : Colors.white, + color: isDark ? AppColors.surfaceDark : AppColors.surface, borderRadius: BorderRadius.circular(10), ), child: Column( @@ -352,17 +355,17 @@ class _PrivacySettingsPageState extends State { ValueChanged onChanged, ) { final isDark = Theme.of(context).brightness == Brightness.dark; - final textPrimary = isDark ? AppColors.textPrimaryDark : AppColors.textPrimaryLight; - final textSecondary = isDark ? AppColors.textSecondaryDark : AppColors.textSecondaryLight; + final textPrimary = isDark ? AppColors.textPrimaryDark : AppColors.textPrimary; + final textSecondary = isDark ? AppColors.textSecondaryDark : AppColors.textSecondary; return Container( padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 7), decoration: BoxDecoration( - color: isDark ? AppColors.darkBackground : Colors.grey[50], + color: isDark ? AppColors.backgroundSubtleDark : AppColors.backgroundSubtle, borderRadius: BorderRadius.circular(8), ), child: Row( children: [ - const Icon(Icons.toggle_on, color: AppColors.primaryGreen, size: 20), + const Icon(Icons.toggle_on, color: AppColors.primary, size: 20), const SizedBox(width: 12), Expanded( child: Column( @@ -382,7 +385,7 @@ class _PrivacySettingsPageState extends State { Switch( value: value, onChanged: onChanged, - activeTrackColor: AppColors.primaryGreen, + activeTrackColor: AppColors.primary, thumbColor: WidgetStateProperty.resolveWith((states) => states.contains(WidgetState.selected) ? Colors.white : null), ), diff --git a/lib/features/solidarity/presentation/widgets/create_demande_aide_dialog.dart b/lib/features/solidarity/presentation/widgets/create_demande_aide_dialog.dart index fa28fa9..a3cd324 100644 --- a/lib/features/solidarity/presentation/widgets/create_demande_aide_dialog.dart +++ b/lib/features/solidarity/presentation/widgets/create_demande_aide_dialog.dart @@ -5,6 +5,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:get_it/get_it.dart'; import '../../../../core/utils/logger.dart'; +import '../../../../shared/design_system/unionflow_design_system.dart'; import '../../bloc/solidarity_bloc.dart'; import '../../data/models/demande_aide_model.dart'; import '../../../organizations/domain/repositories/organization_repository.dart'; @@ -146,7 +147,7 @@ class _CreateDemandeAideDialogState extends State { enabled: false, ) 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: 12), TextFormField( controller: _titreController,