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