feat: Version 3.3.4 - Nouvelle architecture pages, optimisations widgets Flutter et API

- Mise à jour VERSION vers 3.3.4
- Optimisations et révisions architecture API (deploy-api.sh, scripts de migration)
- Ajout documentation Stripe Tap to Pay complète
- Migration vers polices Inter Variable pour Flutter
- Optimisations build Android et nettoyage fichiers temporaires
- Amélioration système de déploiement avec gestion backups
- Ajout scripts CRON et migrations base de données

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
pierre
2025-10-05 20:11:15 +02:00
parent 242a90720e
commit b6584c83fa
1625 changed files with 145669 additions and 51249 deletions

View File

@@ -72,6 +72,39 @@ class PaymentSummaryCard extends StatelessWidget {
@override
Widget build(BuildContext context) {
// Si useValueListenable, construire avec ValueListenableBuilder centralisé
if (useValueListenable) {
return ValueListenableBuilder(
valueListenable: Hive.box<PassageModel>(AppKeys.passagesBoxName).listenable(),
builder: (context, Box<PassageModel> passagesBox, child) {
// Calculer les données une seule fois
final paymentAmounts = _calculatePaymentAmounts(passagesBox);
final totalAmount = paymentAmounts.values.fold(0.0, (sum, amount) => sum + amount);
return _buildCardContent(
context,
totalAmount: totalAmount,
paymentAmounts: paymentAmounts,
);
},
);
} else {
// Données statiques
final totalAmount = paymentsByType?.values.fold(0.0, (sum, amount) => sum + amount) ?? 0.0;
return _buildCardContent(
context,
totalAmount: totalAmount,
paymentAmounts: paymentsByType ?? {},
);
}
}
/// Construit le contenu de la card avec les données calculées
Widget _buildCardContent(
BuildContext context, {
required double totalAmount,
required Map<int, double> paymentAmounts,
}) {
return Card(
elevation: 4,
shape: RoundedRectangleBorder(
@@ -99,9 +132,7 @@ class PaymentSummaryCard extends StatelessWidget {
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Titre avec comptage
useValueListenable
? _buildTitleWithValueListenable()
: _buildTitleWithStaticData(context),
_buildTitle(context, totalAmount),
const Divider(height: 24),
// Contenu principal
Expanded(
@@ -112,9 +143,7 @@ class PaymentSummaryCard extends StatelessWidget {
// Liste des règlements à gauche
Expanded(
flex: isDesktop ? 1 : 2,
child: useValueListenable
? _buildPaymentsListWithValueListenable()
: _buildPaymentsListWithStaticData(context),
child: _buildPaymentsList(context, paymentAmounts),
),
// Séparateur vertical
@@ -126,11 +155,9 @@ class PaymentSummaryCard extends StatelessWidget {
child: Padding(
padding: const EdgeInsets.all(8.0),
child: PaymentPieChart(
useValueListenable: useValueListenable,
payments: useValueListenable
? []
: _convertMapToPaymentData(
paymentsByType ?? {}),
useValueListenable: false, // Utilise les données calculées
payments: _convertMapToPaymentData(paymentAmounts),
showAllPassages: showAllPayments,
userId: showAllPayments ? null : userId,
size: double.infinity,
labelSize: 12,
@@ -158,53 +185,8 @@ class PaymentSummaryCard extends StatelessWidget {
);
}
/// Construction du titre avec ValueListenableBuilder
Widget _buildTitleWithValueListenable() {
return ValueListenableBuilder(
valueListenable:
Hive.box<PassageModel>(AppKeys.passagesBoxName).listenable(),
builder: (context, Box<PassageModel> passagesBox, child) {
final paymentStats = _calculatePaymentStats(passagesBox);
return Row(
children: [
if (titleIcon != null) ...[
Icon(
titleIcon,
color: titleColor,
size: 24,
),
const SizedBox(width: 8),
],
Expanded(
child: Text(
title,
style: TextStyle(
fontSize: AppTheme.r(context, 16),
fontWeight: FontWeight.bold,
),
),
),
Text(
customTotalDisplay?.call(paymentStats['totalAmount']) ??
'${paymentStats['totalAmount'].toStringAsFixed(2)}',
style: TextStyle(
fontSize: AppTheme.r(context, 20),
fontWeight: FontWeight.bold,
color: titleColor,
),
),
],
);
},
);
}
/// Construction du titre avec données statiques
Widget _buildTitleWithStaticData(BuildContext context) {
final totalAmount =
paymentsByType?.values.fold(0.0, (sum, amount) => sum + amount) ?? 0.0;
/// Construction du titre
Widget _buildTitle(BuildContext context, double totalAmount) {
return Row(
children: [
if (titleIcon != null) ...[
@@ -237,24 +219,6 @@ class PaymentSummaryCard extends StatelessWidget {
);
}
/// Construction de la liste des règlements avec ValueListenableBuilder
Widget _buildPaymentsListWithValueListenable() {
return ValueListenableBuilder(
valueListenable:
Hive.box<PassageModel>(AppKeys.passagesBoxName).listenable(),
builder: (context, Box<PassageModel> passagesBox, child) {
final paymentAmounts = _calculatePaymentAmounts(passagesBox);
return _buildPaymentsList(context, paymentAmounts);
},
);
}
/// Construction de la liste des règlements avec données statiques
Widget _buildPaymentsListWithStaticData(BuildContext context) {
return _buildPaymentsList(context, paymentsByType ?? {});
}
/// Construction de la liste des règlements
Widget _buildPaymentsList(BuildContext context, Map<int, double> paymentAmounts) {
return Column(
@@ -307,70 +271,6 @@ class PaymentSummaryCard extends StatelessWidget {
);
}
/// Calcule les statistiques de règlement
Map<String, dynamic> _calculatePaymentStats(Box<PassageModel> passagesBox) {
if (showAllPayments) {
// Pour les administrateurs : tous les règlements
int passagesWithPaymentCount = 0;
double totalAmount = 0.0;
for (final passage in passagesBox.values) {
// Convertir la chaîne de montant en double
double montant = 0.0;
try {
String montantStr = passage.montant.replaceAll(',', '.');
montant = double.tryParse(montantStr) ?? 0.0;
} catch (e) {
// Ignorer les erreurs de conversion
}
if (montant > 0) {
passagesWithPaymentCount++;
totalAmount += montant;
}
}
return {
'passagesCount': passagesWithPaymentCount,
'totalAmount': totalAmount,
};
} else {
// Pour les utilisateurs : seulement leurs règlements
final currentUser = userRepository.getCurrentUser();
final targetUserId = userId ?? currentUser?.id;
if (targetUserId == null) {
return {'passagesCount': 0, 'totalAmount': 0.0};
}
int passagesWithPaymentCount = 0;
double totalAmount = 0.0;
for (final passage in passagesBox.values) {
if (passage.fkUser == targetUserId) {
// Convertir la chaîne de montant en double
double montant = 0.0;
try {
String montantStr = passage.montant.replaceAll(',', '.');
montant = double.tryParse(montantStr) ?? 0.0;
} catch (e) {
// Ignorer les erreurs de conversion
}
if (montant > 0) {
passagesWithPaymentCount++;
totalAmount += montant;
}
}
}
return {
'passagesCount': passagesWithPaymentCount,
'totalAmount': totalAmount,
};
}
}
/// Calcule les montants par type de règlement
Map<int, double> _calculatePaymentAmounts(Box<PassageModel> passagesBox) {
final Map<int, double> paymentAmounts = {};
@@ -380,57 +280,33 @@ class PaymentSummaryCard extends StatelessWidget {
paymentAmounts[typeId] = 0.0;
}
if (showAllPayments) {
// Pour les administrateurs : compter tous les règlements
for (final passage in passagesBox.values) {
final int typeReglement = passage.fkTypeReglement;
// En mode user, filtrer uniquement les passages créés par l'utilisateur (fkUser)
final currentUser = userRepository.getCurrentUser();
final int? filterUserId = showAllPayments ? null : currentUser?.id;
// Convertir la chaîne de montant en double
double montant = 0.0;
try {
String montantStr = passage.montant.replaceAll(',', '.');
montant = double.tryParse(montantStr) ?? 0.0;
} catch (e) {
// Ignorer les erreurs de conversion
}
if (montant > 0) {
if (paymentAmounts.containsKey(typeReglement)) {
paymentAmounts[typeReglement] =
(paymentAmounts[typeReglement] ?? 0.0) + montant;
} else {
paymentAmounts[0] = (paymentAmounts[0] ?? 0.0) + montant;
}
}
for (final passage in passagesBox.values) {
// En mode user, ne compter que les passages de l'utilisateur
if (filterUserId != null && passage.fkUser != filterUserId) {
continue;
}
} else {
// Pour les utilisateurs : compter seulement leurs règlements
final currentUser = userRepository.getCurrentUser();
final targetUserId = userId ?? currentUser?.id;
if (targetUserId != null) {
for (final passage in passagesBox.values) {
if (passage.fkUser == targetUserId) {
final int typeReglement = passage.fkTypeReglement;
final int typeReglement = passage.fkTypeReglement;
// Convertir la chaîne de montant en double
double montant = 0.0;
try {
String montantStr = passage.montant.replaceAll(',', '.');
montant = double.tryParse(montantStr) ?? 0.0;
} catch (e) {
// Ignorer les erreurs de conversion
}
// Convertir la chaîne de montant en double
double montant = 0.0;
try {
String montantStr = passage.montant.replaceAll(',', '.');
montant = double.tryParse(montantStr) ?? 0.0;
} catch (e) {
// Ignorer les erreurs de conversion
}
if (montant > 0) {
if (paymentAmounts.containsKey(typeReglement)) {
paymentAmounts[typeReglement] =
(paymentAmounts[typeReglement] ?? 0.0) + montant;
} else {
paymentAmounts[0] = (paymentAmounts[0] ?? 0.0) + montant;
}
}
}
if (montant > 0) {
if (paymentAmounts.containsKey(typeReglement)) {
paymentAmounts[typeReglement] =
(paymentAmounts[typeReglement] ?? 0.0) + montant;
} else {
paymentAmounts[0] = (paymentAmounts[0] ?? 0.0) + montant;
}
}
}