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>
279 lines
8.0 KiB
Dart
279 lines
8.0 KiB
Dart
import 'package:flutter/material.dart';
|
|
import 'package:p_tits_pas/models/dossier_unifie.dart';
|
|
import 'package:p_tits_pas/models/user.dart';
|
|
import 'package:p_tits_pas/utils/phone_utils.dart';
|
|
import 'package:p_tits_pas/widgets/admin/common/validation_detail_section.dart';
|
|
|
|
/// Valeurs affichées dans un [IdentityBlock] en lecture seule.
|
|
class IdentityValues {
|
|
final String nom;
|
|
final String prenom;
|
|
final String telephone;
|
|
final String email;
|
|
final String adresse;
|
|
final String codePostal;
|
|
final String ville;
|
|
|
|
const IdentityValues({
|
|
required this.nom,
|
|
required this.prenom,
|
|
required this.telephone,
|
|
required this.email,
|
|
required this.adresse,
|
|
required this.codePostal,
|
|
required this.ville,
|
|
});
|
|
|
|
static String _fmt(String? s, String empty) {
|
|
final t = (s ?? '').trim();
|
|
return t.isEmpty ? empty : t;
|
|
}
|
|
|
|
static String _fmtPhone(String? s, String empty) {
|
|
final tel = _fmt(s, empty);
|
|
return tel != empty ? formatPhoneForDisplay(tel) : tel;
|
|
}
|
|
|
|
factory IdentityValues.fromUser(
|
|
AppUser user, {
|
|
String emptyLabel = 'Non défini',
|
|
}) {
|
|
return IdentityValues(
|
|
nom: _fmt(user.nom, emptyLabel),
|
|
prenom: _fmt(user.prenom, emptyLabel),
|
|
telephone: _fmtPhone(user.telephone, emptyLabel),
|
|
email: _fmt(user.email, emptyLabel),
|
|
adresse: _fmt(user.adresse, emptyLabel),
|
|
codePostal: _fmt(user.codePostal, emptyLabel),
|
|
ville: _fmt(user.ville, emptyLabel),
|
|
);
|
|
}
|
|
|
|
factory IdentityValues.fromParentDossier(
|
|
ParentDossier parent, {
|
|
String emptyLabel = 'Non défini',
|
|
}) {
|
|
return IdentityValues(
|
|
nom: _fmt(parent.nom, emptyLabel),
|
|
prenom: _fmt(parent.prenom, emptyLabel),
|
|
telephone: _fmtPhone(parent.telephone, emptyLabel),
|
|
email: _fmt(parent.email, emptyLabel),
|
|
adresse: _fmt(parent.adresse, emptyLabel),
|
|
codePostal: _fmt(parent.codePostal, emptyLabel),
|
|
ville: _fmt(parent.ville, emptyLabel),
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Bloc identité : Nom/Prénom, Tél/Email, Adresse, CP/Ville.
|
|
/// Grille partagée (création de compte, validation AM/famille, fiches admin, etc.).
|
|
class IdentityBlock extends StatelessWidget { final String? title;
|
|
|
|
final String? _nom;
|
|
final String? _prenom;
|
|
final String? _telephone;
|
|
final String? _email;
|
|
final String? _adresse;
|
|
final String? _codePostal;
|
|
final String? _ville;
|
|
|
|
final TextEditingController? _nomCtrl;
|
|
final TextEditingController? _prenomCtrl;
|
|
final TextEditingController? _telCtrl;
|
|
final TextEditingController? _emailCtrl;
|
|
final TextEditingController? _adresseCtrl;
|
|
final TextEditingController? _cpCtrl;
|
|
final TextEditingController? _villeCtrl;
|
|
|
|
static const List<int> rowLayout = [2, 2, 1, 2];
|
|
static const Map<int, List<int>> rowFlex = {3: [2, 5]};
|
|
|
|
const IdentityBlock.readOnly({
|
|
super.key,
|
|
this.title,
|
|
required String nom,
|
|
required String prenom,
|
|
required String telephone,
|
|
required String email,
|
|
required String adresse,
|
|
required String codePostal,
|
|
required String ville,
|
|
}) : _nom = nom,
|
|
_prenom = prenom,
|
|
_telephone = telephone,
|
|
_email = email,
|
|
_adresse = adresse,
|
|
_codePostal = codePostal,
|
|
_ville = ville,
|
|
_nomCtrl = null,
|
|
_prenomCtrl = null,
|
|
_telCtrl = null,
|
|
_emailCtrl = null,
|
|
_adresseCtrl = null,
|
|
_cpCtrl = null,
|
|
_villeCtrl = null;
|
|
|
|
const IdentityBlock.editable({
|
|
super.key,
|
|
this.title,
|
|
required TextEditingController nomController,
|
|
required TextEditingController prenomController,
|
|
required TextEditingController telephoneController,
|
|
required TextEditingController emailController,
|
|
required TextEditingController adresseController,
|
|
required TextEditingController codePostalController,
|
|
required TextEditingController villeController,
|
|
}) : _nom = null,
|
|
_prenom = null,
|
|
_telephone = null,
|
|
_email = null,
|
|
_adresse = null,
|
|
_codePostal = null,
|
|
_ville = null,
|
|
_nomCtrl = nomController,
|
|
_prenomCtrl = prenomController,
|
|
_telCtrl = telephoneController,
|
|
_emailCtrl = emailController,
|
|
_adresseCtrl = adresseController,
|
|
_cpCtrl = codePostalController,
|
|
_villeCtrl = villeController;
|
|
|
|
/// Lecture seule à partir de [IdentityValues].
|
|
factory IdentityBlock.readOnlyValues({
|
|
Key? key,
|
|
String? title,
|
|
required IdentityValues values,
|
|
}) {
|
|
return IdentityBlock.readOnly(
|
|
key: key,
|
|
title: title,
|
|
nom: values.nom,
|
|
prenom: values.prenom,
|
|
telephone: values.telephone,
|
|
email: values.email,
|
|
adresse: values.adresse,
|
|
codePostal: values.codePostal,
|
|
ville: values.ville,
|
|
);
|
|
}
|
|
|
|
/// Lecture seule depuis un [AppUser] (validation AM, fiche AM, etc.).
|
|
factory IdentityBlock.readOnlyFromUser(
|
|
AppUser user, {
|
|
Key? key,
|
|
String? title,
|
|
String emptyLabel = 'Non défini',
|
|
}) {
|
|
return IdentityBlock.readOnlyValues(
|
|
key: key,
|
|
title: title,
|
|
values: IdentityValues.fromUser(user, emptyLabel: emptyLabel),
|
|
);
|
|
}
|
|
|
|
/// Lecture seule depuis un [ParentDossier] (validation famille).
|
|
factory IdentityBlock.readOnlyFromParentDossier(
|
|
ParentDossier parent, {
|
|
Key? key,
|
|
String? title,
|
|
String emptyLabel = 'Non défini',
|
|
}) {
|
|
return IdentityBlock.readOnlyValues(
|
|
key: key,
|
|
title: title,
|
|
values: IdentityValues.fromParentDossier(
|
|
parent,
|
|
emptyLabel: emptyLabel,
|
|
),
|
|
);
|
|
}
|
|
|
|
bool get _isEditable => _nomCtrl != null;
|
|
@override
|
|
Widget build(BuildContext context) {
|
|
if (_isEditable) {
|
|
return ValidationFormGrid(
|
|
title: title,
|
|
rowLayout: rowLayout,
|
|
rowFlex: rowFlex,
|
|
fields: [
|
|
ValidationLabeledField(
|
|
label: 'Nom',
|
|
field: ValidationEditableField(controller: _nomCtrl!),
|
|
),
|
|
ValidationLabeledField(
|
|
label: 'Prénom',
|
|
field: ValidationEditableField(controller: _prenomCtrl!),
|
|
),
|
|
ValidationLabeledField(
|
|
label: 'Téléphone',
|
|
field: ValidationEditableField(
|
|
controller: _telCtrl!,
|
|
keyboardType: TextInputType.phone,
|
|
inputFormatters: frenchPhoneInputFormatters,
|
|
),
|
|
),
|
|
ValidationLabeledField(
|
|
label: 'Email',
|
|
field: ValidationEditableField(
|
|
controller: _emailCtrl!,
|
|
keyboardType: TextInputType.emailAddress,
|
|
),
|
|
),
|
|
ValidationLabeledField(
|
|
label: 'Adresse (N° et Rue)',
|
|
field: ValidationEditableField(controller: _adresseCtrl!),
|
|
),
|
|
ValidationLabeledField(
|
|
label: 'Code postal',
|
|
field: ValidationEditableField(
|
|
controller: _cpCtrl!,
|
|
keyboardType: TextInputType.number,
|
|
),
|
|
),
|
|
ValidationLabeledField(
|
|
label: 'Ville',
|
|
field: ValidationEditableField(controller: _villeCtrl!),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
|
|
return ValidationFormGrid(
|
|
title: title,
|
|
rowLayout: rowLayout,
|
|
rowFlex: rowFlex,
|
|
fields: [
|
|
ValidationLabeledField(
|
|
label: 'Nom',
|
|
field: ValidationReadOnlyField(value: _nom!),
|
|
),
|
|
ValidationLabeledField(
|
|
label: 'Prénom',
|
|
field: ValidationReadOnlyField(value: _prenom!),
|
|
),
|
|
ValidationLabeledField(
|
|
label: 'Téléphone',
|
|
field: ValidationReadOnlyField(value: _telephone!),
|
|
),
|
|
ValidationLabeledField(
|
|
label: 'Email',
|
|
field: ValidationReadOnlyField(value: _email!),
|
|
),
|
|
ValidationLabeledField(
|
|
label: 'Adresse (N° et Rue)',
|
|
field: ValidationReadOnlyField(value: _adresse!),
|
|
),
|
|
ValidationLabeledField(
|
|
label: 'Code postal',
|
|
field: ValidationReadOnlyField(value: _codePostal!),
|
|
),
|
|
ValidationLabeledField(
|
|
label: 'Ville',
|
|
field: ValidationReadOnlyField(value: _ville!),
|
|
),
|
|
],
|
|
);
|
|
}
|
|
}
|