From d03a8e6c8b4363e9443e4a64711a7c68a21c5700 Mon Sep 17 00:00:00 2001 From: Julien Martin Date: Tue, 7 Jul 2026 23:11:24 +0200 Subject: [PATCH] fix(#131): activer Sauvegarder sur statut, enfants et champs AM. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Compare l'état de la fiche à un snapshot initial (liste enfants, statut, places, formulaire) et corrige la gélule statut pour les valeurs hors liste. Co-authored-by: Cursor --- .../admin/common/admin_am_edit_modal.dart | 111 +++++++++++++++--- .../admin/common/admin_status_capsule.dart | 7 +- 2 files changed, 100 insertions(+), 18 deletions(-) diff --git a/frontend/lib/widgets/admin/common/admin_am_edit_modal.dart b/frontend/lib/widgets/admin/common/admin_am_edit_modal.dart index 082356e..d536a29 100644 --- a/frontend/lib/widgets/admin/common/admin_am_edit_modal.dart +++ b/frontend/lib/widgets/admin/common/admin_am_edit_modal.dart @@ -1,3 +1,4 @@ +import 'package:flutter/foundation.dart'; import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:p_tits_pas/models/assistante_maternelle_model.dart'; @@ -57,6 +58,25 @@ class _AdminAmEditModalState extends State bool _saving = false; bool _dirty = false; + late String _baseStatut; + late bool _baseDisponible; + late int? _basePlacesAvailable; + late List _baseChildIds; + late String _baseNom; + late String _basePrenom; + late String _baseEmail; + late String _baseTelephone; + late String _baseAdresse; + late String _baseVille; + late String _baseCodePostal; + late String _baseAgrement; + late String _baseNir; + late String? _baseDateNaissanceIso; + late String _baseLieuNaissanceVille; + late String _baseLieuNaissancePays; + late String? _baseAgreementDateIso; + late int? _baseCapacite; + static const double _modalWidth = 930; static const double _photoProGap = 24; static const double _proColumnMinWidth = 260; @@ -147,16 +167,77 @@ class _AdminAmEditModalState extends State _nomCtrl.addListener(_onNameFieldChanged); _prenomCtrl.addListener(_onNameFieldChanged); _tabCtrl.addListener(_onTabChanged); + _captureBaseline(); _reloadChildren(); } + void _captureBaseline() { + _baseStatut = _statut; + _baseDisponible = _disponible; + _basePlacesAvailable = _placesAvailable; + _baseChildIds = _children.map((c) => c.id).toList()..sort(); + _baseNom = _nomCtrl.text.trim(); + _basePrenom = _prenomCtrl.text.trim(); + _baseEmail = _emailCtrl.text.trim(); + _baseTelephone = normalizePhone(_telCtrl.text); + _baseAdresse = _adresseCtrl.text.trim(); + _baseVille = _villeCtrl.text.trim(); + _baseCodePostal = _cpCtrl.text.trim(); + _baseAgrement = _agrementCtrl.text.trim(); + _baseNir = nirToRaw(_nirCtrl.text); + _baseDateNaissanceIso = _frDateToIso(_dateNaissanceCtrl.text); + _baseLieuNaissanceVille = _lieuNaissanceVilleCtrl.text.trim(); + _baseLieuNaissancePays = _lieuNaissancePaysCtrl.text.trim(); + _baseAgreementDateIso = _frDateToIso(_dateAgrementCtrl.text); + _baseCapacite = _capaciteMax(); + } + + bool _hasUnsavedChanges() { + if (_statut != _baseStatut) return true; + if (_disponible != _baseDisponible) return true; + if (_placesAvailable != _basePlacesAvailable) return true; + final childIds = _children.map((c) => c.id).toList()..sort(); + if (!listEquals(childIds, _baseChildIds)) return true; + if (_nomCtrl.text.trim() != _baseNom) return true; + if (_prenomCtrl.text.trim() != _basePrenom) return true; + if (_emailCtrl.text.trim() != _baseEmail) return true; + if (normalizePhone(_telCtrl.text) != _baseTelephone) return true; + if (_adresseCtrl.text.trim() != _baseAdresse) return true; + if (_villeCtrl.text.trim() != _baseVille) return true; + if (_cpCtrl.text.trim() != _baseCodePostal) return true; + if (_agrementCtrl.text.trim() != _baseAgrement) return true; + if (nirToRaw(_nirCtrl.text) != _baseNir) return true; + if (_frDateToIso(_dateNaissanceCtrl.text) != _baseDateNaissanceIso) { + return true; + } + if (_lieuNaissanceVilleCtrl.text.trim() != _baseLieuNaissanceVille) { + return true; + } + if (_lieuNaissancePaysCtrl.text.trim() != _baseLieuNaissancePays) { + return true; + } + if (_frDateToIso(_dateAgrementCtrl.text) != _baseAgreementDateIso) { + return true; + } + if (_capaciteMax() != _baseCapacite) return true; + return false; + } + + void _syncDirtyState() { + final next = _hasUnsavedChanges(); + if (next != _dirty) setState(() => _dirty = next); + } + void _onTabChanged() { if (!_tabCtrl.indexIsChanging) setState(() {}); } void _onNameFieldChanged() => setState(() {}); - void _onCapacityChanged() => setState(() {}); + void _onCapacityChanged() { + setState(() {}); + _syncDirtyState(); + } @override void dispose() { @@ -182,9 +263,7 @@ class _AdminAmEditModalState extends State super.dispose(); } - void _markDirty() { - if (!_dirty) setState(() => _dirty = true); - } + void _markDirty() => _syncDirtyState(); String _headerTitle() { final fn = _prenomCtrl.text.trim(); @@ -240,10 +319,8 @@ class _AdminAmEditModalState extends State void _applyPlacesCorrection() { final expected = _computedPlacesAvailable(); if (expected == null) return; - setState(() { - _placesAvailable = expected; - _dirty = true; - }); + setState(() => _placesAvailable = expected); + _syncDirtyState(); } String? _placesInconsistencyMessage() { @@ -318,6 +395,7 @@ class _AdminAmEditModalState extends State _dirty = false; _saving = false; }); + _captureBaseline(); widget.onSaved?.call(); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Fiche assistante maternelle enregistrée')), @@ -346,6 +424,7 @@ class _AdminAmEditModalState extends State if (!mounted) return; setState(() => _children = kids); + _syncDirtyState(); } catch (_) {} } @@ -574,10 +653,10 @@ class _AdminAmEditModalState extends State style: TextStyle(fontSize: 13), ), value: _disponible, - onChanged: (v) => setState(() { - _disponible = v; - _dirty = true; - }), + onChanged: (v) { + setState(() => _disponible = v); + _syncDirtyState(); + }, ), ], ), @@ -756,10 +835,10 @@ class _AdminAmEditModalState extends State width: 160, child: AdminStatusCapsule( statut: _statut, - onChanged: (v) => setState(() { - _statut = v; - _dirty = true; - }), + onChanged: (v) { + setState(() => _statut = v); + _syncDirtyState(); + }, ), ), IconButton( diff --git a/frontend/lib/widgets/admin/common/admin_status_capsule.dart b/frontend/lib/widgets/admin/common/admin_status_capsule.dart index a42b015..1e60375 100644 --- a/frontend/lib/widgets/admin/common/admin_status_capsule.dart +++ b/frontend/lib/widgets/admin/common/admin_status_capsule.dart @@ -30,7 +30,10 @@ class AdminStatusCapsule extends StatelessWidget { @override Widget build(BuildContext context) { - final value = statuts.contains(statut) ? statut : statuts.first; + final value = statut; + final items = statuts.contains(statut) + ? statuts + : [statut, ...statuts]; return Container( height: 34, decoration: BoxDecoration( @@ -46,7 +49,7 @@ class AdminStatusCapsule extends StatelessWidget { isDense: true, style: const TextStyle(fontSize: 13, color: Colors.black87), icon: const Icon(Icons.arrow_drop_down, size: 20), - items: statuts + items: items .map( (s) => DropdownMenuItem( value: s,