feat: Mise à jour des interfaces mobiles v3.2.3

- Amélioration des interfaces utilisateur sur mobile
- Optimisation de la responsivité des composants Flutter
- Mise à jour des widgets de chat et communication
- Amélioration des formulaires et tableaux
- Ajout de nouveaux composants pour l'administration
- Optimisation des thèmes et styles visuels

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-09-02 20:35:40 +02:00
parent 08f4bff358
commit 43d4cd66e1
2133 changed files with 237004 additions and 173303 deletions

View File

@@ -40,7 +40,8 @@ class _UserDashboardHomePageState extends State<UserDashboardHomePage> {
if (operation != null) {
return Text(
'${operation.name} (${_formatDate(operation.dateDebut)}-${_formatDate(operation.dateFin)})',
style: theme.textTheme.headlineMedium?.copyWith(
style: TextStyle(
fontSize: AppTheme.r(context, 20),
fontWeight: FontWeight.bold,
color: theme.colorScheme.primary,
),
@@ -48,7 +49,8 @@ class _UserDashboardHomePageState extends State<UserDashboardHomePage> {
} else {
return Text(
'Tableau de bord',
style: theme.textTheme.headlineMedium?.copyWith(
style: TextStyle(
fontSize: AppTheme.r(context, 20),
fontWeight: FontWeight.bold,
color: theme.colorScheme.primary,
),
@@ -88,46 +90,45 @@ class _UserDashboardHomePageState extends State<UserDashboardHomePage> {
}
// Construction d'une carte combinée pour les règlements (liste + graphique)
Widget _buildCombinedPaymentsCard(bool isDesktop) {
return PaymentSummaryCard(
title: 'Mes règlements',
titleColor: AppTheme.accentColor,
titleIcon: Icons.payments,
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) {
// Calculer le nombre de passages avec règlement pour le titre personnalisé
final currentUser = userRepository.getCurrentUser();
if (currentUser == null) return '${totalAmount.toStringAsFixed(2)}';
final passagesBox = Hive.box<PassageModel>(AppKeys.passagesBoxName);
int passagesCount = 0;
for (final passage in passagesBox.values) {
if (passage.fkUser == currentUser.id) {
double montant = 0.0;
try {
String montantStr = passage.montant.replaceAll(',', '.');
montant = double.tryParse(montantStr) ?? 0.0;
} catch (e) {
// Ignorer les erreurs
}
if (montant > 0) passagesCount++;
}
}
return '${totalAmount.toStringAsFixed(2)} € sur $passagesCount passages';
},
);
}
Widget _buildCombinedPaymentsCard(bool isDesktop) {
return PaymentSummaryCard(
title: 'Mes règlements',
titleColor: AppTheme.accentColor,
titleIcon: Icons.payments,
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) {
// Calculer le nombre de passages avec règlement pour le titre personnalisé
final currentUser = userRepository.getCurrentUser();
if (currentUser == null) return '${totalAmount.toStringAsFixed(2)}';
final passagesBox = Hive.box<PassageModel>(AppKeys.passagesBoxName);
int passagesCount = 0;
for (final passage in passagesBox.values) {
if (passage.fkUser == currentUser.id) {
double montant = 0.0;
try {
String montantStr = passage.montant.replaceAll(',', '.');
montant = double.tryParse(montantStr) ?? 0.0;
} catch (e) {
// Ignorer les erreurs
}
if (montant > 0) passagesCount++;
}
}
return '${totalAmount.toStringAsFixed(2)} € sur $passagesCount passages';
},
);
}
// Construction d'une carte combinée pour les passages (liste + graphique)
Widget _buildCombinedPassagesCard(BuildContext context, bool isDesktop) {
@@ -136,8 +137,8 @@ Widget _buildCombinedPaymentsCard(bool isDesktop) {
titleColor: AppTheme.primaryColor,
titleIcon: Icons.route,
height: 300,
useValueListenable: true,
userId: userRepository.getCurrentUser()?.id,
useValueListenable: true,
userId: userRepository.getCurrentUser()?.id,
showAllPassages: false,
excludePassageTypes: const [2], // Exclure "À finaliser"
isDesktop: isDesktop,
@@ -160,7 +161,9 @@ Widget _buildCombinedPaymentsCard(bool isDesktop) {
height: 350,
child: ActivityChart(
useValueListenable: true, // Utiliser le système réactif
excludePassageTypes: const [2], // Exclure les passages "À finaliser"
excludePassageTypes: const [
2
], // Exclure les passages "À finaliser"
daysToShow: 15,
periodType: 'Jour',
height: 350,
@@ -178,27 +181,29 @@ Widget _buildCombinedPaymentsCard(bool isDesktop) {
Widget _buildRecentPassages(BuildContext context, ThemeData theme) {
// Utilisation directe du widget PassagesListWidget sans Card wrapper
return ValueListenableBuilder(
valueListenable: Hive.box<PassageModel>(AppKeys.passagesBoxName).listenable(),
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');
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(
child: Padding(
padding: EdgeInsets.all(32.0),
child: Center(
child: Text(
'Aucun passage récent',
style: TextStyle(
color: Colors.grey,
fontSize: 14,
fontSize: AppTheme.r(context, 14),
),
),
),
@@ -208,7 +213,8 @@ Widget _buildCombinedPaymentsCard(bool isDesktop) {
// Utiliser une hauteur fixe pour le widget dans le dashboard
return SizedBox(
height: 450, // Hauteur légèrement augmentée pour compenser l'absence de Card
height:
450, // Hauteur légèrement augmentée pour compenser l'absence de Card
child: PassagesListWidget(
passages: recentPassages,
showFilters: false,
@@ -217,8 +223,10 @@ Widget _buildCombinedPaymentsCard(bool isDesktop) {
maxPassages: 20,
// Ne pas appliquer de filtres supplémentaires car les passages
// sont déjà filtrés dans _getRecentPassages
excludePassageTypes: null, // Pas de filtre, déjà géré dans _getRecentPassages
filterByUserId: null, // Pas de filtre, déjà géré dans _getRecentPassages
excludePassageTypes:
null, // Pas de filtre, déjà géré dans _getRecentPassages
filterByUserId:
null, // Pas de filtre, déjà géré dans _getRecentPassages
periodFilter: null, // Pas de filtre de période
// Le widget gère maintenant le flux conditionnel par défaut
onPassageSelected: null,
@@ -245,18 +253,19 @@ Widget _buildCombinedPaymentsCard(bool isDesktop) {
/// 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 :
// 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
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!));
@@ -294,7 +303,10 @@ Widget _buildCombinedPaymentsCard(bool isDesktop) {
'hasReceipt': passage.nomRecu.isNotEmpty,
'hasError': passage.emailErreur.isNotEmpty,
'fkUser': passage.fkUser,
'isOwnedByCurrentUser': passage.fkUser == userRepository.getCurrentUser()?.id, // Ajout du champ pour le widget
'isOwnedByCurrentUser': passage.fkUser ==
userRepository
.getCurrentUser()
?.id, // Ajout du champ pour le widget
};
}).toList();
}