diff --git a/frontend/lib/models/user_registration_data.dart b/frontend/lib/models/user_registration_data.dart index 9687898..6675566 100644 --- a/frontend/lib/models/user_registration_data.dart +++ b/frontend/lib/models/user_registration_data.dart @@ -1,7 +1,6 @@ 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 { @@ -32,6 +31,8 @@ class ChildData { String firstName; String lastName; String dob; // Date de naissance ou prévisionnelle + /// Valeurs API : `H`, `F`, `Autre` (GenreType backend). Vide tant que non choisi. + String genre; bool photoConsent; bool multipleBirth; bool isUnbornChild; @@ -42,12 +43,37 @@ class ChildData { this.firstName = '', this.lastName = '', this.dob = '', + this.genre = '', this.photoConsent = false, this.multipleBirth = false, this.isUnbornChild = false, this.imageFile, required this.cardColor, // Rendre requis dans le constructeur }); + + ChildData copyWith({ + String? firstName, + String? lastName, + String? dob, + String? genre, + bool? photoConsent, + bool? multipleBirth, + bool? isUnbornChild, + File? imageFile, + CardColorVertical? cardColor, + }) { + return ChildData( + firstName: firstName ?? this.firstName, + lastName: lastName ?? this.lastName, + dob: dob ?? this.dob, + genre: genre ?? this.genre, + photoConsent: photoConsent ?? this.photoConsent, + multipleBirth: multipleBirth ?? this.multipleBirth, + isUnbornChild: isUnbornChild ?? this.isUnbornChild, + imageFile: imageFile ?? this.imageFile, + cardColor: cardColor ?? this.cardColor, + ); + } } // Nouvelle classe pour les détails bancaires diff --git a/frontend/lib/screens/auth/parent_register_step3_screen.dart b/frontend/lib/screens/auth/parent_register_step3_screen.dart index 029cf79..ec73328 100644 --- a/frontend/lib/screens/auth/parent_register_step3_screen.dart +++ b/frontend/lib/screens/auth/parent_register_step3_screen.dart @@ -135,16 +135,7 @@ class _ParentRegisterStep3ScreenState extends State { if (pickedFile != null) { 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, - ); + final updatedChild = oldChild.copyWith(imageFile: File(pickedFile.path)); registrationData.updateChild(childIndex, updatedChild); } } @@ -185,15 +176,8 @@ class _ParentRegisterStep3ScreenState extends State { ); if (picked != null) { 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, + final updatedChild = oldChild.copyWith( + dob: "${picked.day.toString().padLeft(2, '0')}/${picked.month.toString().padLeft(2, '0')}/${picked.year}", ); registrationData.updateChild(childIndex, updatedChild); } @@ -289,35 +273,29 @@ class _ParentRegisterStep3ScreenState extends State { childIndex: index, 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 - ))), + onFirstNameChanged: (value) => setState(() { + final c = registrationData.children[index]; + registrationData.updateChild(index, c.copyWith(firstName: value)); + }), + onLastNameChanged: (value) => setState(() { + final c = registrationData.children[index]; + registrationData.updateChild(index, c.copyWith(lastName: value)); + }), + onGenreChanged: (value) { + final oldChild = registrationData.children[index]; + registrationData.updateChild(index, oldChild.copyWith(genre: value)); + }, 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 - )); + registrationData.updateChild(index, oldChild.copyWith(photoConsent: newValue)); }, 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 - )); + registrationData.updateChild(index, oldChild.copyWith(multipleBirth: newValue)); }, onToggleIsUnborn: (newValue) { final oldChild = registrationData.children[index]; - registrationData.updateChild(index, ChildData( - firstName: oldChild.firstName, lastName: oldChild.lastName, dob: oldChild.dob, - photoConsent: oldChild.photoConsent, multipleBirth: oldChild.multipleBirth, isUnbornChild: newValue, - imageFile: oldChild.imageFile, cardColor: oldChild.cardColor - )); + registrationData.updateChild(index, oldChild.copyWith(isUnbornChild: newValue)); }, onRemove: () => _removeChild(index, registrationData), canBeRemoved: registrationData.children.length > 1, @@ -395,36 +373,30 @@ class _ParentRegisterStep3ScreenState extends State { childIndex: index, 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 - ))), + onFirstNameChanged: (value) => setState(() { + final c = registrationData.children[index]; + registrationData.updateChild(index, c.copyWith(firstName: value)); + }), + onLastNameChanged: (value) => setState(() { + final c = registrationData.children[index]; + registrationData.updateChild(index, c.copyWith(lastName: value)); + }), + onGenreChanged: (value) { + final oldChild = registrationData.children[index]; + registrationData.updateChild(index, oldChild.copyWith(genre: value)); + }, 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 - )); + registrationData.updateChild(index, oldChild.copyWith(photoConsent: newValue)); }, 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 - )); + final oldChild = registrationData.children[index]; + registrationData.updateChild(index, oldChild.copyWith(multipleBirth: newValue)); }, onToggleIsUnborn: (newValue) { - final oldChild = registrationData.children[index]; - registrationData.updateChild(index, ChildData( - firstName: oldChild.firstName, lastName: oldChild.lastName, dob: oldChild.dob, - photoConsent: oldChild.photoConsent, multipleBirth: oldChild.multipleBirth, isUnbornChild: newValue, - imageFile: oldChild.imageFile, cardColor: oldChild.cardColor - )); - }, + final oldChild = registrationData.children[index]; + registrationData.updateChild(index, oldChild.copyWith(isUnbornChild: newValue)); + }, onRemove: () => _removeChild(index, registrationData), canBeRemoved: registrationData.children.length > 1, ), diff --git a/frontend/lib/screens/auth/parent_register_step5_screen.dart b/frontend/lib/screens/auth/parent_register_step5_screen.dart index 62dc5d3..c3d859a 100644 --- a/frontend/lib/screens/auth/parent_register_step5_screen.dart +++ b/frontend/lib/screens/auth/parent_register_step5_screen.dart @@ -251,6 +251,7 @@ class _ParentRegisterStep5ScreenState extends State { onDateSelect: () {}, onFirstNameChanged: (v) {}, onLastNameChanged: (v) {}, + onGenreChanged: (_) {}, onTogglePhotoConsent: (v) {}, onToggleMultipleBirth: (v) {}, onToggleIsUnborn: (v) {}, diff --git a/frontend/lib/utils/parent_registration_payload.dart b/frontend/lib/utils/parent_registration_payload.dart index 7651e09..3035efb 100644 --- a/frontend/lib/utils/parent_registration_payload.dart +++ b/frontend/lib/utils/parent_registration_payload.dart @@ -8,6 +8,8 @@ class ParentRegistrationPayload { ParentRegistrationPayload._(); static final RegExp _phoneFr = RegExp(r'^(\+33|0)[1-9](\d{2}){4}$'); + /// Aligné sur `GenreType` (backend) : JSON exact `H`, `F`, `Autre`. + static const Set apiGenres = {'H', 'F', 'Autre'}; /// Retourne un message d'erreur utilisateur, ou `null` si le formulaire est cohérent. static String? validateForApi(UserRegistrationData d) { @@ -57,6 +59,9 @@ class ParentRegistrationPayload { for (var i = 0; i < d.children.length; i++) { final c = d.children[i]; final label = 'Enfant ${i + 1}'; + if (!apiGenres.contains(c.genre)) { + return '$label : sélectionnez le genre (garçon, fille ou autre).'; + } if (c.isUnbornChild) { final due = _ddMmYyyyToIso(c.dob); if (due == null) { @@ -130,7 +135,7 @@ class ParentRegistrationPayload { static Map _childToJson(ChildData c, int index, String parentNom) { final map = { - 'genre': 'F', + 'genre': c.genre, 'grossesse_multiple': c.multipleBirth, }; diff --git a/frontend/lib/widgets/child_card_widget.dart b/frontend/lib/widgets/child_card_widget.dart index 18bdf72..2966665 100644 --- a/frontend/lib/widgets/child_card_widget.dart +++ b/frontend/lib/widgets/child_card_widget.dart @@ -19,6 +19,8 @@ class ChildCardWidget extends StatefulWidget { final VoidCallback onDateSelect; final ValueChanged onFirstNameChanged; final ValueChanged onLastNameChanged; + /// `H`, `F` ou `Autre` (GenreType API). + final ValueChanged onGenreChanged; final ValueChanged onTogglePhotoConsent; final ValueChanged onToggleMultipleBirth; final ValueChanged onToggleIsUnborn; @@ -35,6 +37,7 @@ class ChildCardWidget extends StatefulWidget { required this.onDateSelect, required this.onFirstNameChanged, required this.onLastNameChanged, + required this.onGenreChanged, required this.onTogglePhotoConsent, required this.onToggleMultipleBirth, required this.onToggleIsUnborn, @@ -190,6 +193,8 @@ class _ChildCardWidgetState extends State { hint: 'Nom de l\'enfant', ), SizedBox(height: 8.0 * scaleFactor), + _buildGenreField(context, config, scaleFactor), + SizedBox(height: 8.0 * scaleFactor), _buildField( config: config, scaleFactor: scaleFactor, @@ -352,6 +357,8 @@ class _ChildCardWidgetState extends State { const SizedBox(height: 12), _buildReadonlyField('Nom :', _lastNameController.text), const SizedBox(height: 12), + _buildReadonlyField('Genre :', _genreDisplayLabel(widget.childData.genre)), + const SizedBox(height: 12), _buildReadonlyField( widget.childData.isUnbornChild ? 'Date prévisionnelle :' : 'Date de naissance :', _dobController.text @@ -467,6 +474,8 @@ class _ChildCardWidgetState extends State { const SizedBox(height: 8), _buildReadonlyField('Nom :', _lastNameController.text), const SizedBox(height: 8), + _buildReadonlyField('Genre :', _genreDisplayLabel(widget.childData.genre)), + const SizedBox(height: 8), _buildReadonlyField( widget.childData.isUnbornChild ? 'Date prévisionnelle :' : 'Date de naissance :', _dobController.text @@ -556,6 +565,72 @@ class _ChildCardWidgetState extends State { ); } + static String _genreDisplayLabel(String g) { + switch (g) { + case 'H': + return 'Garçon'; + case 'F': + return 'Fille'; + case 'Autre': + return 'Autre / ne souhaite pas préciser'; + default: + return ''; + } + } + + Widget _buildGenreField(BuildContext context, DisplayConfig config, double scaleFactor) { + if (config.isReadonly) { + return FormFieldWrapper( + config: config, + label: 'Genre', + value: _genreDisplayLabel(widget.childData.genre), + ); + } + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + 'Genre *', + style: GoogleFonts.merienda( + fontSize: config.isMobile ? 12.0 : 18.0 * scaleFactor, + fontWeight: FontWeight.w600, + ), + ), + SizedBox(height: 4.0 * scaleFactor), + Container( + width: double.infinity, + padding: EdgeInsets.symmetric(horizontal: 14.0 * scaleFactor, vertical: 4.0), + decoration: BoxDecoration( + image: const DecorationImage( + image: AssetImage('assets/images/bg_beige.png'), + fit: BoxFit.fill, + ), + borderRadius: BorderRadius.circular(8 * scaleFactor), + ), + child: DropdownButtonHideUnderline( + child: DropdownButton( + isExpanded: true, + value: widget.childData.genre.isEmpty ? null : widget.childData.genre, + hint: Text('Choisir', style: GoogleFonts.merienda(fontSize: config.isMobile ? 13.0 : 16.0)), + style: GoogleFonts.merienda(fontSize: config.isMobile ? 13.0 : 16.0, color: Colors.black87), + items: [ + DropdownMenuItem(value: 'H', child: Text('Garçon', style: GoogleFonts.merienda())), + DropdownMenuItem(value: 'F', child: Text('Fille', style: GoogleFonts.merienda())), + DropdownMenuItem( + value: 'Autre', + child: Text('Autre / ne souhaite pas préciser', style: GoogleFonts.merienda()), + ), + ], + onChanged: (v) { + if (v != null) widget.onGenreChanged(v); + }, + ), + ), + ), + ], + ); + } + Widget _buildField({ required DisplayConfig config, required double scaleFactor,