diff --git a/frontend/lib/main.dart b/frontend/lib/main.dart index 7cf07f5..4478ce1 100644 --- a/frontend/lib/main.dart +++ b/frontend/lib/main.dart @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:flutter_localizations/flutter_localizations.dart'; // Import pour la localisation // import 'package:provider/provider.dart'; // Supprimer Provider -import 'navigation/app_router.dart'; +import 'config/app_router.dart'; // <-- Importer le bon routeur (GoRouter) // import 'theme/app_theme.dart'; // Supprimer AppTheme // import 'theme/theme_provider.dart'; // Supprimer ThemeProvider @@ -17,7 +17,7 @@ class MyApp extends StatelessWidget { Widget build(BuildContext context) { // Pas besoin de Provider.of ici - return MaterialApp( + return MaterialApp.router( // <-- Utilisation de MaterialApp.router title: 'P\'titsPas', theme: ThemeData.light().copyWith( // Utiliser un thème simple par défaut textTheme: GoogleFonts.meriendaTextTheme( @@ -35,8 +35,7 @@ class MyApp extends StatelessWidget { // Locale('en', 'US'), // Anglais, si besoin ], locale: const Locale('fr', 'FR'), // Forcer la locale française par défaut - initialRoute: AppRouter.login, - onGenerateRoute: AppRouter.generateRoute, + routerConfig: AppRouter.router, // <-- Passer la configuration du GoRouter debugShowCheckedModeBanner: false, ); } diff --git a/frontend/lib/models/user_registration_data.dart b/frontend/lib/models/user_registration_data.dart index d2c6954..9687898 100644 --- a/frontend/lib/models/user_registration_data.dart +++ b/frontend/lib/models/user_registration_data.dart @@ -1,5 +1,8 @@ import 'dart:io'; // Pour File import '../models/card_assets.dart'; // Import de l'enum CardColorVertical +import 'package:flutter/foundation.dart'; +import 'package:intl/intl.dart'; +// import 'package:p_tits_pas/models/child.dart'; // Commenté car fichier non trouvé class ParentData { String firstName; @@ -47,12 +50,28 @@ class ChildData { }); } -class UserRegistrationData { +// Nouvelle classe pour les détails bancaires +class BankDetails { + String bankName; + String iban; + String bic; + + BankDetails({ + this.bankName = '', + this.iban = '', + this.bic = '', + }); +} + +class UserRegistrationData extends ChangeNotifier { ParentData parent1; ParentData? parent2; // Optionnel List children; String motivationText; bool cguAccepted; + BankDetails? bankDetails; // Ajouté + String attestationCafNumber; // Ajouté + bool consentQuotientFamilial; // Ajouté UserRegistrationData({ ParentData? parent1Data, @@ -60,38 +79,77 @@ class UserRegistrationData { List? childrenData, this.motivationText = '', this.cguAccepted = false, + this.bankDetails, // Ajouté + this.attestationCafNumber = '', // Ajouté + this.consentQuotientFamilial = false, // Ajouté }) : parent1 = parent1Data ?? ParentData(), children = childrenData ?? []; // Méthode pour ajouter/mettre à jour le parent 1 void updateParent1(ParentData data) { parent1 = data; + notifyListeners(); // Notifier les changements } // Méthode pour ajouter/mettre à jour le parent 2 void updateParent2(ParentData? data) { parent2 = data; + notifyListeners(); } // Méthode pour ajouter un enfant void addChild(ChildData child) { children.add(child); + notifyListeners(); } // Méthode pour mettre à jour un enfant (si nécessaire plus tard) void updateChild(int index, ChildData child) { if (index >= 0 && index < children.length) { children[index] = child; + notifyListeners(); + } + } + + // Méthode pour supprimer un enfant + void removeChild(int index) { + if (index >= 0 && index < children.length) { + children.removeAt(index); + notifyListeners(); } } // Mettre à jour la motivation void updateMotivation(String text) { motivationText = text; + notifyListeners(); + } + + // Mettre à jour les informations bancaires et CAF + void updateFinancialInfo({ + BankDetails? bankDetails, + String? attestationCafNumber, + bool? consentQuotientFamilial, + }) { + if (bankDetails != null) this.bankDetails = bankDetails; + if (attestationCafNumber != null) this.attestationCafNumber = attestationCafNumber; + if (consentQuotientFamilial != null) this.consentQuotientFamilial = consentQuotientFamilial; + notifyListeners(); } // Accepter les CGU - void acceptCGU() { - cguAccepted = true; + void acceptCGU(bool accepted) { // Prend un booléen + cguAccepted = accepted; + notifyListeners(); + } + + // Méthode pour vérifier si toutes les données requises sont là (simplifié) + bool isRegistrationComplete() { + // Ajouter ici les validations nécessaires + // Exemple : parent1 doit avoir des champs remplis, au moins un enfant, CGU acceptées + return parent1.firstName.isNotEmpty && + parent1.lastName.isNotEmpty && + children.isNotEmpty && + cguAccepted; } } \ No newline at end of file diff --git a/frontend/lib/navigation/app_router.dart b/frontend/lib/navigation/app_router.dart index 70b55a2..8e53f60 100644 --- a/frontend/lib/navigation/app_router.dart +++ b/frontend/lib/navigation/app_router.dart @@ -67,11 +67,7 @@ class AppRouter { slideTransition = true; break; case parentRegisterStep5: - if (args is UserRegistrationData) { - screen = ParentRegisterStep5Screen(registrationData: args); - } else { - screen = buildErrorScreen('5'); - } + screen = const ParentRegisterStep5Screen(); slideTransition = true; break; case home: diff --git a/frontend/lib/screens/auth/login_screen.dart b/frontend/lib/screens/auth/login_screen.dart index f6c8a8f..6f3cc9c 100644 --- a/frontend/lib/screens/auth/login_screen.dart +++ b/frontend/lib/screens/auth/login_screen.dart @@ -8,14 +8,14 @@ import 'package:go_router/go_router.dart'; import '../../widgets/image_button.dart'; import '../../widgets/custom_app_text_field.dart'; -class LoginPage extends StatefulWidget { - const LoginPage({super.key}); +class LoginScreen extends StatefulWidget { + const LoginScreen({super.key}); @override - State createState() => _LoginPageState(); + State createState() => _LoginPageState(); } -class _LoginPageState extends State { +class _LoginPageState extends State { final _formKey = GlobalKey(); final _emailController = TextEditingController(); final _passwordController = TextEditingController(); @@ -168,12 +168,12 @@ class _LoginPageState extends State { ), ), ), - const SizedBox(height: 10), - // Lien de création de compte + const SizedBox(height: 20), + // Lien de création de compte (version originale) Center( child: TextButton( onPressed: () { - Navigator.pushNamed(context, '/register-choice'); + context.go('/register-choice'); }, child: Text( 'Créer un compte', @@ -185,7 +185,6 @@ class _LoginPageState extends State { ), ), ), - const SizedBox(height: 20), // Réduit l'espacement en bas ], ), ), @@ -234,13 +233,13 @@ class _LoginPageState extends State { _FooterLink( text: 'Mentions légales', onTap: () { - Navigator.pushNamed(context, '/legal'); + context.go('/legal'); }, ), _FooterLink( text: 'Politique de confidentialité', onTap: () { - Navigator.pushNamed(context, '/privacy'); + context.go('/privacy'); }, ), ], diff --git a/frontend/lib/screens/auth/parent_register_step1_screen.dart b/frontend/lib/screens/auth/parent_register_step1_screen.dart index 0463967..5081264 100644 --- a/frontend/lib/screens/auth/parent_register_step1_screen.dart +++ b/frontend/lib/screens/auth/parent_register_step1_screen.dart @@ -5,6 +5,8 @@ import '../../models/user_registration_data.dart'; // Import du modèle de donn import '../../utils/data_generator.dart'; // Import du générateur de données import '../../widgets/custom_app_text_field.dart'; // Import du widget CustomAppTextField import '../../models/card_assets.dart'; // Import des enums de cartes +import 'package:go_router/go_router.dart'; +import 'package:provider/provider.dart'; // Importer Provider class ParentRegisterStep1Screen extends StatefulWidget { const ParentRegisterStep1Screen({super.key}); @@ -15,31 +17,44 @@ class ParentRegisterStep1Screen extends StatefulWidget { class _ParentRegisterStep1ScreenState extends State { final _formKey = GlobalKey(); - late UserRegistrationData _registrationData; + // late UserRegistrationData _registrationData; // Supprimé, on utilisera le Provider - // Contrôleurs pour les champs (restauration CP et Ville) + // Contrôleurs pour les champs final _lastNameController = TextEditingController(); final _firstNameController = TextEditingController(); final _phoneController = TextEditingController(); final _emailController = TextEditingController(); final _passwordController = TextEditingController(); final _confirmPasswordController = TextEditingController(); - final _addressController = TextEditingController(); // Rue seule - final _postalCodeController = TextEditingController(); // Restauré - final _cityController = TextEditingController(); // Restauré + final _addressController = TextEditingController(); + final _postalCodeController = TextEditingController(); + final _cityController = TextEditingController(); @override void initState() { super.initState(); - _registrationData = UserRegistrationData(); - _generateAndFillData(); + // Récupérer les données existantes du Provider pour pré-remplir si l'utilisateur revient + final registrationDataFromProvider = Provider.of(context, listen: false); + _firstNameController.text = registrationDataFromProvider.parent1.firstName; + _lastNameController.text = registrationDataFromProvider.parent1.lastName; + _phoneController.text = registrationDataFromProvider.parent1.phone; + _emailController.text = registrationDataFromProvider.parent1.email; + _passwordController.text = registrationDataFromProvider.parent1.password; + _confirmPasswordController.text = registrationDataFromProvider.parent1.password; // Ou laisser vide pour reconfirmation + _addressController.text = registrationDataFromProvider.parent1.address; + _postalCodeController.text = registrationDataFromProvider.parent1.postalCode; + _cityController.text = registrationDataFromProvider.parent1.city; + + // Si les champs sont vides (première visite), générer des données + if (registrationDataFromProvider.parent1.firstName.isEmpty) { + _generateAndFillData(); + } } void _generateAndFillData() { final String genFirstName = DataGenerator.firstName(); final String genLastName = DataGenerator.lastName(); - // Utilisation des méthodes publiques de DataGenerator _addressController.text = DataGenerator.address(); _postalCodeController.text = DataGenerator.postalCode(); _cityController.text = DataGenerator.city(); @@ -66,6 +81,25 @@ class _ParentRegisterStep1ScreenState extends State { super.dispose(); } + void _submitForm() { + if (_formKey.currentState?.validate() ?? false) { + final registrationData = Provider.of(context, listen: false); + registrationData.updateParent1( + ParentData( + firstName: _firstNameController.text, + lastName: _lastNameController.text, + address: _addressController.text, + postalCode: _postalCodeController.text, + city: _cityController.text, + phone: _phoneController.text, + email: _emailController.text, + password: _passwordController.text, // Sauvegarder le mot de passe + ) + ); + context.go('/parent-register-step2'); + } + } + @override Widget build(BuildContext context) { final screenSize = MediaQuery.of(context).size; @@ -188,7 +222,13 @@ class _ParentRegisterStep1ScreenState extends State { transform: Matrix4.rotationY(math.pi), // Inverse horizontalement child: Image.asset('assets/images/chevron_right.png', height: 40), ), - onPressed: () => Navigator.pop(context), // Retour à l'écran de choix + onPressed: () { + if (context.canPop()) { + context.pop(); + } else { + context.go('/register-choice'); + } + }, tooltip: 'Retour', ), ), @@ -199,23 +239,7 @@ class _ParentRegisterStep1ScreenState extends State { right: 40, child: IconButton( icon: Image.asset('assets/images/chevron_right.png', height: 40), - onPressed: () { - if (_formKey.currentState?.validate() ?? false) { - _registrationData.updateParent1( - ParentData( - firstName: _firstNameController.text, - lastName: _lastNameController.text, - address: _addressController.text, // Rue - postalCode: _postalCodeController.text, // Ajout - city: _cityController.text, // Ajout - phone: _phoneController.text, - email: _emailController.text, - password: _passwordController.text, - ) - ); - Navigator.pushNamed(context, '/parent-register/step2', arguments: _registrationData); - } - }, + onPressed: _submitForm, // Appel de la fonction de soumission tooltip: 'Suivant', ), ), diff --git a/frontend/lib/screens/auth/parent_register_step2_screen.dart b/frontend/lib/screens/auth/parent_register_step2_screen.dart index a7ecaf2..3151c8c 100644 --- a/frontend/lib/screens/auth/parent_register_step2_screen.dart +++ b/frontend/lib/screens/auth/parent_register_step2_screen.dart @@ -5,19 +5,22 @@ import '../../models/user_registration_data.dart'; // Import du modèle import '../../utils/data_generator.dart'; // Import du générateur import '../../widgets/custom_app_text_field.dart'; // Import du widget import '../../models/card_assets.dart'; // Import des enums de cartes +import 'package:provider/provider.dart'; +import 'package:go_router/go_router.dart'; // Importer GoRouter class ParentRegisterStep2Screen extends StatefulWidget { - final UserRegistrationData registrationData; // Accepte les données de l'étape 1 + // final UserRegistrationData registrationData; // Supprimé - const ParentRegisterStep2Screen({super.key, required this.registrationData}); + const ParentRegisterStep2Screen({super.key /*, required this.registrationData */}); // Modifié @override - State createState() => _ParentRegisterStep2ScreenState(); + _ParentRegisterStep2ScreenState createState() => + _ParentRegisterStep2ScreenState(); } class _ParentRegisterStep2ScreenState extends State { final _formKey = GlobalKey(); - late UserRegistrationData _registrationData; // Copie locale pour modification + // late UserRegistrationData _registrationData; // Supprimé bool _addParent2 = true; // Pour le test, on ajoute toujours le parent 2 bool _sameAddressAsParent1 = false; // Peut être généré aléatoirement aussi @@ -36,13 +39,19 @@ class _ParentRegisterStep2ScreenState extends State { @override void initState() { super.initState(); - _registrationData = widget.registrationData; // Récupère les données de l'étape 1 + // On ne récupère plus _registrationData ici + // Mais on peut récupérer les données initiales pour les contrôleurs si nécessaire + final initialData = Provider.of(context, listen: false); + _addParent2 = initialData.parent2 != null; if (_addParent2) { - _generateAndFillParent2Data(); + _fillParent2Data(initialData.parent2!, initialData.parent1); + } else { + _generateAndFillParent2Data(initialData.parent1); // Ou générer si pas de données } } - void _generateAndFillParent2Data() { + // Modifié pour prendre les données parent1 + void _generateAndFillParent2Data(ParentData parent1Data) { final String genFirstName = DataGenerator.firstName(); final String genLastName = DataGenerator.lastName(); _firstNameController.text = genFirstName; @@ -60,12 +69,30 @@ class _ParentRegisterStep2ScreenState extends State { _cityController.text = DataGenerator.city(); } else { // Vider les champs si même adresse (seront désactivés) - _addressController.clear(); - _postalCodeController.clear(); - _cityController.clear(); + _addressController.text = parent1Data.address; + _postalCodeController.text = parent1Data.postalCode; + _cityController.text = parent1Data.city; } } + // Nouvelle fonction pour remplir depuis les données existantes + void _fillParent2Data(ParentData parent2Data, ParentData parent1Data) { + _firstNameController.text = parent2Data.firstName; + _lastNameController.text = parent2Data.lastName; + _phoneController.text = parent2Data.phone; + _emailController.text = parent2Data.email; + _passwordController.text = parent2Data.password; // Attention à la sécurité + _confirmPasswordController.text = parent2Data.password; + + _sameAddressAsParent1 = (parent2Data.address == parent1Data.address && + parent2Data.postalCode == parent1Data.postalCode && + parent2Data.city == parent1Data.city); + + _addressController.text = parent2Data.address; + _postalCodeController.text = parent2Data.postalCode; + _cityController.text = parent2Data.city; + } + @override void dispose() { _lastNameController.dispose(); @@ -85,6 +112,8 @@ class _ParentRegisterStep2ScreenState extends State { @override Widget build(BuildContext context) { + final registrationData = Provider.of(context, listen: false); + final parent1Data = registrationData.parent1; final screenSize = MediaQuery.of(context).size; return Scaffold( @@ -129,7 +158,7 @@ class _ParentRegisterStep2ScreenState extends State { const Spacer(), Switch(value: _addParent2, onChanged: (val) => setState(() { _addParent2 = val ?? false; - if (_addParent2) _generateAndFillParent2Data(); else _clearParent2Fields(); + if (_addParent2) _generateAndFillParent2Data(parent1Data); else _clearParent2Fields(); }), activeColor: Theme.of(context).primaryColor), ]), ), @@ -144,9 +173,9 @@ class _ParentRegisterStep2ScreenState extends State { Switch(value: _sameAddressAsParent1, onChanged: _addParent2 ? (val) => setState(() { _sameAddressAsParent1 = val ?? false; if (_sameAddressAsParent1) { - _addressController.text = _registrationData.parent1.address; - _postalCodeController.text = _registrationData.parent1.postalCode; - _cityController.text = _registrationData.parent1.city; + _addressController.text = parent1Data.address; + _postalCodeController.text = parent1Data.postalCode; + _cityController.text = parent1Data.city; } else { _addressController.text = DataGenerator.address(); _postalCodeController.text = DataGenerator.postalCode(); @@ -204,7 +233,13 @@ class _ParentRegisterStep2ScreenState extends State { left: 40, child: IconButton( icon: Transform(alignment: Alignment.center, transform: Matrix4.rotationY(math.pi), child: Image.asset('assets/images/chevron_right.png', height: 40)), - onPressed: () => Navigator.pop(context), + onPressed: () { + if (context.canPop()) { + context.pop(); + } else { + context.go('/parent-register-step1'); + } + }, tooltip: 'Retour', ), ), @@ -216,22 +251,22 @@ class _ParentRegisterStep2ScreenState extends State { onPressed: () { if (!_addParent2 || (_formKey.currentState?.validate() ?? false)) { if (_addParent2) { - _registrationData.updateParent2( + registrationData.updateParent2( ParentData( firstName: _firstNameController.text, lastName: _lastNameController.text, - address: _sameAddressAsParent1 ? _registrationData.parent1.address : _addressController.text, - postalCode: _sameAddressAsParent1 ? _registrationData.parent1.postalCode : _postalCodeController.text, - city: _sameAddressAsParent1 ? _registrationData.parent1.city : _cityController.text, + address: _sameAddressAsParent1 ? parent1Data.address : _addressController.text, + postalCode: _sameAddressAsParent1 ? parent1Data.postalCode : _postalCodeController.text, + city: _sameAddressAsParent1 ? parent1Data.city : _cityController.text, phone: _phoneController.text, email: _emailController.text, password: _passwordController.text, ) ); } else { - _registrationData.updateParent2(null); + registrationData.updateParent2(null); } - Navigator.pushNamed(context, '/parent-register/step3', arguments: _registrationData); + context.go('/parent-register-step3'); } }, tooltip: 'Suivant', diff --git a/frontend/lib/screens/auth/parent_register_step3_screen.dart b/frontend/lib/screens/auth/parent_register_step3_screen.dart index ac9daff..f82636b 100644 --- a/frontend/lib/screens/auth/parent_register_step3_screen.dart +++ b/frontend/lib/screens/auth/parent_register_step3_screen.dart @@ -12,20 +12,23 @@ import '../../widgets/app_custom_checkbox.dart'; // Import du nouveau widget Che import '../../models/user_registration_data.dart'; // Import du modèle de données import '../../utils/data_generator.dart'; // Import du générateur import '../../models/card_assets.dart'; // Import des enums de cartes +import 'package:provider/provider.dart'; // Assurer l'import +import 'package:go_router/go_router.dart'; // La classe _ChildFormData est supprimée car on utilise ChildData du modèle class ParentRegisterStep3Screen extends StatefulWidget { - final UserRegistrationData registrationData; // Accepte les données + // final UserRegistrationData registrationData; // Supprimé - const ParentRegisterStep3Screen({super.key, required this.registrationData}); + const ParentRegisterStep3Screen({super.key /*, required this.registrationData */}); // Modifié @override - State createState() => _ParentRegisterStep3ScreenState(); + _ParentRegisterStep3ScreenState createState() => + _ParentRegisterStep3ScreenState(); } class _ParentRegisterStep3ScreenState extends State { - late UserRegistrationData _registrationData; // Stocke l'état complet + // late UserRegistrationData _registrationData; // Supprimé final ScrollController _scrollController = ScrollController(); // Pour le défilement horizontal bool _isScrollable = false; bool _showLeftFade = false; @@ -52,14 +55,18 @@ class _ParentRegisterStep3ScreenState extends State { @override void initState() { super.initState(); - _registrationData = widget.registrationData; + final registrationData = Provider.of(context, listen: false); + // _registrationData = registrationData; // Supprimé + // Initialiser les couleurs utilisées avec les enfants existants - for (var child in _registrationData.children) { + for (var child in registrationData.children) { _usedColors.add(child.cardColor); } - // S'il n'y a pas d'enfant, en ajouter un automatiquement avec des données générées - if (_registrationData.children.isEmpty) { - _addChild(); + // S'il n'y a pas d'enfant, en ajouter un automatiquement APRÈS le premier build + if (registrationData.children.isEmpty) { + WidgetsBinding.instance.addPostFrameCallback((_) { + _addChild(registrationData); + }); } _scrollController.addListener(_scrollListener); WidgetsBinding.instance.addPostFrameCallback((_) => _scrollListener()); @@ -87,7 +94,7 @@ class _ParentRegisterStep3ScreenState extends State { } } - void _addChild() { + void _addChild(UserRegistrationData registrationData) { // Prend registrationData setState(() { bool isUnborn = DataGenerator.boolean(); @@ -98,7 +105,7 @@ class _ParentRegisterStep3ScreenState extends State { ); final newChild = ChildData( - lastName: _registrationData.parent1.lastName, + lastName: registrationData.parent1.lastName, firstName: DataGenerator.firstName(), dob: DataGenerator.dob(isUnborn: isUnborn), isUnbornChild: isUnborn, @@ -106,7 +113,7 @@ class _ParentRegisterStep3ScreenState extends State { multipleBirth: DataGenerator.boolean(), cardColor: cardColor, ); - _registrationData.addChild(newChild); + registrationData.addChild(newChild); _usedColors.add(cardColor); }); WidgetsBinding.instance.addPostFrameCallback((_) { @@ -117,33 +124,42 @@ class _ParentRegisterStep3ScreenState extends State { }); } - void _removeChild(int index) { - if (_registrationData.children.length > 1 && index >= 0 && index < _registrationData.children.length) { + void _removeChild(int index, UserRegistrationData registrationData) { + if (registrationData.children.length > 1 && index >= 0 && index < registrationData.children.length) { setState(() { // Ne pas retirer la couleur de _usedColors pour éviter sa réutilisation - _registrationData.children.removeAt(index); + registrationData.children.removeAt(index); }); WidgetsBinding.instance.addPostFrameCallback((_) => _scrollListener()); } } - Future _pickImage(int childIndex) async { + Future _pickImage(int childIndex, UserRegistrationData registrationData) async { final ImagePicker picker = ImagePicker(); try { final XFile? pickedFile = await picker.pickImage( source: ImageSource.gallery, imageQuality: 70, maxWidth: 1024, maxHeight: 1024); if (pickedFile != null) { - setState(() { - if (childIndex < _registrationData.children.length) { - _registrationData.children[childIndex].imageFile = File(pickedFile.path); - } - }); + if (childIndex < registrationData.children.length) { + final oldChild = registrationData.children[childIndex]; + final updatedChild = ChildData( + firstName: oldChild.firstName, + lastName: oldChild.lastName, + dob: oldChild.dob, + photoConsent: oldChild.photoConsent, + multipleBirth: oldChild.multipleBirth, + isUnbornChild: oldChild.isUnbornChild, + imageFile: File(pickedFile.path), + cardColor: oldChild.cardColor, + ); + registrationData.updateChild(childIndex, updatedChild); + } } } catch (e) { print("Erreur image: $e"); } } - Future _selectDate(BuildContext context, int childIndex) async { - final ChildData currentChild = _registrationData.children[childIndex]; + Future _selectDate(BuildContext context, int childIndex, UserRegistrationData registrationData) async { + final ChildData currentChild = registrationData.children[childIndex]; final DateTime now = DateTime.now(); DateTime initialDatePickerDate = now; DateTime firstDatePickerDate = DateTime(1980); DateTime lastDatePickerDate = now; @@ -175,14 +191,24 @@ class _ParentRegisterStep3ScreenState extends State { lastDate: lastDatePickerDate, locale: const Locale('fr', 'FR'), ); if (picked != null) { - setState(() { - currentChild.dob = "${picked.day.toString().padLeft(2, '0')}/${picked.month.toString().padLeft(2, '0')}/${picked.year}"; - }); + final oldChild = registrationData.children[childIndex]; + final updatedChild = ChildData( + firstName: oldChild.firstName, + lastName: oldChild.lastName, + dob: "${picked.day.toString().padLeft(2, '0')}/${picked.month.toString().padLeft(2, '0')}/${picked.year}", + photoConsent: oldChild.photoConsent, + multipleBirth: oldChild.multipleBirth, + isUnbornChild: oldChild.isUnbornChild, + imageFile: oldChild.imageFile, + cardColor: oldChild.cardColor, + ); + registrationData.updateChild(childIndex, updatedChild); } } @override Widget build(BuildContext context) { + final registrationData = Provider.of(context /*, listen: true par défaut */); final screenSize = MediaQuery.of(context).size; return Scaffold( body: Stack( @@ -221,36 +247,57 @@ class _ParentRegisterStep3ScreenState extends State { controller: _scrollController, scrollDirection: Axis.horizontal, padding: const EdgeInsets.symmetric(horizontal: 20.0), - itemCount: _registrationData.children.length + 1, + itemCount: registrationData.children.length + 1, itemBuilder: (context, index) { - if (index < _registrationData.children.length) { + if (index < registrationData.children.length) { // Carte Enfant return Padding( padding: const EdgeInsets.only(right: 20.0), child: _ChildCardWidget( - key: ValueKey(_registrationData.children[index].hashCode), // Utiliser une clé basée sur les données - childData: _registrationData.children[index], + key: ValueKey(registrationData.children[index].hashCode), // Utiliser une clé basée sur les données + childData: registrationData.children[index], childIndex: index, - onPickImage: () => _pickImage(index), - onDateSelect: () => _selectDate(context, index), - onFirstNameChanged: (value) => setState(() => _registrationData.children[index].firstName = value), - onLastNameChanged: (value) => setState(() => _registrationData.children[index].lastName = value), - onTogglePhotoConsent: (newValue) => setState(() => _registrationData.children[index].photoConsent = newValue), - onToggleMultipleBirth: (newValue) => setState(() => _registrationData.children[index].multipleBirth = newValue), - onToggleIsUnborn: (newValue) => setState(() { - _registrationData.children[index].isUnbornChild = newValue; - // Générer une nouvelle date si on change le statut - _registrationData.children[index].dob = DataGenerator.dob(isUnborn: newValue); - }), - onRemove: () => _removeChild(index), - canBeRemoved: _registrationData.children.length > 1, + onPickImage: () => _pickImage(index, registrationData), + onDateSelect: () => _selectDate(context, index, registrationData), + onFirstNameChanged: (value) => setState(() => registrationData.updateChild(index, ChildData( + firstName: value, lastName: registrationData.children[index].lastName, dob: registrationData.children[index].dob, photoConsent: registrationData.children[index].photoConsent, + multipleBirth: registrationData.children[index].multipleBirth, isUnbornChild: registrationData.children[index].isUnbornChild, imageFile: registrationData.children[index].imageFile, cardColor: registrationData.children[index].cardColor + ))), + onLastNameChanged: (value) => setState(() => registrationData.updateChild(index, ChildData( + firstName: registrationData.children[index].firstName, lastName: value, dob: registrationData.children[index].dob, photoConsent: registrationData.children[index].photoConsent, + multipleBirth: registrationData.children[index].multipleBirth, isUnbornChild: registrationData.children[index].isUnbornChild, imageFile: registrationData.children[index].imageFile, cardColor: registrationData.children[index].cardColor + ))), + onTogglePhotoConsent: (newValue) { + final oldChild = registrationData.children[index]; + registrationData.updateChild(index, ChildData( + firstName: oldChild.firstName, lastName: oldChild.lastName, dob: oldChild.dob, photoConsent: newValue, + multipleBirth: oldChild.multipleBirth, isUnbornChild: oldChild.isUnbornChild, imageFile: oldChild.imageFile, cardColor: oldChild.cardColor + )); + }, + onToggleMultipleBirth: (newValue) { + final oldChild = registrationData.children[index]; + registrationData.updateChild(index, ChildData( + firstName: oldChild.firstName, lastName: oldChild.lastName, dob: oldChild.dob, photoConsent: oldChild.photoConsent, + multipleBirth: newValue, isUnbornChild: oldChild.isUnbornChild, imageFile: oldChild.imageFile, cardColor: oldChild.cardColor + )); + }, + onToggleIsUnborn: (newValue) { + final oldChild = registrationData.children[index]; + registrationData.updateChild(index, ChildData( + firstName: oldChild.firstName, lastName: oldChild.lastName, dob: DataGenerator.dob(isUnborn: newValue), + photoConsent: oldChild.photoConsent, multipleBirth: oldChild.multipleBirth, isUnbornChild: newValue, + imageFile: oldChild.imageFile, cardColor: oldChild.cardColor + )); + }, + onRemove: () => _removeChild(index, registrationData), + canBeRemoved: registrationData.children.length > 1, ), ); } else { // Bouton Ajouter return Center( child: HoverReliefWidget( - onPressed: _addChild, + onPressed: () => _addChild(registrationData), borderRadius: BorderRadius.circular(15), child: Image.asset('assets/images/plus.png', height: 80, width: 80), ), @@ -272,7 +319,13 @@ class _ParentRegisterStep3ScreenState extends State { left: 40, child: IconButton( icon: Transform(alignment: Alignment.center, transform: Matrix4.rotationY(math.pi), child: Image.asset('assets/images/chevron_right.png', height: 40)), - onPressed: () => Navigator.pop(context), + onPressed: () { + if (context.canPop()) { + context.pop(); + } else { + context.go('/parent-register-step2'); + } + }, tooltip: 'Retour', ), ), @@ -282,8 +335,7 @@ class _ParentRegisterStep3ScreenState extends State { child: IconButton( icon: Image.asset('assets/images/chevron_right.png', height: 40), onPressed: () { - // TODO: Validation (si nécessaire) - Navigator.pushNamed(context, '/parent-register/step4', arguments: _registrationData); + context.go('/parent-register-step4'); }, tooltip: 'Suivant', ), diff --git a/frontend/lib/screens/auth/parent_register_step4_screen.dart b/frontend/lib/screens/auth/parent_register_step4_screen.dart index 62ae003..5b1b42a 100644 --- a/frontend/lib/screens/auth/parent_register_step4_screen.dart +++ b/frontend/lib/screens/auth/parent_register_step4_screen.dart @@ -7,31 +7,50 @@ import 'package:p_tits_pas/widgets/app_custom_checkbox.dart'; // Import de la ch import '../../models/user_registration_data.dart'; // Import du vrai modèle import '../../utils/data_generator.dart'; // Import du générateur import '../../models/card_assets.dart'; // Import des enums de cartes +import 'package:provider/provider.dart'; // Assurer l'import +import 'package:go_router/go_router.dart'; class ParentRegisterStep4Screen extends StatefulWidget { - final UserRegistrationData registrationData; // Accepte les données + // final UserRegistrationData registrationData; // Supprimé - const ParentRegisterStep4Screen({super.key, required this.registrationData}); + const ParentRegisterStep4Screen({super.key /*, required this.registrationData */}); // Modifié @override - State createState() => _ParentRegisterStep4ScreenState(); + _ParentRegisterStep4ScreenState createState() => + _ParentRegisterStep4ScreenState(); } class _ParentRegisterStep4ScreenState extends State { - late UserRegistrationData _registrationData; // État local + // late UserRegistrationData _registrationData; // Supprimé final _motivationController = TextEditingController(); bool _cguAccepted = true; // Pour le test, CGU acceptées par défaut + final _bankNameController = TextEditingController(); + final _ibanController = TextEditingController(); + final _bicController = TextEditingController(); + final _attestationController = TextEditingController(); + bool _consentQuotientFamilial = false; @override void initState() { super.initState(); - _registrationData = widget.registrationData; - _motivationController.text = DataGenerator.motivation(); // Générer la motivation + final registrationData = Provider.of(context, listen: false); + // _registrationData = registrationData; // Supprimé + _motivationController.text = registrationData.motivationText.isNotEmpty ? registrationData.motivationText : DataGenerator.motivation(); + _bankNameController.text = registrationData.bankDetails?.bankName ?? ''; + _ibanController.text = registrationData.bankDetails?.iban ?? ''; + _bicController.text = registrationData.bankDetails?.bic ?? ''; + _attestationController.text = registrationData.attestationCafNumber; + _consentQuotientFamilial = registrationData.consentQuotientFamilial; + _cguAccepted = registrationData.cguAccepted; } @override void dispose() { _motivationController.dispose(); + _bankNameController.dispose(); + _ibanController.dispose(); + _bicController.dispose(); + _attestationController.dispose(); super.dispose(); } @@ -100,6 +119,7 @@ Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lo @override Widget build(BuildContext context) { + final registrationData = Provider.of(context, listen: false); // listen:false car on met à jour final screenSize = MediaQuery.of(context).size; final cardWidth = screenSize.width * 0.6; // Largeur de la carte (60% de l'écran) final double imageAspectRatio = 2.0; // Ratio corrigé (1024/512 = 2.0) @@ -186,7 +206,13 @@ Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lo left: 40, child: IconButton( icon: Transform(alignment: Alignment.center, transform: Matrix4.rotationY(math.pi), child: Image.asset('assets/images/chevron_right.png', height: 40)), - onPressed: () => Navigator.pop(context), + onPressed: () { + if (context.canPop()) { + context.pop(); + } else { + context.go('/parent-register-step3'); + } + }, tooltip: 'Retour', ), ), @@ -197,14 +223,18 @@ Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lo icon: Image.asset('assets/images/chevron_right.png', height: 40), onPressed: _cguAccepted ? () { - _registrationData.updateMotivation(_motivationController.text); - _registrationData.acceptCGU(); - - Navigator.pushNamed( - context, - '/parent-register/step5', - arguments: _registrationData + registrationData.updateMotivation(_motivationController.text); + registrationData.acceptCGU(_cguAccepted); + registrationData.updateFinancialInfo( + bankDetails: BankDetails( + bankName: _bankNameController.text, + iban: _ibanController.text, + bic: _bicController.text, + ), + attestationCafNumber: _attestationController.text, + consentQuotientFamilial: _consentQuotientFamilial, ); + context.go('/parent-register-step5'); } : null, tooltip: 'Suivant', diff --git a/frontend/lib/screens/auth/parent_register_step5_screen.dart b/frontend/lib/screens/auth/parent_register_step5_screen.dart index 24a914c..5f939f7 100644 --- a/frontend/lib/screens/auth/parent_register_step5_screen.dart +++ b/frontend/lib/screens/auth/parent_register_step5_screen.dart @@ -4,69 +4,153 @@ import '../../models/user_registration_data.dart'; // Utilisation du vrai modèl import '../../widgets/image_button.dart'; // Import du ImageButton import '../../models/card_assets.dart'; // Import des enums de cartes import 'package:flutter/foundation.dart' show kIsWeb; -import '../../widgets/custom_decorated_text_field.dart'; // Import du CustomDecoratedTextField +import 'package:provider/provider.dart'; +import 'package:go_router/go_router.dart'; // Nouvelle méthode helper pour afficher un champ de type "lecture seule" stylisé Widget _buildDisplayFieldValue(BuildContext context, String label, String value, {bool multiLine = false, double fieldHeight = 50.0, double labelFontSize = 18.0}) { const FontWeight labelFontWeight = FontWeight.w600; - // Ne pas afficher le label si labelFontSize est 0 ou si label est vide - bool showLabel = label.isNotEmpty && labelFontSize > 0; - return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ - if (showLabel) - Text(label, style: GoogleFonts.merienda(fontSize: labelFontSize, fontWeight: labelFontWeight)), - if (showLabel) - const SizedBox(height: 4), - // Utiliser Expanded si multiLine et pas de hauteur fixe, sinon Container - multiLine && fieldHeight == null - ? Expanded( - child: Container( - width: double.infinity, - padding: const EdgeInsets.symmetric(horizontal: 18.0, vertical: 12.0), - decoration: BoxDecoration( - image: const DecorationImage( - image: AssetImage('assets/images/input_field_bg.png'), - fit: BoxFit.fill, - ), - ), - child: SingleChildScrollView( // Pour le défilement si le texte dépasse - child: Text( - value.isNotEmpty ? value : '-', - style: GoogleFonts.merienda(fontSize: labelFontSize > 0 ? labelFontSize : 18.0), // Garder une taille de texte par défaut si label caché - maxLines: null, // Permettre un nombre illimité de lignes - ), - ), - ), - ) - : Container( - width: double.infinity, - height: multiLine ? null : fieldHeight, - constraints: multiLine ? BoxConstraints(minHeight: fieldHeight) : null, - padding: const EdgeInsets.symmetric(horizontal: 18.0, vertical: 12.0), - decoration: BoxDecoration( - image: const DecorationImage( - image: AssetImage('assets/images/input_field_bg.png'), - fit: BoxFit.fill, - ), - ), - child: Text( - value.isNotEmpty ? value : '-', - style: GoogleFonts.merienda(fontSize: labelFontSize > 0 ? labelFontSize : 18.0), - maxLines: multiLine ? null : 1, - overflow: multiLine ? TextOverflow.visible : TextOverflow.ellipsis, - ), + Text(label, style: GoogleFonts.merienda(fontSize: labelFontSize, fontWeight: labelFontWeight)), + const SizedBox(height: 4), + Container( + width: double.infinity, // Prendra la largeur allouée par son parent (Expanded) + height: multiLine ? null : fieldHeight, // Hauteur flexible pour multiligne, sinon fixe + constraints: multiLine ? const BoxConstraints(minHeight: 50.0) : null, // Hauteur min pour multiligne + padding: const EdgeInsets.symmetric(horizontal: 18.0, vertical: 12.0), // Ajuster au besoin + decoration: BoxDecoration( + image: const DecorationImage( + image: AssetImage('assets/images/input_field_bg.png'), // Image de fond du champ + fit: BoxFit.fill, ), + // Si votre image input_field_bg.png a des coins arrondis intrinsèques, ce borderRadius n'est pas nécessaire + // ou doit correspondre. Sinon, pour une image rectangulaire, vous pouvez l'ajouter. + // borderRadius: BorderRadius.circular(12), + ), + child: Text( + value.isNotEmpty ? value : '-', + style: GoogleFonts.merienda(fontSize: labelFontSize), + maxLines: multiLine ? null : 1, // Permet plusieurs lignes si multiLine est true + overflow: multiLine ? TextOverflow.visible : TextOverflow.ellipsis, + ), + ), ], ); } -class ParentRegisterStep5Screen extends StatelessWidget { - final UserRegistrationData registrationData; +class ParentRegisterStep5Screen extends StatefulWidget { + const ParentRegisterStep5Screen({super.key}); - const ParentRegisterStep5Screen({super.key, required this.registrationData}); + @override + _ParentRegisterStep5ScreenState createState() => _ParentRegisterStep5ScreenState(); +} + +class _ParentRegisterStep5ScreenState extends State { + @override + Widget build(BuildContext context) { + final registrationData = Provider.of(context); + final screenSize = MediaQuery.of(context).size; + final cardWidth = screenSize.width / 2.0; // Largeur de la carte (50% de l'écran) + final double imageAspectRatio = 2.0; // Ratio corrigé (1024/512 = 2.0) + final cardHeight = cardWidth / imageAspectRatio; + + return Scaffold( + body: Stack( + children: [ + Positioned.fill( + child: Image.asset('assets/images/paper2.png', fit: BoxFit.cover, repeat: ImageRepeat.repeatY), + ), + Center( + child: SingleChildScrollView( + padding: const EdgeInsets.symmetric(vertical: 40.0), // Padding horizontal supprimé ici + child: Padding( // Ajout du Padding horizontal externe + padding: EdgeInsets.symmetric(horizontal: screenSize.width / 4.0), + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text('Étape 5/5', style: GoogleFonts.merienda(fontSize: 16, color: Colors.black54)), + const SizedBox(height: 20), + Text('Récapitulatif de votre demande', style: GoogleFonts.merienda(fontSize: 22, fontWeight: FontWeight.bold, color: Colors.black87), textAlign: TextAlign.center), + const SizedBox(height: 30), + + _buildParent1Card(context, registrationData.parent1), + const SizedBox(height: 20), + if (registrationData.parent2 != null) ...[ + _buildParent2Card(context, registrationData.parent2!), + const SizedBox(height: 20), + ], + ..._buildChildrenCards(context, registrationData.children), + _buildMotivationCard(context, registrationData.motivationText), + const SizedBox(height: 40), + ImageButton( + bg: 'assets/images/btn_green.png', + text: 'Soumettre ma demande', + textColor: const Color(0xFF2D6A4F), + width: 350, + height: 50, + fontSize: 18, + onPressed: () { + print("Données finales: ${registrationData.parent1.firstName}, Enfant(s): ${registrationData.children.length}"); + _showConfirmationModal(context); + }, + ), + ], + ), + ), + ), + ), + Positioned( + top: screenSize.height / 2 - 20, + left: 40, + child: IconButton( + icon: Transform.flip(flipX: true, child: Image.asset('assets/images/chevron_right.png', height: 40)), + onPressed: () { + if (context.canPop()) { + context.pop(); + } else { + context.go('/parent-register-step4'); + } + }, + tooltip: 'Retour', + ), + ), + ], + ), + ); + } + + void _showConfirmationModal(BuildContext context) { + showDialog( + context: context, + barrierDismissible: false, + builder: (BuildContext dialogContext) { + return AlertDialog( + title: Text( + 'Demande enregistrée', + style: GoogleFonts.merienda(fontWeight: FontWeight.bold), + ), + content: Text( + 'Votre dossier a bien été pris en compte. Un gestionnaire le validera bientôt.', + style: GoogleFonts.merienda(fontSize: 14), + ), + actions: [ + TextButton( + child: Text('OK', style: GoogleFonts.merienda(fontWeight: FontWeight.bold)), + onPressed: () { + Navigator.of(dialogContext).pop(); // Ferme la modale + // Utiliser go_router pour la navigation + context.go('/login'); + }, + ), + ], + ); + }, + ); + } // Méthode pour construire la carte Parent 1 Widget _buildParent1Card(BuildContext context, ParentData data) { @@ -98,7 +182,7 @@ class ParentRegisterStep5Screen extends StatelessWidget { backgroundImagePath: CardColorHorizontal.peach.path, title: 'Parent Principal', content: details, - onEdit: () => Navigator.of(context).pushNamed('/parent-register/step1', arguments: registrationData), + onEdit: () => context.go('/parent-register-step1'), ); } @@ -131,7 +215,7 @@ class ParentRegisterStep5Screen extends StatelessWidget { backgroundImagePath: CardColorHorizontal.blue.path, title: 'Deuxième Parent', content: details, - onEdit: () => Navigator.of(context).pushNamed('/parent-register/step2', arguments: registrationData), + onEdit: () => context.go('/parent-register-step2'), ); } @@ -176,10 +260,7 @@ class ParentRegisterStep5Screen extends StatelessWidget { IconButton( icon: const Icon(Icons.edit, color: Colors.black54, size: 28), onPressed: () { - Navigator.of(context).pushNamed( - '/parent-register/step3', - arguments: registrationData, - ); + context.go('/parent-register-step3', extra: {'childIndex': index}); }, tooltip: 'Modifier', ), @@ -264,23 +345,17 @@ class ParentRegisterStep5Screen extends StatelessWidget { // Méthode pour construire la carte Motivation Widget _buildMotivationCard(BuildContext context, String motivation) { + List details = [ + Text(motivation.isNotEmpty ? motivation : 'Aucune motivation renseignée.', + style: GoogleFonts.merienda(fontSize: 18), + maxLines: 4, + overflow: TextOverflow.ellipsis) + ]; return _SummaryCard( - backgroundImagePath: CardColorHorizontal.green.path, + backgroundImagePath: CardColorHorizontal.pink.path, title: 'Votre Motivation', - content: [ - Expanded( - child: CustomDecoratedTextField( - controller: TextEditingController(text: motivation), - hintText: 'Aucune motivation renseignée.', - fieldHeight: 200, - maxLines: 10, - expandDynamically: true, - readOnly: true, - fontSize: 18.0, - ), - ), - ], - onEdit: () => Navigator.of(context).pushNamed('/parent-register/step4', arguments: registrationData), + content: details, + onEdit: () => context.go('/parent-register-step4'), ); } @@ -306,102 +381,6 @@ class ParentRegisterStep5Screen extends StatelessWidget { ), ); } - - @override - Widget build(BuildContext context) { - final screenSize = MediaQuery.of(context).size; - final cardWidth = screenSize.width / 2.0; // Largeur de la carte (50% de l'écran) - final double imageAspectRatio = 2.0; // Ratio corrigé (1024/512 = 2.0) - final cardHeight = cardWidth / imageAspectRatio; - - return Scaffold( - body: Stack( - children: [ - Positioned.fill( - child: Image.asset('assets/images/paper2.png', fit: BoxFit.cover, repeat: ImageRepeat.repeatY), - ), - Center( - child: SingleChildScrollView( - padding: const EdgeInsets.symmetric(vertical: 40.0), // Padding horizontal supprimé ici - child: Padding( // Ajout du Padding horizontal externe - padding: EdgeInsets.symmetric(horizontal: screenSize.width / 4.0), - child: Column( - mainAxisAlignment: MainAxisAlignment.center, - crossAxisAlignment: CrossAxisAlignment.center, - children: [ - Text('Étape 5/5', style: GoogleFonts.merienda(fontSize: 16, color: Colors.black54)), - const SizedBox(height: 20), - Text('Récapitulatif de votre demande', style: GoogleFonts.merienda(fontSize: 22, fontWeight: FontWeight.bold, color: Colors.black87), textAlign: TextAlign.center), - const SizedBox(height: 30), - - _buildParent1Card(context, registrationData.parent1), - const SizedBox(height: 20), - if (registrationData.parent2 != null) ...[ - _buildParent2Card(context, registrationData.parent2!), - const SizedBox(height: 20), - ], - ..._buildChildrenCards(context, registrationData.children), - _buildMotivationCard(context, registrationData.motivationText), - const SizedBox(height: 40), - ImageButton( - bg: 'assets/images/btn_green.png', - text: 'Soumettre ma demande', - textColor: const Color(0xFF2D6A4F), - width: 350, - height: 50, - fontSize: 18, - onPressed: () { - print("Données finales: ${registrationData.parent1.firstName}, Enfant(s): ${registrationData.children.length}"); - _showConfirmationModal(context); - }, - ), - ], - ), - ), - ), - ), - Positioned( - top: screenSize.height / 2 - 20, - left: 40, - child: IconButton( - icon: Transform.flip(flipX: true, child: Image.asset('assets/images/chevron_right.png', height: 40)), - onPressed: () => Navigator.pop(context), // Retour à l'étape 4 - tooltip: 'Retour', - ), - ), - ], - ), - ); - } - - void _showConfirmationModal(BuildContext context) { - showDialog( - context: context, - barrierDismissible: false, - builder: (BuildContext dialogContext) { - return AlertDialog( - title: Text( - 'Demande enregistrée', - style: GoogleFonts.merienda(fontWeight: FontWeight.bold), - ), - content: Text( - 'Votre dossier a bien été pris en compte. Un gestionnaire le validera bientôt.', - style: GoogleFonts.merienda(fontSize: 14), - ), - actions: [ - TextButton( - child: Text('OK', style: GoogleFonts.merienda(fontWeight: FontWeight.bold)), - onPressed: () { - Navigator.of(dialogContext).pop(); // Ferme la modale - // TODO: Naviguer vers l'écran de connexion ou tableau de bord - Navigator.of(context).pushNamedAndRemoveUntil('/login', (Route route) => false); - }, - ), - ], - ); - }, - ); - } } // Widget générique _SummaryCard (ajusté) @@ -422,7 +401,7 @@ class _SummaryCard extends StatelessWidget { @override Widget build(BuildContext context) { return AspectRatio( - aspectRatio: 2.0, + aspectRatio: 2.0, // Le ratio largeur/hauteur de nos images de fond child: Container( padding: const EdgeInsets.symmetric(vertical: 20.0, horizontal: 25.0), decoration: BoxDecoration( @@ -432,30 +411,31 @@ class _SummaryCard extends StatelessWidget { ), borderRadius: BorderRadius.circular(15), ), - child: Column( + child: Row( + crossAxisAlignment: CrossAxisAlignment.start, children: [ - Row( - children: [ - Expanded( - child: Text( - title, - style: GoogleFonts.merienda(fontSize: 28, fontWeight: FontWeight.w600), - textAlign: TextAlign.center, - ), - ), - IconButton( - icon: const Icon(Icons.edit, color: Colors.black54, size: 28), - onPressed: onEdit, - tooltip: 'Modifier', - ), - ], - ), - const SizedBox(height: 18), Expanded( child: Column( - children: content, + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, // Pour que la colonne prenne la hauteur du contenu + children: [ + Align( // Centrer le titre + alignment: Alignment.center, + child: Text( + title, + style: GoogleFonts.merienda(fontSize: 22, fontWeight: FontWeight.bold, color: Colors.black87), // Police légèrement augmentée + ), + ), + const SizedBox(height: 12), // Espacement ajusté après le titre + ...content, + ], ), ), + IconButton( + icon: const Icon(Icons.edit, color: Colors.black54, size: 28), // Icône un peu plus grande + onPressed: onEdit, + tooltip: 'Modifier', + ), ], ), ), diff --git a/frontend/lib/screens/auth/register_choice_screen.dart b/frontend/lib/screens/auth/register_choice_screen.dart index 0b6bd8a..bdf877e 100644 --- a/frontend/lib/screens/auth/register_choice_screen.dart +++ b/frontend/lib/screens/auth/register_choice_screen.dart @@ -3,6 +3,7 @@ import 'package:google_fonts/google_fonts.dart'; import 'dart:math' as math; // Pour la rotation du chevron import '../../widgets/hover_relief_widget.dart'; // Import du widget générique import '../../models/card_assets.dart'; // Import des enums de cartes +import 'package:go_router/go_router.dart'; class RegisterChoiceScreen extends StatelessWidget { const RegisterChoiceScreen({super.key}); @@ -28,12 +29,14 @@ class RegisterChoiceScreen extends StatelessWidget { top: 40, left: 40, child: IconButton( - icon: Transform( - alignment: Alignment.center, - transform: Matrix4.rotationY(math.pi), - child: Image.asset('assets/images/chevron_right.png', height: 40), - ), - onPressed: () => Navigator.pop(context), + icon: Transform.flip(flipX: true, child: Image.asset('assets/images/chevron_right.png', height: 40)), + onPressed: () { + if (context.canPop()) { + context.pop(); + } else { + context.go('/login'); + } + }, tooltip: 'Retour', ), ), @@ -89,7 +92,7 @@ class RegisterChoiceScreen extends StatelessWidget { iconPath: 'assets/images/icon_parents.png', label: 'Parents', onPressed: () { - Navigator.pushNamed(context, '/parent-register/step1'); + context.go('/parent-register-step1'); }, ), // Bouton "Assistante Maternelle" avec HoverReliefWidget appliqué uniquement à l'image @@ -98,8 +101,7 @@ class RegisterChoiceScreen extends StatelessWidget { iconPath: 'assets/images/icon_assmat.png', label: 'Assistante Maternelle', onPressed: () { - // TODO: Naviguer vers l'écran d'inscription assmat - print('Choix: Assistante Maternelle'); + context.go('/nanny-register-step1'); }, ), ], diff --git a/frontend/lib/utils/data_generator.dart b/frontend/lib/utils/data_generator.dart index a6f9077..d7ffde5 100644 --- a/frontend/lib/utils/data_generator.dart +++ b/frontend/lib/utils/data_generator.dart @@ -3,6 +3,11 @@ import 'dart:math'; class DataGenerator { static final Random _random = Random(); + // Méthodes publiques pour la génération de nombres aléatoires + static int randomInt(int max) => _random.nextInt(max); + static int randomIntInRange(int min, int max) => min + _random.nextInt(max - min); + static bool randomBool() => _random.nextBool(); + static final List _firstNames = [ 'Alice', 'Bob', 'Charlie', 'David', 'Eva', 'Félix', 'Gabrielle', 'Hugo', 'Inès', 'Jules', 'Léa', 'Manon', 'Nathan', 'Oscar', 'Pauline', 'Quentin', 'Raphaël', 'Sophie', 'Théo', 'Victoire'