feat(#140): fiches admin famille — IdentityBlock, parsing enfants et avatars web

Extrait IdentityBlock réutilisable, aligne les modales parent/enfant sur le style validation, corrige le parsing parentChildren et les URLs /uploads en Flutter web.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-06-21 17:42:00 +02:00
co-authored by Cursor
parent 1dddc67933
commit 8494341b56
11 changed files with 1075 additions and 458 deletions
+4 -2
View File
@@ -89,8 +89,10 @@ class EnfantParentLink {
EnfantParentLink({required this.parentId, this.parentName});
factory EnfantParentLink.fromJson(Map<String, dynamic> json) {
final parent = json['parent'];
final parentId =
(json['parentId'] ?? json['id_parent'] ?? '').toString();
String? name;
final parent = json['parent'];
if (parent is Map<String, dynamic>) {
final user = parent['user'];
if (user is Map<String, dynamic>) {
@@ -99,7 +101,7 @@ class EnfantParentLink {
}
}
return EnfantParentLink(
parentId: (json['parentId'] ?? json['id_parent'] ?? '').toString(),
parentId: parentId,
parentName: name,
);
}
+32 -3
View File
@@ -23,9 +23,38 @@ class ParentChildSummary {
factory ParentChildSummary.fromJson(Map<String, dynamic> json) {
return ParentChildSummary(
id: (json['id'] ?? '').toString(),
firstName: json['first_name'] as String?,
lastName: json['last_name'] as String?,
status: (json['status'] ?? '').toString(),
firstName: (json['first_name'] ?? json['prenom'])?.toString(),
lastName: (json['last_name'] ?? json['nom'])?.toString(),
status: (json['status'] ?? json['statut'] ?? '').toString(),
);
}
/// Parse un lien `parentChildren` (objet enfant imbriqué ou id seul).
static ParentChildSummary? fromParentChildLink(Map<String, dynamic> link) {
final childRaw = link['child'] ?? link['enfant'];
if (childRaw is Map) {
return ParentChildSummary.fromJson(Map<String, dynamic>.from(childRaw));
}
if (link.containsKey('first_name') ||
link.containsKey('prenom') ||
link.containsKey('id')) {
final id = (link['id'] ?? '').toString();
if (id.isNotEmpty &&
(link.containsKey('first_name') || link.containsKey('prenom'))) {
return ParentChildSummary.fromJson(link);
}
}
final enfantId =
link['enfantId'] ?? link['id_enfant'] ?? link['enfant_id'];
if (enfantId != null && enfantId.toString().isNotEmpty) {
return ParentChildSummary(
id: enfantId.toString(),
status: '',
);
}
return null;
}
}
+31 -15
View File
@@ -13,28 +13,44 @@ class ParentModel {
});
factory ParentModel.fromJson(Map<String, dynamic> json) {
final userJson = Map<String, dynamic>.from(json['user'] ?? json);
if (json['numero_dossier'] != null && userJson['numero_dossier'] == null) {
userJson['numero_dossier'] = json['numero_dossier'];
final root = Map<String, dynamic>.from(json);
final userJson = Map<String, dynamic>.from(root['user'] ?? root);
if (root['numero_dossier'] != null && userJson['numero_dossier'] == null) {
userJson['numero_dossier'] = root['numero_dossier'];
}
final user = AppUser.fromJson(userJson);
final children = <ParentChildSummary>[];
final links = json['parentChildren'] as List?;
if (links != null) {
for (final link in links) {
if (link is Map<String, dynamic> && link['child'] is Map<String, dynamic>) {
children.add(
ParentChildSummary.fromJson(link['child'] as Map<String, dynamic>),
);
}
}
}
final children = _parseChildren(root);
final links = root['parentChildren'] ?? root['parent_children'];
final linkCount = links is List ? links.length : 0;
return ParentModel(
user: user,
childrenCount: children.isNotEmpty ? children.length : (links?.length ?? 0),
childrenCount: children.isNotEmpty ? children.length : linkCount,
children: children,
);
}
static List<ParentChildSummary> _parseChildren(Map<String, dynamic> json) {
final children = <ParentChildSummary>[];
final seen = <String>{};
void add(ParentChildSummary? child) {
if (child == null || child.id.isEmpty || seen.contains(child.id)) return;
seen.add(child.id);
children.add(child);
}
final links = json['parentChildren'] ?? json['parent_children'];
if (links is List) {
for (final link in links) {
if (link is! Map) continue;
add(ParentChildSummary.fromParentChildLink(
Map<String, dynamic>.from(link),
));
}
}
return children;
}
}