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 2786252307
commit 570a1fa1f0
212 changed files with 24275 additions and 11321 deletions

View File

@@ -86,9 +86,7 @@ class _UserFieldModePageState extends State<UserFieldModePage>
// Demander la permission et obtenir la position
final position = await Geolocator.getCurrentPosition(
locationSettings: const LocationSettings(
accuracy: LocationAccuracy.high,
),
desiredAccuracy: LocationAccuracy.high,
);
setState(() {
@@ -232,12 +230,9 @@ class _UserFieldModePageState extends State<UserFieldModePage>
_qualityUpdateTimer =
Timer.periodic(const Duration(seconds: 5), (timer) async {
// Vérifier la connexion réseau
final connectivityResults = await Connectivity().checkConnectivity();
final connectivityResult = await Connectivity().checkConnectivity();
setState(() {
// Prendre le premier résultat de la liste
_connectivityResult = connectivityResults.isNotEmpty
? connectivityResults.first
: ConnectivityResult.none;
_connectivityResult = connectivityResult;
});
// Vérifier si le GPS est activé
@@ -274,7 +269,7 @@ class _UserFieldModePageState extends State<UserFieldModePage>
if (_currentPosition == null) return;
final passagesBox = Hive.box<PassageModel>(AppKeys.passagesBoxName);
final allPassages = passagesBox.values.where((p) => p.fkType == 2).toList();
final allPassages = passagesBox.values.toList(); // Tous les types de passages
// Calculer les distances et trier
final passagesWithDistance = allPassages.map((passage) {
@@ -295,8 +290,7 @@ class _UserFieldModePageState extends State<UserFieldModePage>
setState(() {
_nearbyPassages = passagesWithDistance
.take(50) // Limiter à 50 passages
.where((entry) => entry.value <= 2000) // Max 2km
.where((entry) => entry.value <= 500) // Max 500m
.map((entry) => entry.key)
.toList();
});
@@ -339,7 +333,7 @@ class _UserFieldModePageState extends State<UserFieldModePage>
void _startCompass() {
_magnetometerSubscription =
magnetometerEventStream().listen((MagnetometerEvent event) {
magnetometerEvents.listen((MagnetometerEvent event) {
setState(() {
// Calculer l'orientation à partir du magnétomètre
_heading = math.atan2(event.y, event.x) * (180 / math.pi);
@@ -375,6 +369,7 @@ class _UserFieldModePageState extends State<UserFieldModePage>
passageRepository: passageRepository,
userRepository: userRepository,
operationRepository: operationRepository,
amicaleRepository: amicaleRepository,
onSuccess: () {
// Rafraîchir les passages après modification
_updateNearbyPassages();
@@ -985,22 +980,43 @@ class _UserFieldModePageState extends State<UserFieldModePage>
);
}
// Assombrir une couleur pour les bordures
Color _darkenColor(Color color, [double amount = 0.3]) {
assert(amount >= 0 && amount <= 1);
final hsl = HSLColor.fromColor(color);
final hslDark = hsl.withLightness((hsl.lightness - amount).clamp(0.0, 1.0));
return hslDark.toColor();
}
List<Marker> _buildPassageMarkers() {
if (_currentPosition == null) return [];
return _nearbyPassages.map((passage) {
// Déterminer la couleur selon nbPassages
Color fillColor;
if (passage.nbPassages == 0) {
fillColor = const Color(0xFFFFFFFF); // couleur1: Blanc
} else if (passage.nbPassages == 1) {
fillColor = const Color(0xFFF7A278); // couleur2: Orange
} else {
fillColor = const Color(0xFFE65100); // couleur3: Orange foncé
// Déterminer la couleur selon le type de passage
Color fillColor = Colors.grey; // Couleur par défaut
if (AppKeys.typesPassages.containsKey(passage.fkType)) {
final typeInfo = AppKeys.typesPassages[passage.fkType]!;
if (passage.fkType == 2) {
// Type 2 (À finaliser) : adapter la couleur selon nbPassages
if (passage.nbPassages == 0) {
fillColor = Color(typeInfo['couleur1'] as int);
} else if (passage.nbPassages == 1) {
fillColor = Color(typeInfo['couleur2'] as int);
} else {
fillColor = Color(typeInfo['couleur3'] as int);
}
} else {
// Autres types : utiliser couleur2 par défaut
fillColor = Color(typeInfo['couleur2'] as int);
}
}
// Bordure toujours orange (couleur2)
const borderColor = Color(0xFFF7A278);
// Bordure : version assombrie de la couleur de remplissage
final borderColor = _darkenColor(fillColor, 0.3);
// Convertir les coordonnées GPS string en double
final double lat = double.tryParse(passage.gpsLat) ?? 0;
@@ -1029,8 +1045,10 @@ class _UserFieldModePageState extends State<UserFieldModePage>
child: Text(
'${passage.numero}${(passage.rueBis.isNotEmpty) ? passage.rueBis.substring(0, 1).toLowerCase() : ''}',
style: TextStyle(
color:
fillColor == Colors.white ? Colors.black : Colors.white,
// Texte noir sur fond clair, blanc sur fond foncé
color: fillColor.computeLuminance() > 0.5
? Colors.black
: Colors.white,
fontWeight: FontWeight.bold,
fontSize: AppTheme.r(context, 12),
),
@@ -1120,14 +1138,18 @@ class _UserFieldModePageState extends State<UserFieldModePage>
color: Colors.white,
child: PassagesListWidget(
passages: filteredPassages,
showFilters: false, // Pas de filtres, juste la liste
showSearch: false, // La recherche est déjà dans l'interface
showActions: true,
sortBy: 'distance', // Tri par distance pour le mode terrain
excludePassageTypes: const [], // Afficher tous les types (notamment le type 2)
showAddButton: true, // Activer le bouton de création
// Le widget gère maintenant le flux conditionnel par défaut
onPassageSelected: null,
onPassageEdit: (passage) {
// Retrouver le PassageModel original pour l'édition
final passageId = passage['id'] as int;
final originalPassage = _nearbyPassages.firstWhere(
(p) => p.id == passageId,
orElse: () => _nearbyPassages.first,
);
_openPassageForm(originalPassage);
},
onAddPassage: () async {
// Ouvrir le dialogue de création de passage
await showDialog(
@@ -1139,6 +1161,7 @@ class _UserFieldModePageState extends State<UserFieldModePage>
passageRepository: passageRepository,
userRepository: userRepository,
operationRepository: operationRepository,
amicaleRepository: amicaleRepository,
onSuccess: () {
// Le widget se rafraîchira automatiquement via ValueListenableBuilder
},