- Préparation de la nouvelle branche pour les évolutions - Mise à jour de la version vers 3.2.4 - Intégration des modifications en cours 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
267 lines
9.0 KiB
Dart
Executable File
267 lines
9.0 KiB
Dart
Executable File
import 'package:geosector_app/app.dart'; // Pour accéder aux instances globales
|
|
import 'package:flutter/material.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';
|
|
import 'package:hive_flutter/hive_flutter.dart';
|
|
import 'package:geosector_app/core/data/models/passage_model.dart';
|
|
|
|
class UserDashboardHomePage extends StatefulWidget {
|
|
const UserDashboardHomePage({super.key});
|
|
|
|
@override
|
|
State<UserDashboardHomePage> createState() => _UserDashboardHomePageState();
|
|
}
|
|
|
|
class _UserDashboardHomePageState extends State<UserDashboardHomePage> {
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
final theme = Theme.of(context);
|
|
final size = MediaQuery.of(context).size;
|
|
final isDesktop = size.width > 900;
|
|
final isMobile = size.width < 600;
|
|
final double horizontalPadding = isMobile ? 8.0 : 16.0;
|
|
|
|
return Scaffold(
|
|
backgroundColor: Colors.transparent,
|
|
body: SafeArea(
|
|
child: SingleChildScrollView(
|
|
padding: EdgeInsets.all(horizontalPadding),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
Builder(builder: (context) {
|
|
// Récupérer l'opération actuelle
|
|
final operation = userRepository.getCurrentOperation();
|
|
if (operation != null) {
|
|
return Text(
|
|
operation.name,
|
|
style: TextStyle(
|
|
fontSize: AppTheme.r(context, 20),
|
|
fontWeight: FontWeight.bold,
|
|
color: theme.colorScheme.primary,
|
|
),
|
|
);
|
|
} else {
|
|
return Text(
|
|
'Tableau de bord',
|
|
style: TextStyle(
|
|
fontSize: AppTheme.r(context, 20),
|
|
fontWeight: FontWeight.bold,
|
|
color: theme.colorScheme.primary,
|
|
),
|
|
);
|
|
}
|
|
}),
|
|
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) {
|
|
return PaymentSummaryCard(
|
|
title: 'Règlements',
|
|
titleColor: AppTheme.accentColor,
|
|
titleIcon: Icons.euro,
|
|
height: 300,
|
|
useValueListenable: true,
|
|
userId: userRepository.getCurrentUser()?.id,
|
|
showAllPayments: false,
|
|
isDesktop: isDesktop,
|
|
backgroundIcon: Icons.euro_symbol,
|
|
backgroundIconColor: Colors.blue,
|
|
backgroundIconOpacity: 0.07,
|
|
backgroundIconSize: 180,
|
|
customTotalDisplay: (totalAmount) {
|
|
return '${totalAmount.toStringAsFixed(2)} €';
|
|
},
|
|
);
|
|
}
|
|
|
|
// Construction d'une carte combinée pour les passages (liste + graphique)
|
|
Widget _buildCombinedPassagesCard(BuildContext context, bool isDesktop) {
|
|
return PassageSummaryCard(
|
|
title: 'Passages',
|
|
titleColor: AppTheme.primaryColor,
|
|
titleIcon: Icons.route,
|
|
height: 300,
|
|
useValueListenable: true,
|
|
userId: userRepository.getCurrentUser()?.id,
|
|
showAllPassages: false,
|
|
excludePassageTypes: const [2], // Exclure "À finaliser"
|
|
isDesktop: isDesktop,
|
|
);
|
|
}
|
|
|
|
// Construction du graphique des passages
|
|
Widget _buildPassagesChart(BuildContext context, ThemeData theme) {
|
|
return Card(
|
|
elevation: 4,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(16),
|
|
),
|
|
child: Padding(
|
|
padding: const EdgeInsets.fromLTRB(16.0, 12.0, 16.0, 8.0),
|
|
child: Column(
|
|
crossAxisAlignment: CrossAxisAlignment.start,
|
|
children: [
|
|
SizedBox(
|
|
height: 350,
|
|
child: ActivityChart(
|
|
useValueListenable: true, // Utiliser le système réactif
|
|
excludePassageTypes: const [
|
|
2
|
|
], // Exclure les passages "À finaliser"
|
|
daysToShow: 15,
|
|
periodType: 'Jour',
|
|
height: 350,
|
|
userId: userRepository.getCurrentUser()?.id,
|
|
title: 'Dernière activité enregistrée sur 15 jours',
|
|
),
|
|
),
|
|
],
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
// Construction de la liste des derniers passages
|
|
Widget _buildRecentPassages(BuildContext context, ThemeData theme) {
|
|
// Utilisation directe du widget PassagesListWidget
|
|
return ValueListenableBuilder(
|
|
valueListenable:
|
|
Hive.box<PassageModel>(AppKeys.passagesBoxName).listenable(),
|
|
builder: (context, Box<PassageModel> passagesBox, child) {
|
|
final recentPassages = _getRecentPassages(passagesBox);
|
|
|
|
// Debug : afficher le nombre de passages récupérés
|
|
debugPrint(
|
|
'UserDashboardHomePage: ${recentPassages.length} passages récents récupérés');
|
|
|
|
if (recentPassages.isEmpty) {
|
|
return Card(
|
|
elevation: 4,
|
|
shape: RoundedRectangleBorder(
|
|
borderRadius: BorderRadius.circular(16),
|
|
),
|
|
child: const Padding(
|
|
padding: EdgeInsets.all(32.0),
|
|
child: Center(
|
|
child: Text(
|
|
'Aucun passage récent',
|
|
style: TextStyle(
|
|
color: Colors.grey,
|
|
fontSize: 14,
|
|
),
|
|
),
|
|
),
|
|
),
|
|
);
|
|
}
|
|
|
|
// Utiliser PassagesListWidget sans hauteur fixe - laisse le widget gérer sa propre taille
|
|
return PassagesListWidget(
|
|
passages: recentPassages,
|
|
showFilters: false,
|
|
showSearch: false,
|
|
showActions: true,
|
|
maxPassages: 20,
|
|
showAddButton: false,
|
|
sortBy: 'date',
|
|
);
|
|
},
|
|
);
|
|
}
|
|
|
|
/// Récupère les passages récents pour la liste
|
|
List<Map<String, dynamic>> _getRecentPassages(Box<PassageModel> passagesBox) {
|
|
final currentUserId = userRepository.getCurrentUser()?.id;
|
|
|
|
// Filtrer les passages :
|
|
// - Avoir une date passedAt
|
|
// - Exclure le type 2 ("À finaliser")
|
|
// - Appartenir à l'utilisateur courant
|
|
final allPassages = passagesBox.values.where((p) {
|
|
if (p.passedAt == null) return false;
|
|
if (p.fkType == 2) return false; // Exclure les passages "À finaliser"
|
|
if (currentUserId != null && p.fkUser != currentUserId) {
|
|
return false; // Filtrer par utilisateur
|
|
}
|
|
return true;
|
|
}).toList();
|
|
|
|
// Trier par date décroissante
|
|
allPassages.sort((a, b) => b.passedAt!.compareTo(a.passedAt!));
|
|
|
|
// Limiter aux 20 passages les plus récents
|
|
final recentPassagesModels = allPassages.take(20).toList();
|
|
|
|
// Convertir les modèles de passage au format attendu par le widget PassagesListWidget
|
|
return 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
|
|
double amount = 0.0;
|
|
try {
|
|
if (passage.montant.isNotEmpty) {
|
|
// Gérer les formats possibles (virgule ou point)
|
|
String montantStr = passage.montant.replaceAll(',', '.');
|
|
amount = double.tryParse(montantStr) ?? 0.0;
|
|
}
|
|
} catch (e) {
|
|
debugPrint('Erreur de conversion du montant: ${passage.montant}');
|
|
amount = 0.0;
|
|
}
|
|
|
|
return {
|
|
'id': passage.id, // Garder l'ID comme int, pas besoin de toString()
|
|
'address': address,
|
|
'amount': amount,
|
|
'date': passage.passedAt ?? DateTime.now(),
|
|
'type': passage.fkType,
|
|
'payment': passage.fkTypeReglement,
|
|
'name': passage.name,
|
|
'notes': passage.remarque,
|
|
'hasReceipt': passage.nomRecu.isNotEmpty,
|
|
'hasError': passage.emailErreur.isNotEmpty,
|
|
'fkUser': passage.fkUser,
|
|
'isOwnedByCurrentUser': passage.fkUser ==
|
|
userRepository
|
|
.getCurrentUser()
|
|
?.id, // Ajout du champ pour le widget
|
|
};
|
|
}).toList();
|
|
}
|
|
}
|