Amélioration de la splash_page et du login

This commit is contained in:
d6soft
2025-06-04 16:51:40 +02:00
parent 8c9e9a21c4
commit bcfdbb2c8b
168 changed files with 153842 additions and 6183 deletions

View File

@@ -1,5 +1,5 @@
import 'package:flutter/material.dart';
import 'package:geosector_app/shared/app_theme.dart';
import 'package:geosector_app/core/theme/app_theme.dart';
import 'package:geosector_app/presentation/widgets/chat/chat_sidebar.dart';
import 'package:geosector_app/presentation/widgets/chat/chat_messages.dart';
import 'package:geosector_app/presentation/widgets/chat/chat_input.dart';

View File

@@ -3,18 +3,13 @@ import 'package:flutter/material.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'dart:math' as math;
import 'package:hive_flutter/hive_flutter.dart';
import 'package:geosector_app/presentation/widgets/charts/activity_chart.dart';
import 'package:geosector_app/presentation/widgets/charts/passage_pie_chart.dart';
import 'package:geosector_app/presentation/widgets/charts/payment_pie_chart.dart';
import 'package:geosector_app/presentation/widgets/charts/payment_data.dart';
import 'package:geosector_app/presentation/widgets/sector_distribution_card.dart';
import 'package:geosector_app/core/repositories/user_repository.dart';
import 'package:geosector_app/core/repositories/passage_repository.dart';
import 'package:geosector_app/presentation/widgets/charts/charts.dart';
import 'package:geosector_app/core/data/models/passage_model.dart';
import 'package:geosector_app/core/data/models/operation_model.dart';
import 'package:geosector_app/core/data/models/sector_model.dart';
import 'package:geosector_app/core/constants/app_keys.dart';
import 'package:geosector_app/shared/app_theme.dart';
import 'package:geosector_app/core/theme/app_theme.dart';
/// Class pour dessiner les petits points blancs sur le fond
class DotsPainter extends CustomPainter {
@@ -281,7 +276,7 @@ class _AdminDashboardHomePageState extends State<AdminDashboardHomePage> {
0.0,
(sum, passage) =>
sum +
(passage.montant != null && passage.montant.isNotEmpty
(passage.montant.isNotEmpty
? double.tryParse(passage.montant) ?? 0.0
: 0.0));
@@ -310,10 +305,8 @@ class _AdminDashboardHomePageState extends State<AdminDashboardHomePage> {
// Compter les passages par membre
for (final passage in passages) {
if (passage.fkUser != null) {
memberCounts[passage.fkUser!] =
(memberCounts[passage.fkUser!] ?? 0) + 1;
}
memberCounts[passage.fkUser] =
(memberCounts[passage.fkUser] ?? 0) + 1;
}
// Récupérer les informations des membres
@@ -504,7 +497,6 @@ class _AdminDashboardHomePageState extends State<AdminDashboardHomePage> {
key: ValueKey(
'sector_distribution_${isFirstLoad ? 'initial' : 'refreshed'}_$isLoading'),
height: 200,
forceRefresh: !isFirstLoad,
),
),
],
@@ -531,7 +523,6 @@ class _AdminDashboardHomePageState extends State<AdminDashboardHomePage> {
key: ValueKey(
'sector_distribution_${isFirstLoad ? 'initial' : 'refreshed'}_$isLoading'),
height: 200,
forceRefresh: !isFirstLoad,
),
],
),
@@ -550,12 +541,10 @@ class _AdminDashboardHomePageState extends State<AdminDashboardHomePage> {
key: ValueKey(
'activity_chart_${isFirstLoad ? 'initial' : 'refreshed'}_$isLoading'),
height: 350,
loadFromHive: true,
showAllPassages:
true, // Tous les passages, pas seulement ceux de l'utilisateur courant
title: 'Passages réalisés par jour (15 derniers jours)',
daysToShow: 15,
forceRefresh: !isFirstLoad,
),
// Si vous avez besoin de passer l'ID de l'opération en cours, décommentez les lignes suivantes
// child: ActivityChart(
@@ -607,7 +596,7 @@ class _AdminDashboardHomePageState extends State<AdminDashboardHomePage> {
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
const Text(
'Actions sur cette opération',
style: TextStyle(
fontWeight: FontWeight.bold,
@@ -624,7 +613,7 @@ class _AdminDashboardHomePageState extends State<AdminDashboardHomePage> {
context,
'Exporter les données',
Icons.file_download_outlined,
AppTheme.buttonPrimaryColor,
AppTheme.primaryColor,
() {},
),
_buildActionButton(
@@ -705,386 +694,54 @@ class _AdminDashboardHomePageState extends State<AdminDashboardHomePage> {
);
}
Widget _buildChartCard(
BuildContext context,
String title,
Widget chart,
) {
return Container(
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(AppTheme.borderRadiusMedium),
boxShadow: AppTheme.cardShadow,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(AppTheme.spacingM),
child: Text(
title,
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
),
chart,
],
),
);
}
// Construit la carte de répartition par type de passage avec liste
Widget _buildPassageTypeCard(BuildContext context) {
return Container(
height: 300, // Hauteur fixe de 300px
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(AppTheme.borderRadiusMedium),
boxShadow: AppTheme.cardShadow,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(AppTheme.spacingM),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Répartition par type de passage',
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
Text(
'$totalPassages passages',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: AppTheme.primaryColor,
),
),
],
),
),
Expanded(
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: AppTheme.spacingM),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Graphique à gauche
Expanded(
flex: 1,
child: SizedBox(
height: 180, // Taille réduite
child: Builder(
builder: (context) {
// Vérifier si nous avons des données de passages
if (passagesByType.isEmpty) {
debugPrint(
'AdminDashboardHomePage: Aucune donnée de passage disponible pour le graphique');
return const Center(
child: Text('Aucune donnée disponible'),
);
}
// Si nous avons des données, afficher le graphique
// Mais d'abord, vérifier si tous les passages sont de type 2 (à finaliser)
// qui est exclu par défaut dans PassagePieChart
bool hasNonType2Passages = passagesByType.entries.any(
(entry) => entry.key != 2 && entry.value > 0);
debugPrint(
'AdminDashboardHomePage: Données pour le graphique: $passagesByType');
// Créer un widget personnalisé pour afficher le graphique ou un message
// selon le contenu des données
if (passagesByType.isEmpty) {
debugPrint(
'AdminDashboardHomePage: Aucune donnée de passage disponible');
return const Center(
child: Text('Aucune donnée disponible'),
);
}
// Vérifier si nous avons des données pour au moins un type
int totalPassages = 0;
passagesByType
.forEach((_, count) => totalPassages += count);
if (totalPassages == 0) {
debugPrint(
'AdminDashboardHomePage: Aucun passage trouvé');
return const Center(
child: Text('Aucun passage trouvé'),
);
}
// Vérifier si tous les passages sont de type 2 (à finaliser)
if (!hasNonType2Passages) {
debugPrint(
'AdminDashboardHomePage: Tous les passages sont de type 2 (à finaliser)');
// Créer un widget personnalisé pour afficher un message
return Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.info_outline,
color: Colors.orange,
size: 40,
),
const SizedBox(height: 8),
const Text(
'Uniquement des passages à finaliser',
textAlign: TextAlign.center,
style: TextStyle(
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 4),
Text(
'${passagesByType[2] ?? 0} passages',
textAlign: TextAlign.center,
style: TextStyle(
color: Colors.orange,
fontWeight: FontWeight.bold,
),
),
],
);
}
// Sinon, afficher le graphique avec les données
debugPrint(
'AdminDashboardHomePage: Affichage du graphique avec ${passagesByType.length} types');
return PassagePieChart(
size: 180,
passagesByType: passagesByType,
loadFromHive: false,
isDonut: true,
innerRadius: '50%',
showIcons: false,
showLegend: false,
);
},
),
),
),
// Liste des types à droite
Expanded(
flex: 1,
child: Padding(
padding: const EdgeInsets.only(left: AppTheme.spacingM),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.end, // Alignement à droite
mainAxisAlignment: MainAxisAlignment.center,
children: [
...AppKeys.typesPassages.entries.map((entry) {
final int typeId = entry.key;
final Map<String, dynamic> typeInfo = entry.value;
final int count = passagesByType[typeId] ?? 0;
final Color color =
Color(typeInfo['couleur2'] as int);
return Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment
.end, // Alignement à droite
children: [
Expanded(
child: Text(
'$count ${typeInfo['titres']}',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: color,
),
textAlign: TextAlign
.right, // Texte aligné à droite
overflow: TextOverflow.ellipsis,
),
),
const SizedBox(width: 8),
Container(
width: 16,
height: 16,
decoration: BoxDecoration(
color: color,
shape: BoxShape.circle,
),
),
],
),
);
}).toList(),
],
),
),
),
],
),
),
),
],
),
return PassageSummaryCard(
title: 'Répartition par type de passage',
titleColor: AppTheme.primaryColor,
titleIcon: Icons.route,
height: 300,
useValueListenable: false, // Utiliser les données statiques
showAllPassages: true,
excludePassageTypes: const [2], // Exclure "À finaliser"
passagesByType: passagesByType,
customTotalDisplay: (total) => '$totalPassages passages',
isDesktop: MediaQuery.of(context).size.width > 800,
backgroundIcon: Icons.route,
backgroundIconColor: AppTheme.primaryColor,
backgroundIconOpacity: 0.07,
backgroundIconSize: 180,
);
}
// Construit la carte de répartition par mode de paiement
Widget _buildPaymentTypeCard(BuildContext context) {
return Container(
height: 300, // Hauteur fixe de 300px
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(AppTheme.borderRadiusMedium),
boxShadow: AppTheme.cardShadow,
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Padding(
padding: const EdgeInsets.all(AppTheme.spacingM),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'Répartition par mode de paiement',
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
),
),
Text(
'${totalAmounts.toStringAsFixed(2)}',
style: TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: AppTheme.buttonSuccessColor,
),
),
],
),
),
Expanded(
child: Padding(
padding:
const EdgeInsets.symmetric(horizontal: AppTheme.spacingM),
child: Row(
crossAxisAlignment: CrossAxisAlignment.center,
children: [
// Graphique à gauche
Expanded(
flex: 1,
child: SizedBox(
height: 180, // Taille réduite
child: PaymentPieChart(
size: 180,
payments: paymentData,
isDonut: true,
innerRadius: '50%',
showIcons: false,
showLegend: false,
enable3DEffect:
false, // Désactiver l'effet 3D pour conserver les couleurs originales
effect3DIntensity: 0.0, // Pas d'intensité 3D
enableEnhancedExplode: false, // Désactiver l'explosion
useGradient:
false, // Ne pas utiliser de dégradé pour conserver les couleurs originales
),
),
),
// Liste des types de règlement à droite
Expanded(
flex: 1,
child: Padding(
padding: const EdgeInsets.only(left: AppTheme.spacingM),
child: Column(
crossAxisAlignment:
CrossAxisAlignment.end, // Alignement à droite
mainAxisAlignment: MainAxisAlignment.center,
children: [
...[1, 2, 3].map((typeId) {
// Uniquement les types 1, 2 et 3
if (!AppKeys.typesReglements.containsKey(typeId)) {
return const SizedBox
.shrink(); // Ignorer si le type n'existe pas
}
final Map<String, dynamic> typeInfo =
AppKeys.typesReglements[typeId]!;
// Calculer le montant total pour ce type de règlement
double amount = 0.0;
for (final payment in paymentData) {
if (payment.typeId == typeId) {
amount = payment.amount;
break;
}
}
// Ne pas afficher si le montant est 0
if (amount <= 0) {
return const SizedBox.shrink();
}
final Color color =
Color(typeInfo['couleur'] as int);
return Padding(
padding: const EdgeInsets.only(bottom: 8.0),
child: Row(
mainAxisAlignment: MainAxisAlignment
.end, // Alignement à droite
children: [
Expanded(
child: Text(
'${amount.toStringAsFixed(2)}${typeInfo['titre']}',
style: TextStyle(
fontSize: 14,
fontWeight: FontWeight.w500,
color: color,
),
textAlign: TextAlign
.right, // Texte aligné à droite
overflow: TextOverflow.ellipsis,
),
),
const SizedBox(width: 8),
Container(
width: 16,
height: 16,
decoration: BoxDecoration(
color: color,
shape: BoxShape.circle,
),
),
],
),
);
}).toList(),
],
),
),
),
],
),
),
),
],
),
return PaymentSummaryCard(
title: 'Répartition par mode de paiement',
titleColor: AppTheme.buttonSuccessColor,
titleIcon: Icons.euro,
height: 300,
useValueListenable: false, // Utiliser les données statiques
showAllPayments: true,
paymentsByType: _convertPaymentDataToMap(paymentData),
customTotalDisplay: (total) => '${totalAmounts.toStringAsFixed(2)}',
isDesktop: MediaQuery.of(context).size.width > 800,
backgroundIcon: Icons.euro,
backgroundIconColor: AppTheme.primaryColor,
backgroundIconOpacity: 0.07,
backgroundIconSize: 180,
);
}
// Méthode helper pour convertir les PaymentData en Map
Map<int, double> _convertPaymentDataToMap(List<PaymentData> paymentDataList) {
final Map<int, double> result = {};
for (final payment in paymentDataList) {
result[payment.typeId] = payment.amount;
}
return result;
}
Widget _buildActionButton(
BuildContext context,
String label,

View File

@@ -2,11 +2,7 @@ import 'package:geosector_app/app.dart'; // Pour accéder aux instances globales
import 'package:flutter/material.dart';
import 'package:hive_flutter/hive_flutter.dart';
import 'package:geosector_app/presentation/widgets/dashboard_layout.dart';
import 'package:geosector_app/core/repositories/user_repository.dart';
import 'package:geosector_app/core/constants/app_keys.dart';
import 'package:geosector_app/shared/app_theme.dart';
import 'package:geosector_app/presentation/widgets/loading_progress_overlay.dart';
import 'package:geosector_app/core/models/loading_state.dart';
import 'dart:math' as math;
// Import des pages admin
@@ -54,14 +50,114 @@ class _AdminDashboardPageState extends State<AdminDashboardPage>
// Liste des pages à afficher
late final List<Widget> _pages;
// Index de la page Amicale et membres
static const int entitePageIndex = 5;
// Référence à la boîte Hive pour les paramètres
late Box _settingsBox;
// Overlay pour afficher la progression du chargement
OverlayEntry? _progressOverlay;
// Liste des éléments de navigation de base (toujours visibles)
final List<_NavigationItem> _baseNavigationItems = [
const _NavigationItem(
label: 'Tableau de bord',
icon: Icons.dashboard_outlined,
selectedIcon: Icons.dashboard,
page: AdminDashboardHomePage(),
),
const _NavigationItem(
label: 'Statistiques',
icon: Icons.bar_chart_outlined,
selectedIcon: Icons.bar_chart,
page: AdminStatisticsPage(),
),
const _NavigationItem(
label: 'Historique',
icon: Icons.history_outlined,
selectedIcon: Icons.history,
page: AdminHistoryPage(),
),
const _NavigationItem(
label: 'Messages',
icon: Icons.chat_outlined,
selectedIcon: Icons.chat,
page: AdminCommunicationPage(),
),
const _NavigationItem(
label: 'Carte',
icon: Icons.map_outlined,
selectedIcon: Icons.map,
page: AdminMapPage(),
),
];
// Éléments de navigation supplémentaires pour le rôle 2
final List<_NavigationItem> _adminNavigationItems = [
const _NavigationItem(
label: 'Amicale & membres',
icon: Icons.business_outlined,
selectedIcon: Icons.business,
page: AdminEntitePage(),
requiredRole: 2,
),
const _NavigationItem(
label: 'Opérations',
icon: Icons.calendar_today_outlined,
selectedIcon: Icons.calendar_today,
page: Scaffold(body: Center(child: Text('Page Opérations'))),
requiredRole: 2,
),
];
// Construire la liste des destinations de navigation en fonction du rôle
List<NavigationDestination> _buildNavigationDestinations() {
final destinations = <NavigationDestination>[];
final currentUser = userRepository.getCurrentUser();
// Ajouter les éléments de base
for (final item in _baseNavigationItems) {
destinations.add(
NavigationDestination(
icon: Icon(item.icon),
selectedIcon: Icon(item.selectedIcon),
label: item.label,
),
);
}
// Ajouter les éléments admin si l'utilisateur a le rôle requis
if (currentUser?.role == 2) {
for (final item in _adminNavigationItems) {
if (item.requiredRole == null || item.requiredRole == 2) {
destinations.add(
NavigationDestination(
icon: Icon(item.icon),
selectedIcon: Icon(item.selectedIcon),
label: item.label,
),
);
}
}
}
return destinations;
}
// Construire la liste des pages en fonction du rôle
List<Widget> _buildPages() {
final pages = <Widget>[];
final currentUser = userRepository.getCurrentUser();
// Ajouter les pages de base
pages.addAll(_baseNavigationItems.map((item) => item.page));
// Ajouter les pages admin si l'utilisateur a le rôle requis
if (currentUser?.role == 2) {
for (final item in _adminNavigationItems) {
if (item.requiredRole == null || item.requiredRole == 2) {
pages.add(item.page);
}
}
}
return pages;
}
@override
void initState() {
@@ -72,35 +168,19 @@ class _AdminDashboardPageState extends State<AdminDashboardPage>
debugPrint('Initialisation de AdminDashboardPage');
// Vérifier que userRepository est correctement initialisé
if (userRepository == null) {
debugPrint('ERREUR: userRepository est null dans AdminDashboardPage');
debugPrint('userRepository est correctement initialisé');
final currentUser = userRepository.getCurrentUser();
if (currentUser == null) {
debugPrint(
'ERREUR: Aucun utilisateur connecté dans AdminDashboardPage');
} else {
debugPrint('userRepository est correctement initialisé');
// Vérifier l'utilisateur courant
final currentUser = userRepository.getCurrentUser();
if (currentUser == null) {
debugPrint(
'ERREUR: Aucun utilisateur connecté dans AdminDashboardPage',
);
} else {
debugPrint(
'Utilisateur connecté: ${currentUser.username} (${currentUser.id})',
);
}
// Écouter les changements d'état du UserRepository
userRepository.addListener(_handleUserRepositoryChanges);
debugPrint(
'Utilisateur connecté: ${currentUser.username} (${currentUser.id})');
}
userRepository.addListener(_handleUserRepositoryChanges);
_pages = [
const AdminDashboardHomePage(),
const AdminStatisticsPage(),
const AdminHistoryPage(),
const AdminCommunicationPage(),
const AdminMapPage(),
const AdminEntitePage(),
];
// Initialiser les pages et les destinations
_pages = _buildPages();
// Initialiser et charger les paramètres
_initSettings();
@@ -117,10 +197,7 @@ class _AdminDashboardPageState extends State<AdminDashboardPage>
@override
void dispose() {
WidgetsBinding.instance.removeObserver(this);
if (userRepository != null) {
userRepository.removeListener(_handleUserRepositoryChanges);
}
_removeProgressOverlay();
userRepository.removeListener(_handleUserRepositoryChanges);
super.dispose();
}
@@ -134,19 +211,6 @@ class _AdminDashboardPageState extends State<AdminDashboardPage>
// La barre de progression est désactivée, ne rien faire
}
// Méthodes pour gérer l'overlay de progression (désactivées)
void _showProgressOverlay(LoadingState state) {
// La barre de progression est désactivée, ne rien faire
}
void _updateProgressOverlay(LoadingState state) {
// La barre de progression est désactivée, ne rien faire
}
void _removeProgressOverlay() {
// La barre de progression est désactivée, ne rien faire
}
// Initialiser la boîte de paramètres et charger les préférences
Future<void> _initSettings() async {
try {
@@ -233,47 +297,21 @@ class _AdminDashboardPageState extends State<AdminDashboardPage>
],
);
}
/// Construit la liste des destinations de navigation
List<NavigationDestination> _buildNavigationDestinations() {
// Destinations de base toujours présentes
final List<NavigationDestination> destinations = [
const NavigationDestination(
icon: Icon(Icons.dashboard_outlined),
selectedIcon: Icon(Icons.dashboard),
label: 'Tableau de bord',
),
const NavigationDestination(
icon: Icon(Icons.bar_chart_outlined),
selectedIcon: Icon(Icons.bar_chart),
label: 'Statistiques',
),
const NavigationDestination(
icon: Icon(Icons.history_outlined),
selectedIcon: Icon(Icons.history),
label: 'Historique',
),
const NavigationDestination(
icon: Icon(Icons.chat_outlined),
selectedIcon: Icon(Icons.chat),
label: 'Messages',
),
const NavigationDestination(
icon: Icon(Icons.map_outlined),
selectedIcon: Icon(Icons.map),
label: 'Carte',
),
];
// Ajouter la destination "Amicale et membres"
destinations.add(
const NavigationDestination(
icon: Icon(Icons.business_outlined),
selectedIcon: Icon(Icons.business),
label: 'Amicale',
),
);
return destinations;
}
}
// Classe pour représenter une destination de navigation avec sa page associée
class _NavigationItem {
final String label;
final IconData icon;
final IconData selectedIcon;
final Widget page;
final int? requiredRole; // null si accessible à tous les rôles
const _NavigationItem({
required this.label,
required this.icon,
required this.selectedIcon,
required this.page,
this.requiredRole,
});
}

View File

@@ -7,7 +7,7 @@ import 'package:geosector_app/core/constants/app_keys.dart';
import 'package:geosector_app/core/services/location_service.dart';
import 'package:geosector_app/core/data/models/sector_model.dart';
import 'package:geosector_app/core/data/models/passage_model.dart';
import '../../shared/app_theme.dart';
import 'package:geosector_app/core/theme/app_theme.dart';
class AdminMapPage extends StatefulWidget {
const AdminMapPage({Key? key}) : super(key: key);

View File

@@ -1,14 +1,6 @@
import 'package:geosector_app/app.dart'; // Pour accéder aux instances globales
import 'package:flutter/material.dart';
import 'package:geosector_app/app.dart'; // Pour accéder aux instances globales
import 'package:geosector_app/presentation/widgets/charts/activity_chart.dart';
import 'package:geosector_app/presentation/widgets/charts/passage_pie_chart.dart';
import 'package:geosector_app/presentation/widgets/charts/payment_pie_chart.dart';
import 'package:geosector_app/presentation/widgets/charts/payment_data.dart';
import 'package:geosector_app/presentation/widgets/charts/combined_chart.dart';
import 'package:geosector_app/core/repositories/passage_repository.dart';
import 'package:geosector_app/core/repositories/user_repository.dart';
import '../../shared/app_theme.dart';
import 'package:geosector_app/core/theme/app_theme.dart';
import 'package:geosector_app/presentation/widgets/charts/charts.dart';
import 'dart:math' as math;
/// Class pour dessiner les petits points blancs sur le fond
@@ -186,7 +178,6 @@ class _AdminStatisticsPageState extends State<AdminStatisticsPage> {
const SizedBox(height: AppTheme.spacingM),
ActivityChart(
height: 350,
loadFromHive: true,
showAllPassages: true,
title: '',
daysToShow: _daysToShow,
@@ -210,13 +201,21 @@ class _AdminStatisticsPageState extends State<AdminStatisticsPage> {
Expanded(
child: _buildChartCard(
'Répartition par type de passage',
PassagePieChart(
size: 300,
loadFromHive: true,
PassageSummaryCard(
title: '',
titleColor: AppTheme.primaryColor,
titleIcon: Icons.pie_chart,
height: 300,
useValueListenable: true,
showAllPassages: true,
excludePassageTypes: const [
2
], // Exclure "À finaliser"
userId: _selectedUser != 'Tous'
? _getUserIdFromName(_selectedUser)
: null,
isDesktop:
MediaQuery.of(context).size.width > 800,
),
),
),
@@ -224,7 +223,7 @@ class _AdminStatisticsPageState extends State<AdminStatisticsPage> {
Expanded(
child: _buildChartCard(
'Répartition par mode de paiement',
PaymentPieChart(
const PaymentPieChart(
payments: [
PaymentData(
typeId: 1,
@@ -258,19 +257,26 @@ class _AdminStatisticsPageState extends State<AdminStatisticsPage> {
children: [
_buildChartCard(
'Répartition par type de passage',
PassagePieChart(
size: 300,
loadFromHive: true,
PassageSummaryCard(
title: '',
titleColor: AppTheme.primaryColor,
titleIcon: Icons.pie_chart,
height: 300,
useValueListenable: true,
showAllPassages: true,
excludePassageTypes: const [
2
], // Exclure "À finaliser"
userId: _selectedUser != 'Tous'
? _getUserIdFromName(_selectedUser)
: null,
isDesktop: MediaQuery.of(context).size.width > 800,
),
),
const SizedBox(height: AppTheme.spacingM),
_buildChartCard(
'Répartition par mode de paiement',
PaymentPieChart(
const PaymentPieChart(
payments: [
PaymentData(
typeId: 1,
@@ -357,7 +363,7 @@ class _AdminStatisticsPageState extends State<AdminStatisticsPage> {
icon: const Icon(Icons.print),
label: const Text('Imprimer'),
style: ElevatedButton.styleFrom(
backgroundColor: AppTheme.buttonSecondaryColor,
backgroundColor: AppTheme.secondaryColor,
foregroundColor: Colors.white,
),
),