feat: Gestion des secteurs et migration v3.0.4+304

- Ajout système complet de gestion des secteurs avec contours géographiques
- Import des contours départementaux depuis GeoJSON
- API REST pour la gestion des secteurs (/api/sectors)
- Service de géolocalisation pour déterminer les secteurs
- Migration base de données avec tables x_departements_contours et sectors_adresses
- Interface Flutter pour visualisation et gestion des secteurs
- Ajout thème sombre dans l'application
- Corrections diverses et optimisations

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
pierre
2025-08-07 11:01:45 +02:00
parent 6a609fb467
commit 599b9fcda0
662 changed files with 213221 additions and 174243 deletions

View File

@@ -86,7 +86,8 @@ class PaymentSummaryCard extends StatelessWidget {
child: Icon(
backgroundIcon,
size: backgroundIconSize,
color: (backgroundIconColor ?? Colors.blue).withOpacity(backgroundIconOpacity),
color: (backgroundIconColor ?? Colors.blue)
.withOpacity(backgroundIconOpacity),
),
),
),
@@ -115,7 +116,7 @@ class PaymentSummaryCard extends StatelessWidget {
? _buildPaymentsListWithValueListenable()
: _buildPaymentsListWithStaticData(),
),
// Séparateur vertical
if (isDesktop) const VerticalDivider(width: 24),
@@ -126,7 +127,10 @@ class PaymentSummaryCard extends StatelessWidget {
padding: const EdgeInsets.all(8.0),
child: PaymentPieChart(
useValueListenable: useValueListenable,
payments: useValueListenable ? [] : _convertMapToPaymentData(paymentsByType ?? {}),
payments: useValueListenable
? []
: _convertMapToPaymentData(
paymentsByType ?? {}),
userId: showAllPayments ? null : userId,
size: double.infinity,
labelSize: 12,
@@ -157,7 +161,8 @@ class PaymentSummaryCard extends StatelessWidget {
/// Construction du titre avec ValueListenableBuilder
Widget _buildTitleWithValueListenable() {
return ValueListenableBuilder(
valueListenable: Hive.box<PassageModel>(AppKeys.passagesBoxName).listenable(),
valueListenable:
Hive.box<PassageModel>(AppKeys.passagesBoxName).listenable(),
builder: (context, Box<PassageModel> passagesBox, child) {
final paymentStats = _calculatePaymentStats(passagesBox);
@@ -181,8 +186,8 @@ class PaymentSummaryCard extends StatelessWidget {
),
),
Text(
customTotalDisplay?.call(paymentStats['totalAmount']) ??
'${paymentStats['totalAmount'].toStringAsFixed(2)}',
customTotalDisplay?.call(paymentStats['totalAmount']) ??
'${paymentStats['totalAmount'].toStringAsFixed(2)}',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
@@ -197,8 +202,9 @@ class PaymentSummaryCard extends StatelessWidget {
/// Construction du titre avec données statiques
Widget _buildTitleWithStaticData() {
final totalAmount = paymentsByType?.values.fold(0.0, (sum, amount) => sum + amount) ?? 0.0;
final totalAmount =
paymentsByType?.values.fold(0.0, (sum, amount) => sum + amount) ?? 0.0;
return Row(
children: [
if (titleIcon != null) ...[
@@ -219,7 +225,8 @@ class PaymentSummaryCard extends StatelessWidget {
),
),
Text(
customTotalDisplay?.call(totalAmount) ?? '${totalAmount.toStringAsFixed(2)}',
customTotalDisplay?.call(totalAmount) ??
'${totalAmount.toStringAsFixed(2)}',
style: TextStyle(
fontSize: 20,
fontWeight: FontWeight.bold,
@@ -233,7 +240,8 @@ class PaymentSummaryCard extends StatelessWidget {
/// Construction de la liste des règlements avec ValueListenableBuilder
Widget _buildPaymentsListWithValueListenable() {
return ValueListenableBuilder(
valueListenable: Hive.box<PassageModel>(AppKeys.passagesBoxName).listenable(),
valueListenable:
Hive.box<PassageModel>(AppKeys.passagesBoxName).listenable(),
builder: (context, Box<PassageModel> passagesBox, child) {
final paymentAmounts = _calculatePaymentAmounts(passagesBox);
@@ -294,7 +302,7 @@ class PaymentSummaryCard extends StatelessWidget {
],
),
);
}).toList(),
}),
],
);
}
@@ -330,7 +338,7 @@ class PaymentSummaryCard extends StatelessWidget {
// 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};
}
@@ -388,7 +396,8 @@ class PaymentSummaryCard extends StatelessWidget {
if (montant > 0) {
if (paymentAmounts.containsKey(typeReglement)) {
paymentAmounts[typeReglement] = (paymentAmounts[typeReglement] ?? 0.0) + montant;
paymentAmounts[typeReglement] =
(paymentAmounts[typeReglement] ?? 0.0) + montant;
} else {
paymentAmounts[0] = (paymentAmounts[0] ?? 0.0) + montant;
}
@@ -398,7 +407,7 @@ class PaymentSummaryCard extends StatelessWidget {
// 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) {
@@ -415,7 +424,8 @@ class PaymentSummaryCard extends StatelessWidget {
if (montant > 0) {
if (paymentAmounts.containsKey(typeReglement)) {
paymentAmounts[typeReglement] = (paymentAmounts[typeReglement] ?? 0.0) + montant;
paymentAmounts[typeReglement] =
(paymentAmounts[typeReglement] ?? 0.0) + montant;
} else {
paymentAmounts[0] = (paymentAmounts[0] ?? 0.0) + montant;
}
@@ -433,10 +443,11 @@ class PaymentSummaryCard extends StatelessWidget {
final List<PaymentData> paymentDataList = [];
paymentsMap.forEach((typeReglement, montant) {
if (montant > 0) { // Ne retourner que les types avec un montant > 0
if (montant > 0) {
// Ne retourner que les types avec un montant > 0
// Récupérer les informations depuis AppKeys.typesReglements
final reglementInfo = AppKeys.typesReglements[typeReglement];
if (reglementInfo != null) {
paymentDataList.add(PaymentData(
typeId: typeReglement,
@@ -457,7 +468,7 @@ class PaymentSummaryCard extends StatelessWidget {
}
}
});
return paymentDataList;
}
}
}