feat(inscription AM #120): UI étape pro, photo unifiée, scripts et données test

- Panneau AM : validation NIR alignée API, date d’agrément requise côté app,
  capacité 1–10, n° agrément + date sur une ligne, ville/pays formatés au blur.
- Widget RegistrationPhotoSlot (cadre, croix) partagé avec les cartes enfant.
- AuthService : MIME PNG/JPEG pour la photo ; payload date_agrement.
- Scripts register-am-dubois / mansouri ; chemins tests/ressources/photos ;
  doc test-data + seed ; smoke curl inscription AM.

Made-with: Cursor
This commit is contained in:
2026-04-13 13:19:32 +02:00
parent 12bf85b7bd
commit 58607cdbc9
18 changed files with 763 additions and 284 deletions
+12 -10
View File
@@ -57,20 +57,22 @@ String formatNir(String raw) {
return '${r.substring(0, 1)} ${r.substring(1, 3)} ${r.substring(3, 5)} ${r.substring(5, 7)} ${r.substring(7, 10)} ${r.substring(10, 13)} - ${r.substring(13, 15)}';
}
/// Vérifie le format : 15 caractères, structure 1+2+2+2+3+3+2, département 2A/2B autorisé.
/// Aligné sur le backend (NIR côté API / nir.util.ts) : sexe 13, département 2A ou 2B pour la Corse.
bool _isFormatValid(String raw) {
if (raw.length != 15) return false;
final dept = raw.substring(5, 7);
final restDigits = raw.substring(0, 5) + (dept == '2A' ? '19' : dept == '2B' ? '18' : dept) + raw.substring(7, 15);
if (!RegExp(r'^[12]\d{12}\d{2}$').hasMatch(restDigits)) return false;
return RegExp(r'^[12]\d{4}(?:\d{2}|2A|2B)\d{8}$').hasMatch(raw);
final r = raw.toUpperCase();
if (r.length != 15) return false;
return RegExp(r'^[1-3]\d{4}(?:2A|2B|\d{2})\d{6}\d{2}$').hasMatch(r);
}
/// Calcule la clé de contrôle (97 - (NIR13 mod 97)). Pour 2A→19, 2B→18.
/// Clé INSEE : 97 - (NIR13 mod 97). Corse : 2A→19, 2B→20 (aligné backend).
int _controlKey(String raw13) {
String n = raw13;
if (raw13.length >= 7 && (raw13.substring(5, 7) == '2A' || raw13.substring(5, 7) == '2B')) {
n = raw13.substring(0, 5) + (raw13.substring(5, 7) == '2A' ? '19' : '18') + raw13.substring(7);
String n;
if (raw13.length >= 7 && raw13.substring(5, 7) == '2A') {
n = '${raw13.substring(0, 5)}19${raw13.substring(7)}';
} else if (raw13.length >= 7 && raw13.substring(5, 7) == '2B') {
n = '${raw13.substring(0, 5)}20${raw13.substring(7)}';
} else {
n = '${raw13.substring(0, 5)}${raw13.substring(5, 7)}${raw13.substring(7)}';
}
final big = int.tryParse(n);
if (big == null) return -1;