feat(#131): grille enfants AM, statuts garde et polish champs admin.

Aligne le front sur les statuts enfant back (garde/sans_garde), refond l’onglet Enfants accueillis en grille 2×2 avec correction des places, et unifie les champs validation éditables/lecture seule.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
2026-07-07 23:01:41 +02:00
co-authored by Cursor
parent 53721ffbb3
commit 66c7f22280
14 changed files with 557 additions and 131 deletions
@@ -148,11 +148,30 @@ class ValidationFieldDecoration {
);
}
static BoxDecoration container() {
static InputDecoration readOnly({bool error = false, bool compact = false}) {
final borderColor = error ? Colors.red.shade400 : Colors.grey.shade300;
final fillColor = error ? Colors.red.shade50 : Colors.grey.shade50;
return input(compact: compact).copyWith(
filled: true,
fillColor: fillColor,
enabledBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(6),
borderSide: BorderSide(color: borderColor),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(6),
borderSide: BorderSide(color: borderColor),
),
);
}
static BoxDecoration container({bool error = false}) {
return BoxDecoration(
color: Colors.grey.shade50,
color: error ? Colors.red.shade50 : Colors.grey.shade50,
borderRadius: BorderRadius.circular(6),
border: Border.all(color: Colors.grey.shade300),
border: Border.all(
color: error ? Colors.red.shade400 : Colors.grey.shade300,
),
);
}
}
@@ -234,20 +253,24 @@ class ValidationEditableField extends StatelessWidget {
@override
Widget build(BuildContext context) {
if (!compact || maxLines > 1) {
if (maxLines > 1) {
return TextField(
controller: controller,
keyboardType: keyboardType,
inputFormatters: inputFormatters,
maxLines: maxLines,
style: TextStyle(
color: Colors.black87,
fontSize: compact ? 13 : 14,
),
decoration: ValidationFieldDecoration.input(
hint: hintText,
compact: compact,
),
style: const TextStyle(color: Colors.black87, fontSize: 14),
decoration: ValidationFieldDecoration.input(hint: hintText),
);
}
if (!compact) {
return TextField(
controller: controller,
keyboardType: keyboardType,
inputFormatters: inputFormatters,
maxLines: 1,
style: const TextStyle(color: Colors.black87, fontSize: 14),
decoration: ValidationFieldDecoration.input(hint: hintText),
);
}
return SizedBox(
@@ -259,10 +282,11 @@ class ValidationEditableField extends StatelessWidget {
keyboardType: keyboardType,
inputFormatters: inputFormatters,
maxLines: 1,
textAlignVertical: TextAlignVertical.center,
style: const TextStyle(
color: Colors.black87,
fontSize: 13,
height: 1.15,
height: 1.0,
),
decoration: _compactInputDecoration(hint: hintText),
),
@@ -297,8 +321,8 @@ class ValidationEditableSection extends StatelessWidget {
}
}
/// Champ texte en lecture seule, style formulaire (fond gris léger, bordure).
class ValidationReadOnlyField extends StatelessWidget {
/// Champ texte en lecture seule, même coque [TextField] que [ValidationEditableField].
class ValidationReadOnlyField extends StatefulWidget {
final String value;
final int? maxLines;
final bool compact;
@@ -312,34 +336,69 @@ class ValidationReadOnlyField extends StatelessWidget {
this.error = false,
});
@override
State<ValidationReadOnlyField> createState() => _ValidationReadOnlyFieldState();
}
class _ValidationReadOnlyFieldState extends State<ValidationReadOnlyField> {
late final TextEditingController _controller;
static const double _compactFieldHeight = 34;
@override
void initState() {
super.initState();
_controller = TextEditingController(text: widget.value);
}
@override
void didUpdateWidget(ValidationReadOnlyField oldWidget) {
super.didUpdateWidget(oldWidget);
if (oldWidget.value != widget.value) {
_controller.text = widget.value;
}
}
@override
void dispose() {
_controller.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
if (!widget.compact && widget.maxLines == 1) {
return TextField(
controller: _controller,
readOnly: true,
enableInteractiveSelection: false,
style: TextStyle(
color: widget.error ? Colors.red.shade800 : Colors.black87,
fontSize: 14,
fontWeight: widget.error ? FontWeight.w600 : null,
),
decoration: ValidationFieldDecoration.readOnly(error: widget.error),
);
}
return Container(
width: double.infinity,
height: compact && maxLines == 1 ? _compactFieldHeight : null,
alignment: compact ? Alignment.centerLeft : null,
height: widget.compact && widget.maxLines == 1 ? _compactFieldHeight : null,
alignment: widget.compact ? Alignment.centerLeft : null,
padding: EdgeInsets.symmetric(
horizontal: compact ? 10 : 12,
vertical: compact ? 7 : 10,
horizontal: widget.compact ? 10 : 12,
vertical: widget.compact ? 7 : 10,
),
decoration: error
? BoxDecoration(
color: Colors.red.shade50,
borderRadius: BorderRadius.circular(6),
border: Border.all(color: Colors.red.shade400),
)
: ValidationFieldDecoration.container(),
decoration: ValidationFieldDecoration.container(error: widget.error),
child: Text(
value,
widget.value,
style: TextStyle(
color: error ? Colors.red.shade800 : Colors.black87,
fontSize: compact ? 13 : 14,
height: compact ? 1.2 : null,
fontWeight: error ? FontWeight.w600 : null,
color: widget.error ? Colors.red.shade800 : Colors.black87,
fontSize: widget.compact ? 13 : 14,
height: widget.compact ? 1.0 : null,
fontWeight: widget.error ? FontWeight.w600 : null,
),
maxLines: maxLines,
maxLines: widget.maxLines,
overflow: TextOverflow.ellipsis,
),
);