import 'package:flutter/material.dart'; import 'package:p_tits_pas/models/enfant_admin_model.dart'; import 'package:p_tits_pas/services/user_service.dart'; /// Fiche enfant consultation / édition (ticket #138). class AdminChildDetailModal extends StatefulWidget { final EnfantAdminModel enfant; final VoidCallback? onSaved; const AdminChildDetailModal({ super.key, required this.enfant, this.onSaved, }); @override State createState() => _AdminChildDetailModalState(); } class _AdminChildDetailModalState extends State { late final TextEditingController _prenomCtrl; late final TextEditingController _nomCtrl; late final TextEditingController _birthCtrl; late final TextEditingController _dueCtrl; late String _status; late String _gender; late bool _consentPhoto; late bool _isMultiple; bool _dirty = false; bool _saving = false; static const _statuses = ['a_naitre', 'actif', 'scolarise']; static const _genders = ['H', 'F', 'Autre']; @override void initState() { super.initState(); final e = widget.enfant; _prenomCtrl = TextEditingController(text: e.firstName ?? ''); _nomCtrl = TextEditingController(text: e.lastName ?? ''); _birthCtrl = TextEditingController(text: e.birthDate ?? ''); _dueCtrl = TextEditingController(text: e.dueDate ?? ''); _status = _statuses.contains(e.status) ? e.status : 'actif'; _gender = _genders.contains(e.gender) ? e.gender! : 'H'; _consentPhoto = e.consentPhoto; _isMultiple = e.isMultiple; for (final c in [_prenomCtrl, _nomCtrl, _birthCtrl, _dueCtrl]) { c.addListener(_markDirty); } } @override void dispose() { for (final c in [_prenomCtrl, _nomCtrl, _birthCtrl, _dueCtrl]) { c.dispose(); } super.dispose(); } void _markDirty() { if (!_dirty) setState(() => _dirty = true); } Future _save() async { if (!_dirty) return; setState(() => _saving = true); try { await UserService.updateEnfant( enfantId: widget.enfant.id, body: { 'first_name': _prenomCtrl.text.trim(), 'last_name': _nomCtrl.text.trim(), 'status': _status, 'gender': _gender, if (_birthCtrl.text.trim().isNotEmpty) 'birth_date': _birthCtrl.text.trim(), if (_dueCtrl.text.trim().isNotEmpty) 'due_date': _dueCtrl.text.trim(), 'consent_photo': _consentPhoto, 'is_multiple': _isMultiple, }, ); if (!mounted) return; setState(() { _dirty = false; _saving = false; }); widget.onSaved?.call(); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Fiche enfant enregistrée')), ); } catch (e) { if (!mounted) return; setState(() => _saving = false); ScaffoldMessenger.of(context).showSnackBar( SnackBar(content: Text(e.toString().replaceFirst('Exception: ', ''))), ); } } @override Widget build(BuildContext context) { final parents = widget.enfant.parentLinks .map((l) => l.parentName ?? l.parentId) .where((s) => s.isNotEmpty) .join(', '); return Dialog( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 560, maxHeight: 640), child: Padding( padding: const EdgeInsets.all(18), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Row( children: [ Expanded( child: Text( widget.enfant.fullName, style: const TextStyle(fontSize: 18, fontWeight: FontWeight.w700), ), ), IconButton( onPressed: () => Navigator.of(context).pop(), icon: const Icon(Icons.close), ), ], ), if (parents.isNotEmpty) Padding( padding: const EdgeInsets.only(bottom: 8), child: Text('Responsables : $parents', style: const TextStyle(color: Colors.black54)), ), const Divider(), Expanded( child: SingleChildScrollView( child: Column( children: [ _rowField('Prénom', TextField(controller: _prenomCtrl, decoration: _decoration())), _rowField('Nom', TextField(controller: _nomCtrl, decoration: _decoration())), _rowDropdown( 'Statut', _status, _statuses.map((s) => MapEntry(s, _statusLabel(s))).toList(), (v) => setState(() { _status = v; _dirty = true; }), ), _rowDropdown( 'Genre', _gender, _genders.map((g) => MapEntry(g, g)).toList(), (v) => setState(() { _gender = v; _dirty = true; }), ), _rowField( 'Date naissance', TextField( controller: _birthCtrl, decoration: _decoration(hint: 'AAAA-MM-JJ'), ), ), _rowField( 'Date prévue', TextField( controller: _dueCtrl, decoration: _decoration(hint: 'AAAA-MM-JJ'), ), ), SwitchListTile( contentPadding: EdgeInsets.zero, title: const Text('Consentement photo'), value: _consentPhoto, onChanged: (v) => setState(() { _consentPhoto = v; _dirty = true; }), ), SwitchListTile( contentPadding: EdgeInsets.zero, title: const Text('Naissance multiple'), value: _isMultiple, onChanged: (v) => setState(() { _isMultiple = v; _dirty = true; }), ), ], ), ), ), Row( mainAxisAlignment: MainAxisAlignment.end, children: [ TextButton( onPressed: () => Navigator.of(context).pop(), child: const Text('Fermer'), ), const SizedBox(width: 8), ElevatedButton.icon( onPressed: !_dirty || _saving ? null : _save, icon: _saving ? const SizedBox( width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2), ) : const Icon(Icons.save), label: const Text('Sauvegarder'), ), ], ), ], ), ), ), ); } InputDecoration _decoration({String? hint}) { return InputDecoration(isDense: true, border: const OutlineInputBorder(), hintText: hint); } Widget _rowField(String label, Widget field) { return Padding( padding: const EdgeInsets.symmetric(vertical: 6), child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ SizedBox( width: 120, child: Padding( padding: const EdgeInsets.only(top: 12), child: Text(label, style: const TextStyle(fontWeight: FontWeight.w600)), ), ), Expanded(child: field), ], ), ); } Widget _rowDropdown( String label, String value, List> items, ValueChanged onChanged, ) { return Padding( padding: const EdgeInsets.symmetric(vertical: 6), child: Row( children: [ SizedBox( width: 120, child: Text(label, style: const TextStyle(fontWeight: FontWeight.w600)), ), Expanded( child: DropdownButtonFormField( value: items.any((e) => e.key == value) ? value : items.first.key, decoration: _decoration(), items: items .map((e) => DropdownMenuItem(value: e.key, child: Text(e.value))) .toList(), onChanged: (v) { if (v != null) onChanged(v); }, ), ), ], ), ); } String _statusLabel(String status) { switch (status) { case 'a_naitre': return 'À naître'; case 'actif': return 'Actif'; case 'scolarise': return 'Scolarisé'; default: return status; } } }