feat(frontend): inscription — UI cartes enfant, formatage noms, focus Tab

- Formulaire infos perso : ordre de tabulation explicite, Checkbox Material
  pour le consentement photo, formatage nom/prénom/ville à la perte de focus
  et à la soumission (name_format_utils).
- Carte enfant : cadre photo, croix, ombres, consentement ; formatage
  prénom/nom enfant ; normalisation avant passage étape 4.
- Asset photo_frame.png ; HoverReliefWidget clipBehavior.
- Ajustements payload / modèle / étapes inscription liés au parcours.

Made-with: Cursor
This commit is contained in:
2026-03-28 20:40:27 +01:00
parent a723412043
commit 110240b682
10 changed files with 740 additions and 264 deletions
@@ -11,6 +11,7 @@ import '../../models/card_assets.dart';
import '../../config/display_config.dart';
import 'package:provider/provider.dart';
import 'package:go_router/go_router.dart';
import 'package:p_tits_pas/utils/name_format_utils.dart';
class ParentRegisterStep3Screen extends StatefulWidget {
// final UserRegistrationData registrationData; // Supprimé
@@ -74,6 +75,22 @@ class _ParentRegisterStep3ScreenState extends State<ParentRegisterStep3Screen> {
super.dispose();
}
/// Même logique que nom / prénom parent : normalisation avant passage à l’étape suivante.
void _normalizeChildrenNamesAndGoToStep4(
BuildContext context,
UserRegistrationData registrationData,
) {
for (var i = 0; i < registrationData.children.length; i++) {
final c = registrationData.children[i];
final fn = formatPersonNameCase(c.firstName);
final ln = formatPersonNameCase(c.lastName);
if (fn != c.firstName || ln != c.lastName) {
registrationData.updateChild(i, c.copyWith(firstName: fn, lastName: ln));
}
}
context.go('/parent-register-step4');
}
void _scrollListener() {
if (!_scrollController.hasClients) return;
final position = _scrollController.position;
@@ -272,6 +289,10 @@ class _ParentRegisterStep3ScreenState extends State<ParentRegisterStep3Screen> {
childData: registrationData.children[index],
childIndex: index,
onPickImage: () => _pickImage(index, registrationData),
onClearImage: () => setState(() {
final c = registrationData.children[index];
registrationData.updateChild(index, c.copyWith(imageFile: null));
}),
onDateSelect: () => _selectDate(context, index, registrationData),
onFirstNameChanged: (value) => setState(() {
final c = registrationData.children[index];
@@ -281,17 +302,24 @@ class _ParentRegisterStep3ScreenState extends State<ParentRegisterStep3Screen> {
final c = registrationData.children[index];
registrationData.updateChild(index, c.copyWith(lastName: value));
}),
onGenreChanged: (value) => setState(() {
final c = registrationData.children[index];
registrationData.updateChild(index, c.copyWith(genre: value));
}),
onTogglePhotoConsent: (newValue) {
final oldChild = registrationData.children[index];
registrationData.updateChild(index, oldChild.copyWith(photoConsent: newValue));
},
onToggleMultipleBirth: (newValue) {
final oldChild = registrationData.children[index];
registrationData.updateChild(index, oldChild.copyWith(multipleBirth: newValue));
},
onToggleIsUnborn: (newValue) {
final oldChild = registrationData.children[index];
registrationData.updateChild(index, oldChild.copyWith(isUnbornChild: newValue));
var g = oldChild.genre;
if (!newValue && g == 'Autre') {
g = '';
}
registrationData.updateChild(
index,
oldChild.copyWith(isUnbornChild: newValue, genre: g),
);
},
onRemove: () => _removeChild(index, registrationData),
canBeRemoved: registrationData.children.length > 1,
@@ -314,7 +342,7 @@ class _ParentRegisterStep3ScreenState extends State<ParentRegisterStep3Screen> {
const SizedBox(height: 30),
// Boutons navigation en bas du scroll
_buildMobileButtons(context, config, screenSize),
_buildMobileButtons(context, config, screenSize, registrationData),
const SizedBox(height: 20),
],
),
@@ -368,6 +396,10 @@ class _ParentRegisterStep3ScreenState extends State<ParentRegisterStep3Screen> {
childData: registrationData.children[index],
childIndex: index,
onPickImage: () => _pickImage(index, registrationData),
onClearImage: () => setState(() {
final c = registrationData.children[index];
registrationData.updateChild(index, c.copyWith(imageFile: null));
}),
onDateSelect: () => _selectDate(context, index, registrationData),
onFirstNameChanged: (value) => setState(() {
final c = registrationData.children[index];
@@ -377,17 +409,24 @@ class _ParentRegisterStep3ScreenState extends State<ParentRegisterStep3Screen> {
final c = registrationData.children[index];
registrationData.updateChild(index, c.copyWith(lastName: value));
}),
onGenreChanged: (value) => setState(() {
final c = registrationData.children[index];
registrationData.updateChild(index, c.copyWith(genre: value));
}),
onTogglePhotoConsent: (newValue) {
final oldChild = registrationData.children[index];
registrationData.updateChild(index, oldChild.copyWith(photoConsent: newValue));
},
onToggleMultipleBirth: (newValue) {
final oldChild = registrationData.children[index];
registrationData.updateChild(index, oldChild.copyWith(multipleBirth: newValue));
},
onToggleIsUnborn: (newValue) {
final oldChild = registrationData.children[index];
registrationData.updateChild(index, oldChild.copyWith(isUnbornChild: newValue));
var g = oldChild.genre;
if (!newValue && g == 'Autre') {
g = '';
}
registrationData.updateChild(
index,
oldChild.copyWith(isUnbornChild: newValue, genre: g),
);
},
onRemove: () => _removeChild(index, registrationData),
canBeRemoved: registrationData.children.length > 1,
@@ -416,7 +455,12 @@ class _ParentRegisterStep3ScreenState extends State<ParentRegisterStep3Screen> {
}
/// Boutons navigation mobile
Widget _buildMobileButtons(BuildContext context, DisplayConfig config, Size screenSize) {
Widget _buildMobileButtons(
BuildContext context,
DisplayConfig config,
Size screenSize,
UserRegistrationData registrationData,
) {
return Row(
children: [
Expanded(
@@ -444,7 +488,7 @@ class _ParentRegisterStep3ScreenState extends State<ParentRegisterStep3Screen> {
text: 'Suivant',
style: NavigationButtonStyle.green,
onPressed: () {
context.go('/parent-register-step4');
_normalizeChildrenNamesAndGoToStep4(context, registrationData);
},
width: double.infinity,
height: 50,