feat: Version 3.5.2 - Configuration Stripe et gestion des immeubles

- Configuration complète Stripe pour les 3 environnements (DEV/REC/PROD)
  * DEV: Clés TEST Pierre (mode test)
  * REC: Clés TEST Client (mode test)
  * PROD: Clés LIVE Client (mode live)
- Ajout de la gestion des bases de données immeubles/bâtiments
  * Configuration buildings_database pour DEV/REC/PROD
  * Service BuildingService pour enrichissement des adresses
- Optimisations pages et améliorations ergonomie
- Mises à jour des dépendances Composer
- Nettoyage des fichiers obsolètes

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

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
pierre
2025-11-09 18:26:27 +01:00
parent 21657a3820
commit 2f5946a184
812 changed files with 142105 additions and 25992 deletions

View File

@@ -12,6 +12,7 @@ import 'package:image_picker/image_picker.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'dart:io';
import 'package:geosector_app/presentation/widgets/loading_spin_overlay.dart';
import 'package:geosector_app/presentation/widgets/result_dialog.dart';
import 'custom_text_field.dart';
class AmicaleForm extends StatefulWidget {
@@ -196,6 +197,7 @@ class _AmicaleFormState extends State<AmicaleForm> {
// Afficher le loading
if (!context.mounted) return;
showDialog(
// ignore: use_build_context_synchronously
context: context,
barrierDismissible: false,
builder: (context) => const AlertDialog(
@@ -279,24 +281,13 @@ class _AmicaleFormState extends State<AmicaleForm> {
Future<void> _updateAmicale(AmicaleModel amicale) async {
if (!mounted) return;
// Afficher l'overlay de chargement
final overlay = LoadingSpinOverlayUtils.show(
context: context,
message: 'Mise à jour en cours...',
);
try {
// Afficher un indicateur de chargement
showDialog(
context: context,
barrierDismissible: false,
builder: (BuildContext context) {
return const AlertDialog(
content: Column(
mainAxisSize: MainAxisSize.min,
children: [
CircularProgressIndicator(),
SizedBox(height: 16),
Text('Mise à jour en cours...'),
],
),
);
},
);
// Préparer les données pour l'API
final Map<String, dynamic> data = {
@@ -357,10 +348,8 @@ class _AmicaleFormState extends State<AmicaleForm> {
}
}
// Fermer l'indicateur de chargement
if (mounted && Navigator.of(context).canPop()) {
Navigator.of(context).pop();
}
// Masquer le loading
LoadingSpinOverlayUtils.hideSpecific(overlay);
if (!mounted) return;
@@ -370,46 +359,39 @@ class _AmicaleFormState extends State<AmicaleForm> {
widget.onSubmit!(amicale);
}
// Afficher un message de succès
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(widget.apiService != null ? 'Amicale mise à jour avec succès' : 'Modifications enregistrées localement'),
backgroundColor: Colors.green,
),
// Afficher le résultat de succès
await ResultDialog.show(
context: context,
success: true,
message: widget.apiService != null
? 'Amicale mise à jour avec succès'
: 'Modifications enregistrées localement',
);
// Fermer le formulaire après un délai pour que l'utilisateur voie le message
await Future.delayed(const Duration(milliseconds: 500));
// Fermer le formulaire
if (mounted && Navigator.of(context).canPop()) {
Navigator.of(context).pop();
}
} else {
// Afficher un message d'erreur
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(errorMessage ?? 'Erreur lors de la mise à jour'),
backgroundColor: Colors.red,
duration: const Duration(seconds: 4),
),
// Afficher le résultat d'erreur
await ResultDialog.show(
context: context,
success: false,
message: errorMessage ?? 'Erreur lors de la mise à jour',
);
}
} catch (e) {
debugPrint('❌ Erreur générale dans _updateAmicale: $e');
// Fermer l'indicateur de chargement si encore ouvert
if (mounted && Navigator.of(context).canPop()) {
Navigator.of(context).pop();
}
// Masquer le loading
LoadingSpinOverlayUtils.hideSpecific(overlay);
// Afficher un message d'erreur
// Afficher l'erreur
if (mounted) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Erreur inattendue: ${e.toString()}'),
backgroundColor: Colors.red,
duration: const Duration(seconds: 4),
),
await ResultDialog.show(
context: context,
success: false,
message: 'Erreur inattendue: ${e.toString()}',
);
}
}
@@ -527,81 +509,114 @@ class _AmicaleFormState extends State<AmicaleForm> {
void _submitForm() {
debugPrint('🔧 _submitForm appelée');
if (_formKey.currentState!.validate()) {
debugPrint('🔧 Formulaire valide');
// Vérifier qu'au moins un numéro de téléphone est renseigné
if (_phoneController.text.isEmpty && _mobileController.text.isEmpty) {
debugPrint('⚠️ Aucun numéro de téléphone renseigné');
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Veuillez renseigner au moins un numéro de téléphone'),
backgroundColor: Colors.red,
if (!_formKey.currentState!.validate()) {
// Afficher une dialog si la validation échoue
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Row(
children: [
Icon(Icons.warning_amber_rounded, color: Colors.orange),
SizedBox(width: 8),
Text('Formulaire incomplet'),
],
),
);
return;
}
debugPrint('🔧 Création de l\'objet AmicaleModel...');
final amicale = widget.amicale?.copyWith(
name: _nameController.text,
adresse1: _adresse1Controller.text,
adresse2: _adresse2Controller.text,
codePostal: _codePostalController.text,
ville: _villeController.text,
fkRegion: _fkRegion,
libRegion: _libRegion,
phone: _phoneController.text,
mobile: _mobileController.text,
email: _emailController.text,
gpsLat: _gpsLatController.text,
gpsLng: _gpsLngController.text,
stripeId: _stripeIdController.text,
chkDemo: _chkDemo,
chkCopieMailRecu: _chkCopieMailRecu,
chkAcceptSms: _chkAcceptSms,
chkActive: _chkActive,
chkStripe: _chkStripe,
chkMdpManuel: _chkMdpManuel,
chkUsernameManuel: _chkUsernameManuel,
chkUserDeletePass: _chkUserDeletePass,
chkLotActif: _chkLotActif,
) ??
AmicaleModel(
id: 0, // Sera remplacé par l'API
name: _nameController.text,
adresse1: _adresse1Controller.text,
adresse2: _adresse2Controller.text,
codePostal: _codePostalController.text,
ville: _villeController.text,
fkRegion: _fkRegion,
libRegion: _libRegion,
phone: _phoneController.text,
mobile: _mobileController.text,
email: _emailController.text,
gpsLat: _gpsLatController.text,
gpsLng: _gpsLngController.text,
stripeId: _stripeIdController.text,
chkDemo: _chkDemo,
chkCopieMailRecu: _chkCopieMailRecu,
chkAcceptSms: _chkAcceptSms,
chkActive: _chkActive,
chkStripe: _chkStripe,
chkMdpManuel: _chkMdpManuel,
chkUsernameManuel: _chkUsernameManuel,
chkUserDeletePass: _chkUserDeletePass,
chkLotActif: _chkLotActif,
);
debugPrint('🔧 AmicaleModel créé: ${amicale.name}');
debugPrint('🔧 Appel de _updateAmicale...');
// Appeler l'API pour mettre à jour l'amicale
_updateAmicale(amicale);
} else {
debugPrint('❌ Formulaire invalide');
content: const Text('Veuillez vérifier tous les champs marqués en rouge avant d\'enregistrer'),
actions: [
ElevatedButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('OK'),
),
],
),
);
return;
}
debugPrint('🔧 Formulaire valide');
// Vérifier qu'au moins un numéro de téléphone est renseigné
if (_phoneController.text.isEmpty && _mobileController.text.isEmpty) {
debugPrint('⚠️ Aucun numéro de téléphone renseigné');
showDialog(
context: context,
builder: (context) => AlertDialog(
title: const Row(
children: [
Icon(Icons.warning_amber_rounded, color: Colors.orange),
SizedBox(width: 8),
Text('Formulaire incomplet'),
],
),
content: const Text('Veuillez renseigner au moins un numéro de téléphone'),
actions: [
ElevatedButton(
onPressed: () => Navigator.of(context).pop(),
child: const Text('OK'),
),
],
),
);
return;
}
debugPrint('🔧 Création de l\'objet AmicaleModel...');
final amicale = widget.amicale?.copyWith(
name: _nameController.text,
adresse1: _adresse1Controller.text,
adresse2: _adresse2Controller.text,
codePostal: _codePostalController.text,
ville: _villeController.text,
fkRegion: _fkRegion,
libRegion: _libRegion,
phone: _phoneController.text,
mobile: _mobileController.text,
email: _emailController.text,
gpsLat: _gpsLatController.text,
gpsLng: _gpsLngController.text,
stripeId: _stripeIdController.text,
chkDemo: _chkDemo,
chkCopieMailRecu: _chkCopieMailRecu,
chkAcceptSms: _chkAcceptSms,
chkActive: _chkActive,
chkStripe: _chkStripe,
chkMdpManuel: _chkMdpManuel,
chkUsernameManuel: _chkUsernameManuel,
chkUserDeletePass: _chkUserDeletePass,
chkLotActif: _chkLotActif,
) ??
AmicaleModel(
id: 0, // Sera remplacé par l'API
name: _nameController.text,
adresse1: _adresse1Controller.text,
adresse2: _adresse2Controller.text,
codePostal: _codePostalController.text,
ville: _villeController.text,
fkRegion: _fkRegion,
libRegion: _libRegion,
phone: _phoneController.text,
mobile: _mobileController.text,
email: _emailController.text,
gpsLat: _gpsLatController.text,
gpsLng: _gpsLngController.text,
stripeId: _stripeIdController.text,
chkDemo: _chkDemo,
chkCopieMailRecu: _chkCopieMailRecu,
chkAcceptSms: _chkAcceptSms,
chkActive: _chkActive,
chkStripe: _chkStripe,
chkMdpManuel: _chkMdpManuel,
chkUsernameManuel: _chkUsernameManuel,
chkUserDeletePass: _chkUserDeletePass,
chkLotActif: _chkLotActif,
);
debugPrint('🔧 AmicaleModel créé: ${amicale.name}');
debugPrint('🔧 Appel de _updateAmicale...');
// Appeler l'API pour mettre à jour l'amicale
_updateAmicale(amicale);
}
// Construire la section logo
@@ -618,7 +633,7 @@ class _AmicaleFormState extends State<AmicaleForm> {
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.1),
color: Colors.black.withOpacity(0.1),
blurRadius: 4,
offset: const Offset(0, 2),
),
@@ -642,7 +657,7 @@ class _AmicaleFormState extends State<AmicaleForm> {
onTap: _selectImage,
child: Container(
decoration: BoxDecoration(
color: Colors.black.withValues(alpha: 0.3),
color: Colors.black.withOpacity(0.3),
),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
@@ -822,7 +837,7 @@ class _AmicaleFormState extends State<AmicaleForm> {
borderRadius: BorderRadius.circular(8),
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.1),
color: Colors.black.withOpacity(0.1),
blurRadius: 4,
offset: const Offset(0, 2),
),
@@ -1234,10 +1249,10 @@ class _AmicaleFormState extends State<AmicaleForm> {
child: Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: _stripeStatus?.statusColor.withValues(alpha: 0.1) ?? Colors.orange.withValues(alpha: 0.1),
color: _stripeStatus?.statusColor.withOpacity(0.1) ?? Colors.orange.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
border: Border.all(
color: _stripeStatus?.statusColor.withValues(alpha: 0.3) ?? Colors.orange.withValues(alpha: 0.3),
color: _stripeStatus?.statusColor.withOpacity(0.3) ?? Colors.orange.withOpacity(0.3),
),
),
child: Row(