fix(#131): différer rattachement enfants AM jusqu'à Sauvegarder.

Rattachement et détachement restent locaux, recalculent places_available et sont persistés avec le PATCH fiche.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
MARTIN Julien 2026-07-07 23:36:06 +02:00
parent fbf22f2540
commit 18718670e9

View File

@ -53,6 +53,7 @@ class _AdminAmEditModalState extends State<AdminAmEditModal>
late bool _disponible; late bool _disponible;
late int? _placesAvailable; late int? _placesAvailable;
late List<ParentChildSummary> _children; late List<ParentChildSummary> _children;
late Set<String> _baselineChildIds;
bool _saving = false; bool _saving = false;
bool _dirty = false; bool _dirty = false;
@ -124,6 +125,7 @@ class _AdminAmEditModalState extends State<AdminAmEditModal>
_disponible = am.available ?? true; _disponible = am.available ?? true;
_placesAvailable = am.placesAvailable; _placesAvailable = am.placesAvailable;
_children = List.of(am.children); _children = List.of(am.children);
_baselineChildIds = _children.map((c) => c.id).toSet();
for (final c in [ for (final c in [
_nomCtrl, _nomCtrl,
@ -147,7 +149,18 @@ class _AdminAmEditModalState extends State<AdminAmEditModal>
_nomCtrl.addListener(_onNameFieldChanged); _nomCtrl.addListener(_onNameFieldChanged);
_prenomCtrl.addListener(_onNameFieldChanged); _prenomCtrl.addListener(_onNameFieldChanged);
_tabCtrl.addListener(_onTabChanged); _tabCtrl.addListener(_onTabChanged);
_reloadChildren(); _fetchChildrenFromServer();
}
bool _childrenChanged() {
final current = _children.map((c) => c.id).toSet();
return current.length != _baselineChildIds.length ||
!current.containsAll(_baselineChildIds);
}
void _syncPlacesAfterChildrenChange() {
final expected = _computedPlacesAvailable();
if (expected != null) _placesAvailable = expected;
} }
void _onTabChanged() { void _onTabChanged() {
@ -288,6 +301,22 @@ class _AdminAmEditModalState extends State<AdminAmEditModal>
setState(() => _saving = true); setState(() => _saving = true);
try { try {
if (_childrenChanged()) _syncPlacesAfterChildrenChange();
final currentIds = _children.map((c) => c.id).toSet();
for (final id in _baselineChildIds.difference(currentIds)) {
await UserService.detachEnfantFromAm(
amUserId: widget.assistante.user.id,
enfantId: id,
);
}
for (final id in currentIds.difference(_baselineChildIds)) {
await UserService.attachEnfantToAm(
amUserId: widget.assistante.user.id,
enfantId: id,
);
}
await UserService.updateAmFiche( await UserService.updateAmFiche(
amUserId: widget.assistante.user.id, amUserId: widget.assistante.user.id,
body: { body: {
@ -308,8 +337,7 @@ class _AdminAmEditModalState extends State<AdminAmEditModal>
if (_frDateToIso(_dateAgrementCtrl.text) != null) if (_frDateToIso(_dateAgrementCtrl.text) != null)
'agreement_date': _frDateToIso(_dateAgrementCtrl.text), 'agreement_date': _frDateToIso(_dateAgrementCtrl.text),
if (_capaciteMax() != null) 'max_children': _capaciteMax(), if (_capaciteMax() != null) 'max_children': _capaciteMax(),
if (_placesAvailable != widget.assistante.placesAvailable) if (_placesAvailable != null) 'places_available': _placesAvailable,
'places_available': _placesAvailable,
'available': _disponible, 'available': _disponible,
}, },
); );
@ -317,6 +345,7 @@ class _AdminAmEditModalState extends State<AdminAmEditModal>
setState(() { setState(() {
_dirty = false; _dirty = false;
_saving = false; _saving = false;
_baselineChildIds = currentIds;
}); });
widget.onSaved?.call(); widget.onSaved?.call();
ScaffoldMessenger.of(context).showSnackBar( ScaffoldMessenger.of(context).showSnackBar(
@ -331,7 +360,7 @@ class _AdminAmEditModalState extends State<AdminAmEditModal>
} }
} }
Future<void> _reloadChildren() async { Future<void> _fetchChildrenFromServer() async {
try { try {
final refreshed = final refreshed =
await UserService.getAssistanteMaternelle(widget.assistante.user.id); await UserService.getAssistanteMaternelle(widget.assistante.user.id);
@ -345,7 +374,26 @@ class _AdminAmEditModalState extends State<AdminAmEditModal>
}).toList(); }).toList();
if (!mounted) return; if (!mounted) return;
setState(() => _children = kids); setState(() {
_children = kids;
_baselineChildIds = kids.map((c) => c.id).toSet();
});
} catch (_) {}
}
Future<void> _refreshChildrenDetails() async {
try {
final all = await UserService.getEnfants();
final byId = {for (final e in all) e.id: e};
if (!mounted) return;
setState(() {
_children = _children.map((c) {
final full = byId[c.id];
if (full != null) return ParentChildSummary.fromEnfant(full);
return c;
}).toList();
});
} catch (_) {} } catch (_) {}
} }
@ -357,7 +405,7 @@ class _AdminAmEditModalState extends State<AdminAmEditModal>
context: context, context: context,
builder: (ctx) => AdminChildDetailModal( builder: (ctx) => AdminChildDetailModal(
enfant: enfant, enfant: enfant,
onSaved: _reloadChildren, onSaved: _refreshChildrenDetails,
), ),
); );
} catch (e) { } catch (e) {
@ -392,23 +440,11 @@ class _AdminAmEditModalState extends State<AdminAmEditModal>
); );
if (confirmed != true || !mounted) return; if (confirmed != true || !mounted) return;
try { setState(() {
await UserService.detachEnfantFromAm( _children = _children.where((c) => c.id != child.id).toList();
amUserId: widget.assistante.user.id, _syncPlacesAfterChildrenChange();
enfantId: child.id, _dirty = true;
); });
if (!mounted) return;
await _reloadChildren();
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Enfant détaché')),
);
} catch (e) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(e.toString().replaceFirst('Exception: ', ''))),
);
}
} }
Future<void> _attachChild() async { Future<void> _attachChild() async {
@ -450,23 +486,14 @@ class _AdminAmEditModalState extends State<AdminAmEditModal>
); );
if (selected == null || !mounted) return; if (selected == null || !mounted) return;
try { setState(() {
await UserService.attachEnfantToAm( _children = [
amUserId: widget.assistante.user.id, ..._children,
enfantId: selected.id, ParentChildSummary.fromEnfant(selected),
); ];
if (!mounted) return; _syncPlacesAfterChildrenChange();
await _reloadChildren(); _dirty = true;
if (!mounted) return; });
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Enfant rattaché')),
);
} catch (e) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(e.toString().replaceFirst('Exception: ', ''))),
);
}
} }