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
+17 -5
View File
@@ -367,7 +367,9 @@ class UserService {
}
final List<dynamic> data = jsonDecode(response.body);
return data.map((e) => ParentModel.fromJson(e)).toList();
return data
.map((e) => ParentModel.fromJson(Map<String, dynamic>.from(e as Map)))
.toList();
}
static Future<ParentModel> getParent(String userId) async {
@@ -379,7 +381,10 @@ class UserService {
final err = jsonDecode(response.body) as Map<String, dynamic>?;
throw Exception(_toStr(err?['message']) ?? 'Erreur chargement parent');
}
return ParentModel.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
final decoded = jsonDecode(response.body);
return ParentModel.fromJson(
Map<String, dynamic>.from(decoded is Map ? decoded : {}),
);
}
static Future<ParentModel> updateParentFiche({
@@ -394,7 +399,7 @@ class UserService {
if (response.statusCode != 200) {
throw Exception(_extractErrorMessage(response.body, 'Erreur mise à jour parent'));
}
return ParentModel.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
return _parentModelFromBody(response.body);
}
static Future<ParentModel> attachEnfantToParent({
@@ -410,7 +415,7 @@ class UserService {
if (response.statusCode != 200 && response.statusCode != 201) {
throw Exception(_extractErrorMessage(response.body, 'Erreur rattachement enfant'));
}
return ParentModel.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
return _parentModelFromBody(response.body);
}
static Future<ParentModel> detachEnfantFromParent({
@@ -429,7 +434,7 @@ class UserService {
if (response.body.isEmpty) {
return getParent(parentUserId);
}
return ParentModel.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
return _parentModelFromBody(response.body);
}
static Future<List<EnfantAdminModel>> getEnfants() async {
@@ -473,6 +478,13 @@ class UserService {
return EnfantAdminModel.fromJson(jsonDecode(response.body) as Map<String, dynamic>);
}
static ParentModel _parentModelFromBody(String body) {
final decoded = jsonDecode(body);
return ParentModel.fromJson(
Map<String, dynamic>.from(decoded is Map ? decoded : {}),
);
}
static String _extractErrorMessage(String body, String fallback) {
try {
final decoded = jsonDecode(body);