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
@@ -3,11 +3,10 @@ import 'package:flutter/gestures.dart';
import 'package:google_fonts/google_fonts.dart';
import 'package:p_tits_pas/models/dossier_unifie.dart';
import 'package:p_tits_pas/utils/date_display_utils.dart';
import 'package:p_tits_pas/utils/phone_utils.dart';
import 'package:p_tits_pas/services/user_service.dart';
import 'package:p_tits_pas/services/api/api_config.dart';
import 'package:p_tits_pas/widgets/admin/common/admin_detail_modal.dart';
import 'package:p_tits_pas/widgets/admin/common/validation_detail_section.dart';
import 'package:p_tits_pas/widgets/common/identity_block.dart';
import 'package:p_tits_pas/widgets/common/auth_network_image.dart';
import 'validation_modal_theme.dart';
import 'validation_refus_form.dart';
@@ -108,26 +107,6 @@ class _ValidationFamilyWizardState extends State<ValidationFamilyWizard> {
static String _formatBirthDate(String? s) =>
formatIsoDateFr(s, ifEmpty: 'Non défini');
/// Même ordre et disposition que le formulaire de création (Nom/Prénom, Tél/Email, Adresse, CP/Ville).
List<AdminDetailField> _parentFields(ParentDossier p) => [
AdminDetailField(label: 'Nom', value: _v(p.nom)),
AdminDetailField(label: 'Prénom', value: _v(p.prenom)),
AdminDetailField(
label: 'Téléphone',
value: _v(p.telephone) != 'Non défini'
? formatPhoneForDisplay(_v(p.telephone))
: 'Non défini'),
AdminDetailField(label: 'Email', value: _v(p.email)),
AdminDetailField(label: 'Adresse (N° et Rue)', value: _v(p.adresse)),
AdminDetailField(label: 'Code postal', value: _v(p.codePostal)),
AdminDetailField(label: 'Ville', value: _v(p.ville)),
];
static const List<int> _parentRowLayout = [2, 2, 1, 2];
static const Map<int, List<int>> _parentRowFlex = {
3: [2, 5]
}; // Code postal étroit, Ville large
static String _fullPhotoUrl(String? url) => ApiConfig.absoluteMediaUrl(url);
@override
@@ -153,11 +132,9 @@ class _ValidationFamilyWizardState extends State<ValidationFamilyWizard> {
final d = widget.dossier;
switch (_step) {
case 0:
return ValidationDetailSection(
return IdentityBlock.readOnlyFromParentDossier(
d.parents.first,
title: 'Parent principal',
fields: _parentFields(d.parents.first),
rowLayout: _parentRowLayout,
rowFlex: _parentRowFlex,
);
case 1:
return _buildParent2Step();
@@ -178,11 +155,9 @@ class _ValidationFamilyWizardState extends State<ValidationFamilyWizard> {
style: TextStyle(color: Colors.black87)),
);
}
return ValidationDetailSection(
return IdentityBlock.readOnlyFromParentDossier(
widget.dossier.parents[1],
title: 'Deuxième parent',
fields: _parentFields(widget.dossier.parents[1]),
rowLayout: _parentRowLayout,
rowFlex: _parentRowFlex,
);
}