fix(inscription-am): étape 1/2 obligatoires alignées API (min 2 car., photo)

- Étape 1 : prénom/nom min 2 caractères (minPersonNameLength sur PersonalInfoFormScreen pour AM)

- Étape 2 : ville/pays naissance min 2 ; photo réelle + consentement obligatoires ; isStep2Complete strict

- registerAM : lieu naissance trim + toujours envoyer photo_base64 ; lieu_* non null si valide

- Étape 4 : blocage envoi si isRegistrationComplete faux

Made-with: Cursor
This commit is contained in:
2026-04-13 13:32:25 +02:00
parent b4b44cb1b9
commit d550e57cb8
6 changed files with 91 additions and 20 deletions
@@ -189,6 +189,20 @@ class _ProfessionalInfoFormScreenState extends State<ProfessionalInfoFormScreen>
super.dispose();
}
String? _validateBirthCity(String? v) {
final t = (v ?? '').trim();
if (t.isEmpty) return 'Ville requise';
if (t.length < 2) return 'Au moins 2 caractères';
return null;
}
String? _validateBirthCountry(String? v) {
final t = (v ?? '').trim();
if (t.isEmpty) return 'Pays requis';
if (t.length < 2) return 'Au moins 2 caractères';
return null;
}
String? _validatePlacesAvailable(String? v) {
if (v == null || v.isEmpty) return 'Places disponibles requises';
final n = int.tryParse(v);
@@ -281,7 +295,13 @@ class _ProfessionalInfoFormScreenState extends State<ProfessionalInfoFormScreen>
_applyPlaceNameFormat(_birthCityController);
_applyPlaceNameFormat(_birthCountryController);
if (_formKey.currentState!.validate()) {
if (_hasRealPhoto && !_photoConsent) {
if (!_hasRealPhoto) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Veuillez ajouter une photo de profil.')),
);
return;
}
if (!_photoConsent) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Veuillez accepter le consentement photo pour continuer.')),
);
@@ -295,8 +315,8 @@ class _ProfessionalInfoFormScreenState extends State<ProfessionalInfoFormScreen>
photoFilename: _photoFilename,
photoConsent: _photoConsent,
dateOfBirth: _selectedDate,
birthCity: _birthCityController.text,
birthCountry: _birthCountryController.text,
birthCity: _birthCityController.text.trim(),
birthCountry: _birthCountryController.text.trim(),
nir: normalizeNir(_nirController.text),
agrementNumber: _agrementController.text,
agreementDate: _selectedAgreementDate,
@@ -753,7 +773,7 @@ class _ProfessionalInfoFormScreenState extends State<ProfessionalInfoFormScreen>
label: 'Ville de naissance',
controller: _birthCityController,
hint: 'Ex. Ajaccio, Paris, Casablanca…',
validator: (v) => v!.isEmpty ? 'Ville requise' : null,
validator: _validateBirthCity,
focusNode: _birthCityFocus,
),
SizedBox(height: verticalSpacing),
@@ -762,7 +782,7 @@ class _ProfessionalInfoFormScreenState extends State<ProfessionalInfoFormScreen>
label: 'Pays de naissance',
controller: _birthCountryController,
hint: 'Ex. France, Maroc…',
validator: (v) => v!.isEmpty ? 'Pays requis' : null,
validator: _validateBirthCountry,
focusNode: _birthCountryFocus,
),
],
@@ -875,7 +895,7 @@ class _ProfessionalInfoFormScreenState extends State<ProfessionalInfoFormScreen>
label: 'Ville de naissance',
controller: _birthCityController,
hint: 'Ex. Ajaccio, Paris, Casablanca…',
validator: (v) => v!.isEmpty ? 'Ville requise' : null,
validator: _validateBirthCity,
focusNode: _birthCityFocus,
),
const SizedBox(height: 12),
@@ -885,7 +905,7 @@ class _ProfessionalInfoFormScreenState extends State<ProfessionalInfoFormScreen>
label: 'Pays de naissance',
controller: _birthCountryController,
hint: 'Ex. France, Maroc…',
validator: (v) => v!.isEmpty ? 'Pays requis' : null,
validator: _validateBirthCountry,
focusNode: _birthCountryFocus,
),
const SizedBox(height: 12),