Initialisation du projet geosector complet (web + flutter)
This commit is contained in:
656
flutt/lib/presentation/user/user_dashboard_home_page.dart
Normal file
656
flutt/lib/presentation/user/user_dashboard_home_page.dart
Normal file
@@ -0,0 +1,656 @@
|
||||
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/core/repositories/user_repository.dart';
|
||||
import 'package:geosector_app/core/repositories/passage_repository.dart';
|
||||
import 'package:geosector_app/core/theme/app_theme.dart';
|
||||
import 'package:geosector_app/core/constants/app_keys.dart';
|
||||
import 'package:geosector_app/presentation/widgets/passages/passages_list_widget.dart';
|
||||
import 'package:geosector_app/presentation/widgets/charts/charts.dart';
|
||||
|
||||
class UserDashboardHomePage extends StatefulWidget {
|
||||
const UserDashboardHomePage({super.key});
|
||||
|
||||
@override
|
||||
State<UserDashboardHomePage> createState() => _UserDashboardHomePageState();
|
||||
}
|
||||
|
||||
class _UserDashboardHomePageState extends State<UserDashboardHomePage> {
|
||||
// Formater une date au format JJ/MM/YYYY
|
||||
String _formatDate(DateTime date) {
|
||||
return '${date.day.toString().padLeft(2, '0')}/${date.month.toString().padLeft(2, '0')}/${date.year}';
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final theme = Theme.of(context);
|
||||
// Utiliser l'instance globale définie dans app.dart
|
||||
final size = MediaQuery.of(context).size;
|
||||
final isDesktop = size.width > 900;
|
||||
|
||||
return Scaffold(
|
||||
backgroundColor: Colors.transparent,
|
||||
body: SafeArea(
|
||||
child: SingleChildScrollView(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
'Tableau de bord',
|
||||
style: theme.textTheme.headlineMedium?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
color: theme.colorScheme.primary,
|
||||
),
|
||||
),
|
||||
Builder(builder: (context) {
|
||||
// Récupérer l'opération actuelle
|
||||
final operation = userRepository.getCurrentOperation();
|
||||
if (operation != null) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(top: 4.0),
|
||||
child: Text(
|
||||
'${operation.name} (${_formatDate(operation.dateDebut)}-${_formatDate(operation.dateFin)})',
|
||||
style: theme.textTheme.titleSmall?.copyWith(
|
||||
color: theme.colorScheme.primary.withOpacity(0.7),
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
),
|
||||
);
|
||||
} else {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
}),
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Synthèse des passages
|
||||
_buildSummaryCards(isDesktop),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Graphique des passages
|
||||
_buildPassagesChart(context, theme),
|
||||
|
||||
const SizedBox(height: 24),
|
||||
|
||||
// Derniers passages
|
||||
_buildRecentPassages(context, theme),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Construction des cartes de synthèse
|
||||
Widget _buildSummaryCards(bool isDesktop) {
|
||||
return Column(
|
||||
children: [
|
||||
_buildCombinedPassagesCard(context, isDesktop),
|
||||
const SizedBox(height: 16),
|
||||
_buildCombinedPaymentsCard(isDesktop),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// Construction d'une carte combinée pour les règlements (liste + graphique)
|
||||
Widget _buildCombinedPaymentsCard(bool isDesktop) {
|
||||
// Utiliser les instances globales définies dans app.dart
|
||||
|
||||
// Récupérer l'utilisateur actuel
|
||||
final currentUser = userRepository.getCurrentUser();
|
||||
final int? currentUserId = currentUser?.id;
|
||||
|
||||
// Récupérer tous les passages
|
||||
final passages = passageRepository.getAllPassages();
|
||||
|
||||
// Pas de log ici pour éviter les logs excessifs
|
||||
|
||||
// Initialiser les montants par type de règlement
|
||||
final Map<int, double> paymentAmounts = {
|
||||
0: 0.0, // Pas de règlement
|
||||
1: 0.0, // Espèces
|
||||
2: 0.0, // Chèques
|
||||
3: 0.0, // CB
|
||||
};
|
||||
|
||||
// Compteur pour les passages avec montant > 0
|
||||
int passagesWithPaymentCount = 0;
|
||||
|
||||
// Parcourir les passages et calculer les montants par type de règlement
|
||||
for (final passage in passages) {
|
||||
// Vérifier si le passage appartient à l'utilisateur actuel
|
||||
if (currentUserId != null && passage.fkUser == currentUserId) {
|
||||
final int typeReglement = passage.fkTypeReglement;
|
||||
|
||||
// Convertir la chaîne de montant en double
|
||||
double montant = 0.0;
|
||||
try {
|
||||
// Gérer les formats possibles (virgule ou point)
|
||||
String montantStr = passage.montant.replaceAll(',', '.');
|
||||
montant = double.tryParse(montantStr) ?? 0.0;
|
||||
} catch (e) {
|
||||
debugPrint('Erreur de conversion du montant: ${passage.montant}');
|
||||
}
|
||||
|
||||
// Ne compter que les passages avec un montant > 0
|
||||
if (montant > 0) {
|
||||
passagesWithPaymentCount++;
|
||||
|
||||
// Ajouter au montant total par type de règlement
|
||||
if (paymentAmounts.containsKey(typeReglement)) {
|
||||
paymentAmounts[typeReglement] =
|
||||
(paymentAmounts[typeReglement] ?? 0.0) + montant;
|
||||
} else {
|
||||
// Si le type n'est pas dans notre map, l'ajouter à la catégorie par défaut (0: Pas de règlement)
|
||||
paymentAmounts[0] = (paymentAmounts[0] ?? 0.0) + montant;
|
||||
// Type de règlement inconnu, ajouté à la catégorie 'Pas de règlement'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Calculer le total des règlements
|
||||
final double totalPayments =
|
||||
paymentAmounts.values.fold(0.0, (sum, amount) => sum + amount);
|
||||
|
||||
// Convertir les montants en objets PaymentData pour le graphique
|
||||
final List<PaymentData> paymentDataList =
|
||||
PaymentUtils.getPaymentDataFromAmounts(paymentAmounts);
|
||||
|
||||
return Card(
|
||||
elevation: 4,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Stack(
|
||||
children: [
|
||||
// Symbole euro en arrière-plan
|
||||
Positioned.fill(
|
||||
child: Center(
|
||||
child: Icon(
|
||||
Icons.euro_symbol,
|
||||
size: 180,
|
||||
color: Colors.blue.withOpacity(0.07), // Bleuté et estompé
|
||||
),
|
||||
),
|
||||
),
|
||||
// Contenu principal
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.payments,
|
||||
color: AppTheme.accentColor,
|
||||
size: 24,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
'Règlements sur $passagesWithPaymentCount passages',
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'${totalPayments.toStringAsFixed(2)} €',
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppTheme.accentColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const Divider(height: 24),
|
||||
SizedBox(
|
||||
height: 250,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Liste des règlements (côté gauche)
|
||||
Expanded(
|
||||
flex: isDesktop ? 1 : 2,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
...AppKeys.typesReglements.entries.map((entry) {
|
||||
final int typeId = entry.key;
|
||||
final Map<String, dynamic> typeData = entry.value;
|
||||
final double amount =
|
||||
paymentAmounts[typeId] ?? 0.0;
|
||||
final Color color =
|
||||
Color(typeData['couleur'] as int);
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8.0),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 24,
|
||||
height: 24,
|
||||
decoration: BoxDecoration(
|
||||
color: color,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(
|
||||
typeData['icon_data'] as IconData,
|
||||
color: Colors.white,
|
||||
size: 16,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
typeData['titre'] as String,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
'${amount.toStringAsFixed(2)} €',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// Séparateur vertical
|
||||
if (isDesktop) const VerticalDivider(width: 24),
|
||||
|
||||
// Graphique en camembert (côté droit)
|
||||
Expanded(
|
||||
flex: isDesktop ? 1 : 2,
|
||||
child: PaymentPieChart(
|
||||
payments: paymentDataList,
|
||||
size: double
|
||||
.infinity, // Utiliser tout l'espace disponible
|
||||
labelSize: 12,
|
||||
showPercentage: true,
|
||||
showIcons: false, // Désactiver les icônes
|
||||
showLegend: false,
|
||||
isDonut: true,
|
||||
innerRadius: '50%',
|
||||
enable3DEffect: true, // Activer l'effet 3D
|
||||
effect3DIntensity:
|
||||
1.5, // Intensité de l'effet 3D plus forte
|
||||
enableEnhancedExplode:
|
||||
true, // Activer l'effet d'explosion amélioré
|
||||
useGradient:
|
||||
true, // Utiliser des dégradés pour renforcer l'effet 3D
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Construction d'une carte combinée pour les passages (liste + graphique)
|
||||
Widget _buildCombinedPassagesCard(BuildContext context, bool isDesktop) {
|
||||
// Utiliser les instances globales définies dans app.dart
|
||||
|
||||
// Récupérer l'utilisateur actuel
|
||||
final currentUser = userRepository.getCurrentUser();
|
||||
final int? currentUserId = currentUser?.id;
|
||||
|
||||
// Récupérer tous les passages
|
||||
final passages = passageRepository.getAllPassages();
|
||||
|
||||
// Pas de log ici pour éviter les logs excessifs
|
||||
|
||||
// Compter les passages par type
|
||||
final Map<int, int> passagesCounts = {
|
||||
1: 0, // Effectués
|
||||
2: 0, // À finaliser
|
||||
3: 0, // Refusés
|
||||
4: 0, // Dons
|
||||
5: 0, // Lots
|
||||
6: 0, // Maisons vides
|
||||
};
|
||||
|
||||
// Créer un map pour compter les types de passages
|
||||
final Map<int, int> typesCount = {};
|
||||
final Map<int, int> userTypesCount = {};
|
||||
|
||||
// Parcourir les passages et les compter par type
|
||||
for (final passage in passages) {
|
||||
final typeId = passage.fkType;
|
||||
final int passageUserId = passage.fkUser;
|
||||
|
||||
// Compter les occurrences de chaque type pour le débogage
|
||||
typesCount[typeId] = (typesCount[typeId] ?? 0) + 1;
|
||||
|
||||
// Vérifier si le passage appartient à l'utilisateur actuel ou est de type 2
|
||||
bool shouldCount = typeId == 2 ||
|
||||
(currentUserId != null && passageUserId == currentUserId);
|
||||
|
||||
if (shouldCount) {
|
||||
// Compter pour les statistiques de l'utilisateur
|
||||
userTypesCount[typeId] = (userTypesCount[typeId] ?? 0) + 1;
|
||||
|
||||
// Ajouter au compteur des passages par type
|
||||
if (passagesCounts.containsKey(typeId)) {
|
||||
passagesCounts[typeId] = passagesCounts[typeId]! + 1;
|
||||
} else {
|
||||
// Si le type n'est pas dans notre map, l'ajouter à la catégorie par défaut (2: À finaliser)
|
||||
passagesCounts[2] = passagesCounts[2]! + 1;
|
||||
// Type de passage inconnu ajouté à 'A finaliser'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Pas de log ici pour éviter les logs excessifs
|
||||
|
||||
// Calculer le total des passages pour l'utilisateur (somme des valeurs dans userTypesCount)
|
||||
final int totalUserPassages =
|
||||
userTypesCount.values.fold(0, (sum, count) => sum + count);
|
||||
|
||||
return Card(
|
||||
elevation: 4,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16.0, 12.0, 16.0,
|
||||
8.0), // Réduire les paddings vertical pour donner plus d'espace au graphique
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Icon(
|
||||
Icons.route,
|
||||
color: AppTheme.primaryColor,
|
||||
size: 24,
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Builder(builder: (context) {
|
||||
// Récupérer les secteurs de l'utilisateur
|
||||
final userSectors = userRepository.getUserSectors();
|
||||
final int sectorCount = userSectors.length;
|
||||
|
||||
// Déterminer le titre en fonction du nombre de secteurs
|
||||
String title = 'Passages';
|
||||
if (sectorCount > 1) {
|
||||
title = 'Passages sur mes $sectorCount secteurs';
|
||||
}
|
||||
|
||||
return Text(
|
||||
title,
|
||||
style: const TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
Text(
|
||||
totalUserPassages.toString(),
|
||||
style: TextStyle(
|
||||
fontSize: 20,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: AppTheme.primaryColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const Divider(height: 24),
|
||||
SizedBox(
|
||||
height: 250,
|
||||
child: Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Liste des passages (côté gauche)
|
||||
Expanded(
|
||||
flex: isDesktop ? 1 : 2,
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
...AppKeys.typesPassages.entries.map((entry) {
|
||||
final int typeId = entry.key;
|
||||
final Map<String, dynamic> typeData = entry.value;
|
||||
final int count = passagesCounts[typeId] ?? 0;
|
||||
final Color color = Color(typeData['couleur2']
|
||||
as int); // Utiliser la deuxième couleur
|
||||
final IconData iconData = typeData['icon_data']
|
||||
as IconData; // Utiliser l'icône définie dans AppKeys
|
||||
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8.0),
|
||||
child: Row(
|
||||
children: [
|
||||
Container(
|
||||
width: 24,
|
||||
height: 24,
|
||||
decoration: BoxDecoration(
|
||||
color: color,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: Icon(
|
||||
iconData,
|
||||
color: Colors.white,
|
||||
size: 16,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(
|
||||
typeData['titres'] as String,
|
||||
style: const TextStyle(
|
||||
fontSize: 14,
|
||||
),
|
||||
),
|
||||
),
|
||||
Text(
|
||||
count.toString(),
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: color,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
// Séparateur vertical
|
||||
if (isDesktop) const VerticalDivider(width: 24),
|
||||
|
||||
// Graphique en camembert (côté droit)
|
||||
Expanded(
|
||||
flex: isDesktop ? 1 : 2,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: PassagePieChart(
|
||||
passagesByType: passagesCounts,
|
||||
size: double
|
||||
.infinity, // Utiliser tout l'espace disponible
|
||||
labelSize: 12,
|
||||
showPercentage: true,
|
||||
showIcons: false, // Désactiver les icônes
|
||||
showLegend: false, // Désactiver la légende
|
||||
isDonut: true, // Activer le format donut
|
||||
innerRadius: '50%' // Rayon interne du donut
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Construction du graphique des passages
|
||||
Widget _buildPassagesChart(BuildContext context, ThemeData theme) {
|
||||
// Définir les types de passages à exclure
|
||||
// Selon la mémoire, le type 2 correspond aux passages "A finaliser"
|
||||
// et nous voulons les exclure du comptage pour l'utilisateur actuel
|
||||
final List<int> excludePassageTypes = [2];
|
||||
|
||||
return Card(
|
||||
elevation: 4,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16.0, 12.0, 16.0,
|
||||
8.0), // Réduire les paddings vertical pour donner plus d'espace
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// Titre supprimé car déjà présent dans le widget ActivityChart
|
||||
SizedBox(
|
||||
height:
|
||||
350, // Augmentation de la hauteur à 350px pour résoudre le problème de l'axe Y
|
||||
child: ActivityChart(
|
||||
// Utiliser le chargement depuis Hive directement dans le widget
|
||||
loadFromHive: true,
|
||||
// Ne pas filtrer par utilisateur (afficher tous les passages)
|
||||
showAllPassages: true,
|
||||
// Exclure les passages de type 2 (A finaliser)
|
||||
excludePassageTypes: excludePassageTypes,
|
||||
// Afficher les 15 derniers jours
|
||||
daysToShow: 15,
|
||||
periodType: 'Jour',
|
||||
height:
|
||||
350, // Augmentation de la hauteur à 350px aussi dans le widget
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// Construction de la liste des derniers passages
|
||||
Widget _buildRecentPassages(BuildContext context, ThemeData theme) {
|
||||
// Utiliser les instances globales définies dans app.dart
|
||||
|
||||
// Récupérer tous les passages et les trier par date (les plus récents d'abord)
|
||||
final allPassages = passageRepository.getAllPassages();
|
||||
allPassages.sort((a, b) => b.passedAt.compareTo(a.passedAt));
|
||||
|
||||
// Limiter aux 10 passages les plus récents
|
||||
final recentPassagesModels = allPassages.take(10).toList();
|
||||
|
||||
// Convertir les modèles de passage au format attendu par le widget PassagesListWidget
|
||||
final List<Map<String, dynamic>> recentPassages =
|
||||
recentPassagesModels.map((passage) {
|
||||
// Construire l'adresse complète à partir des champs disponibles
|
||||
final String address =
|
||||
'${passage.numero} ${passage.rue}${passage.rueBis.isNotEmpty ? ' ${passage.rueBis}' : ''}, ${passage.ville}';
|
||||
|
||||
// Convertir le montant en double
|
||||
final double amount = double.tryParse(passage.montant) ?? 0.0;
|
||||
|
||||
return {
|
||||
'id': passage.id.toString(),
|
||||
'address': address,
|
||||
'amount': amount,
|
||||
'date': passage.passedAt,
|
||||
'type': passage.fkType,
|
||||
'payment': passage.fkTypeReglement,
|
||||
'name': passage.name,
|
||||
'notes': passage.remarque,
|
||||
'hasReceipt': passage.nomRecu.isNotEmpty,
|
||||
'hasError': passage.emailErreur.isNotEmpty,
|
||||
'fkUser': passage.fkUser, // Ajouter l'ID de l'utilisateur
|
||||
};
|
||||
}).toList();
|
||||
|
||||
return Card(
|
||||
elevation: 4,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(16.0, 16.0, 16.0, 0.0),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Derniers passages',
|
||||
style: theme.textTheme.titleMedium?.copyWith(
|
||||
fontWeight: FontWeight.bold,
|
||||
),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () {
|
||||
// Naviguer vers la page d'historique
|
||||
},
|
||||
child: const Text('Voir tout'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
// Utilisation du widget commun PassagesListWidget
|
||||
PassagesListWidget(
|
||||
passages: recentPassages,
|
||||
showFilters: false,
|
||||
showSearch: false,
|
||||
showActions: true, // Activer l'affichage des boutons d'action
|
||||
maxPassages: 10,
|
||||
// Exclure les passages de type 2 (À finaliser)
|
||||
excludePassageTypes: [2],
|
||||
// Filtrer par utilisateur courant
|
||||
filterByUserId: userRepository.getCurrentUser()?.id,
|
||||
// Période par défaut (derniers 15 jours)
|
||||
periodFilter: 'last15',
|
||||
onPassageSelected: (passage) {
|
||||
// Action lors de la sélection d'un passage
|
||||
debugPrint('Passage sélectionné: ${passage['id']}');
|
||||
},
|
||||
onDetailsView: (passage) {
|
||||
// Action lors de l'affichage des détails
|
||||
debugPrint('Affichage des détails: ${passage['id']}');
|
||||
},
|
||||
// Callback pour le bouton de modification
|
||||
onPassageEdit: (passage) {
|
||||
// Action lors de la modification d'un passage
|
||||
debugPrint('Modification du passage: ${passage['id']}');
|
||||
// Ici, vous pourriez ouvrir un formulaire d'édition
|
||||
},
|
||||
// Callback pour le bouton de reçu (uniquement pour les passages de type 1)
|
||||
onReceiptView: (passage) {
|
||||
// Action lors de la demande d'affichage du reçu
|
||||
debugPrint('Affichage du reçu pour le passage: ${passage['id']}');
|
||||
// Ici, vous pourriez générer et afficher un PDF
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user