feat(#138,#143,#144): fiche enfant admin, consentement photo et fix gestionnaire.

Squash merge develop → master.
- #138 : modale enfant paysage, zone AM/scolarisation, liens responsables
- #144 : persistance consent_photo à l'inscription enfant
- #143 : masquer Supprimer sur la propre fiche gestionnaire

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-17 13:21:43 +02:00
co-authored by Cursor
parent d6dac181d2
commit 1f8f1b9507
14 changed files with 1255 additions and 311 deletions
+67 -9
View File
@@ -37,6 +37,34 @@ class EnfantAdminModel {
return '$fn $ln';
}
EnfantAdminModel copyWith({
String? id,
String? firstName,
String? lastName,
String? gender,
String? birthDate,
String? dueDate,
String? status,
String? photoUrl,
bool? consentPhoto,
bool? isMultiple,
List<EnfantParentLink>? parentLinks,
}) {
return EnfantAdminModel(
id: id ?? this.id,
firstName: firstName ?? this.firstName,
lastName: lastName ?? this.lastName,
gender: gender ?? this.gender,
birthDate: birthDate ?? this.birthDate,
dueDate: dueDate ?? this.dueDate,
status: status ?? this.status,
photoUrl: photoUrl ?? this.photoUrl,
consentPhoto: consentPhoto ?? this.consentPhoto,
isMultiple: isMultiple ?? this.isMultiple,
parentLinks: parentLinks ?? this.parentLinks,
);
}
factory EnfantAdminModel.fromJson(Map<String, dynamic> json) {
final linksRaw = json['parentLinks'] as List?;
final links = <EnfantParentLink>[];
@@ -48,17 +76,25 @@ class EnfantAdminModel {
}
}
final photoUrl = json['photo_url'] as String? ?? json['photoUrl'] as String?;
final consentPhoto = _parseBool(json['consent_photo']) ||
_parseBool(json['consentement_photo']) ||
_parseBool(json['consentPhoto']);
return EnfantAdminModel(
id: (json['id'] ?? '').toString(),
firstName: json['first_name'] as String?,
lastName: json['last_name'] as String?,
gender: json['gender'] as String?,
birthDate: _dateString(json['birth_date']),
dueDate: _dateString(json['due_date']),
status: normalizeEnfantStatus(json['status']?.toString()),
photoUrl: json['photo_url'] as String?,
consentPhoto: json['consent_photo'] == true,
isMultiple: json['is_multiple'] == true,
firstName: json['first_name'] as String? ?? json['prenom'] as String?,
lastName: json['last_name'] as String? ?? json['nom'] as String?,
gender: json['gender'] as String? ?? json['genre'] as String?,
birthDate: _dateString(json['birth_date'] ?? json['date_naissance']),
dueDate: _dateString(json['due_date'] ?? json['date_prevue_naissance']),
status: normalizeEnfantStatus(
(json['status'] ?? json['statut'])?.toString(),
),
photoUrl: photoUrl,
consentPhoto: consentPhoto,
isMultiple: _parseBool(json['is_multiple']) ||
_parseBool(json['est_multiple']),
parentLinks: links,
);
}
@@ -76,6 +112,15 @@ class EnfantAdminModel {
};
}
static bool _parseBool(dynamic value) {
if (value == true || value == 1) return true;
if (value is String) {
final s = value.trim().toLowerCase();
return s == 'true' || s == '1' || s == 't' || s == 'yes';
}
return false;
}
static String? _dateString(dynamic v) {
if (v == null) return null;
if (v is String) return v.split('T').first;
@@ -89,6 +134,13 @@ class EnfantParentLink {
EnfantParentLink({required this.parentId, this.parentName});
EnfantParentLink copyWith({String? parentId, String? parentName}) {
return EnfantParentLink(
parentId: parentId ?? this.parentId,
parentName: parentName ?? this.parentName,
);
}
factory EnfantParentLink.fromJson(Map<String, dynamic> json) {
final parentId =
(json['parentId'] ?? json['id_parent'] ?? '').toString();
@@ -99,6 +151,12 @@ class EnfantParentLink {
if (user is Map<String, dynamic>) {
final u = AppUser.fromJson(user);
name = u.fullName.isNotEmpty ? u.fullName : u.email;
} else {
// Parfois le parent est aplati (prenom/nom) sans nested user.
final prenom = (parent['prenom'] ?? parent['first_name'] ?? '').toString().trim();
final nom = (parent['nom'] ?? parent['last_name'] ?? '').toString().trim();
final flat = '$prenom $nom'.trim();
if (flat.isNotEmpty) name = flat;
}
}
return EnfantParentLink(