feat(#132): création enfant admin — modale + sélection famille.

Onglet Enfants « Ajouter » ouvre la fiche en mode création ; bloc Famille/dossier ; client POST /enfants avec parent_user_id (contrat back à livrer).

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
MARTIN Julien 2026-07-17 18:00:41 +02:00
parent 6fe2b89a61
commit c3acf1970d
4 changed files with 482 additions and 29 deletions

View File

@ -515,6 +515,32 @@ class UserService {
return enrichEnfantParentNames(enfant); return enrichEnfantParentNames(enfant);
} }
/// Création enfant côté staff (#132) nécessite `POST /enfants` étendu (voir mini-spec).
/// [parentUserId] : parent pivot du foyer (`parent_user_id`).
static Future<EnfantAdminModel> createEnfant({
required String parentUserId,
required Map<String, dynamic> body,
}) async {
final payload = <String, dynamic>{
...body,
'parent_user_id': parentUserId,
};
final response = await http.post(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.enfants}'),
headers: await _headers(),
body: jsonEncode(payload),
);
if (response.statusCode != 200 && response.statusCode != 201) {
throw Exception(
_extractErrorMessage(response.body, 'Erreur création enfant'),
);
}
final enfant = EnfantAdminModel.fromJson(
jsonDecode(response.body) as Map<String, dynamic>,
);
return enrichEnfantParentNames(enfant);
}
static Future<void> deleteEnfant(String enfantId) async { static Future<void> deleteEnfant(String enfantId) async {
final response = await http.delete( final response = await http.delete(
Uri.parse('${ApiConfig.baseUrl}${ApiConfig.enfants}/$enfantId'), Uri.parse('${ApiConfig.baseUrl}${ApiConfig.enfants}/$enfantId'),

View File

@ -10,22 +10,32 @@ import 'package:p_tits_pas/widgets/admin/common/admin_am_edit_modal.dart';
import 'package:p_tits_pas/widgets/admin/common/admin_am_photo_frame.dart'; import 'package:p_tits_pas/widgets/admin/common/admin_am_photo_frame.dart';
import 'package:p_tits_pas/widgets/admin/common/admin_parent_edit_modal.dart'; import 'package:p_tits_pas/widgets/admin/common/admin_parent_edit_modal.dart';
import 'package:p_tits_pas/widgets/admin/common/admin_select_am_modal.dart'; import 'package:p_tits_pas/widgets/admin/common/admin_select_am_modal.dart';
import 'package:p_tits_pas/widgets/admin/common/admin_select_famille_modal.dart';
import 'package:p_tits_pas/widgets/admin/common/validation_detail_section.dart'; import 'package:p_tits_pas/widgets/admin/common/validation_detail_section.dart';
import 'package:p_tits_pas/widgets/admin/validation_modal_theme.dart'; import 'package:p_tits_pas/widgets/admin/validation_modal_theme.dart';
import 'package:p_tits_pas/widgets/common/auth_network_image.dart'; import 'package:p_tits_pas/widgets/common/auth_network_image.dart';
/// Fiche enfant consultation / édition (ticket #138) format paysage comme fiche AM. /// Fiche enfant consultation / édition (#138) ou création (#132).
class AdminChildDetailModal extends StatefulWidget { class AdminChildDetailModal extends StatefulWidget {
final EnfantAdminModel enfant; final EnfantAdminModel? enfant;
final VoidCallback? onSaved; final VoidCallback? onSaved;
final VoidCallback? onDeleted; final VoidCallback? onDeleted;
final bool isCreating;
const AdminChildDetailModal({ const AdminChildDetailModal({
super.key, super.key,
required this.enfant, required EnfantAdminModel this.enfant,
this.onSaved, this.onSaved,
this.onDeleted, this.onDeleted,
}); }) : isCreating = false;
/// Création depuis l'onglet Enfants (#132).
const AdminChildDetailModal.create({
super.key,
this.onSaved,
}) : enfant = null,
onDeleted = null,
isCreating = true;
@override @override
State<AdminChildDetailModal> createState() => _AdminChildDetailModalState(); State<AdminChildDetailModal> createState() => _AdminChildDetailModalState();
@ -49,6 +59,9 @@ class _AdminChildDetailModalState extends State<AdminChildDetailModal> {
/// AM rattachée au chargement (pour sync différé au Sauvegarder). /// AM rattachée au chargement (pour sync différé au Sauvegarder).
String? _baselineAmUserId; String? _baselineAmUserId;
/// Famille choisie en mode création (#132).
AdminFamilleFoyer? _selectedFamily;
static const double _modalWidth = 930; static const double _modalWidth = 930;
static const double _mainRowHeight = 380; static const double _mainRowHeight = 380;
static const double _placementHeight = 96; static const double _placementHeight = 96;
@ -74,6 +87,7 @@ class _AdminChildDetailModalState extends State<AdminChildDetailModal> {
_isUnborn ? 'Date prévisionnelle' : 'Date de naissance'; _isUnborn ? 'Date prévisionnelle' : 'Date de naissance';
bool get _canDelete => bool get _canDelete =>
!widget.isCreating &&
(_currentUserRole ?? '').toLowerCase() == 'super_admin'; (_currentUserRole ?? '').toLowerCase() == 'super_admin';
bool get _busy => _saving || _deleting; bool get _busy => _saving || _deleting;
@ -82,36 +96,50 @@ class _AdminChildDetailModalState extends State<AdminChildDetailModal> {
void initState() { void initState() {
super.initState(); super.initState();
final e = widget.enfant; final e = widget.enfant;
_prenomCtrl = TextEditingController(text: e.firstName ?? ''); _prenomCtrl = TextEditingController(text: e?.firstName ?? '');
_nomCtrl = TextEditingController(text: e.lastName ?? ''); _nomCtrl = TextEditingController(text: e?.lastName ?? '');
_birthCtrl = TextEditingController( _birthCtrl = TextEditingController(
text: formatIsoDateFr(e.birthDate, ifEmpty: ''), text: formatIsoDateFr(e?.birthDate, ifEmpty: ''),
); );
_dueCtrl = TextEditingController( _dueCtrl = TextEditingController(
text: formatIsoDateFr(e.dueDate, ifEmpty: ''), text: formatIsoDateFr(e?.dueDate, ifEmpty: ''),
); );
_status = normalizeEnfantStatus(e.status); _status = normalizeEnfantStatus(e?.status);
if (!enfantStatusValues.contains(_status)) { if (!enfantStatusValues.contains(_status)) {
_status = 'sans_garde'; _status = 'sans_garde';
} }
_gender = _normalizeGender(e.gender, allowUnknown: _isUnborn); _gender = _normalizeGender(e?.gender, allowUnknown: _isUnborn);
_consentPhoto = e.consentPhoto; _consentPhoto = e?.consentPhoto ?? false;
_isMultiple = e.isMultiple; _isMultiple = e?.isMultiple ?? false;
for (final c in [_prenomCtrl, _nomCtrl, _birthCtrl, _dueCtrl]) { for (final c in [_prenomCtrl, _nomCtrl, _birthCtrl, _dueCtrl]) {
c.addListener(_markDirty); c.addListener(_markDirty);
} }
_prenomCtrl.addListener(_onNameChanged); _prenomCtrl.addListener(_onNameChanged);
_nomCtrl.addListener(_onNameChanged); _nomCtrl.addListener(_onNameChanged);
_loadCurrentUserRole(); _loadCurrentUserRole();
_loadLinkedAm(); if (widget.isCreating) {
_loadingAm = false;
_dirty = true;
} else {
_loadLinkedAm();
}
} }
void _onNameChanged() => setState(() {}); void _onNameChanged() => setState(() {});
Future<void> _loadLinkedAm() async { Future<void> _loadLinkedAm() async {
if (widget.isCreating) {
setState(() => _loadingAm = false);
return;
}
final enfantId = widget.enfant?.id;
if (enfantId == null || enfantId.isEmpty) {
setState(() => _loadingAm = false);
return;
}
setState(() => _loadingAm = true); setState(() => _loadingAm = true);
try { try {
final am = await UserService.findAmForEnfant(widget.enfant.id); final am = await UserService.findAmForEnfant(enfantId);
if (!mounted) return; if (!mounted) return;
setState(() { setState(() {
_linkedAm = am; _linkedAm = am;
@ -168,18 +196,25 @@ class _AdminChildDetailModalState extends State<AdminChildDetailModal> {
String? _dateToIso(String text) => parseFrDateToIso(text); String? _dateToIso(String text) => parseFrDateToIso(text);
String _headerTitle() { String _headerTitle() {
if (widget.isCreating) {
final fn = _prenomCtrl.text.trim();
final ln = _nomCtrl.text.trim();
if (fn.isEmpty && ln.isEmpty) return 'Nouvel enfant';
return '$fn $ln'.trim();
}
final fn = _prenomCtrl.text.trim(); final fn = _prenomCtrl.text.trim();
final ln = _nomCtrl.text.trim(); final ln = _nomCtrl.text.trim();
if (fn.isEmpty && ln.isEmpty) { if (fn.isEmpty && ln.isEmpty) {
final fallback = widget.enfant.fullName.trim(); final fallback = widget.enfant?.fullName.trim() ?? '';
return fallback.isNotEmpty ? fallback : 'Enfant'; return fallback.isNotEmpty ? fallback : 'Enfant';
} }
return '$fn $ln'.trim(); return '$fn $ln'.trim();
} }
List<EnfantParentLink> get _parentLinks => widget.enfant.parentLinks List<EnfantParentLink> get _parentLinks =>
.where((l) => l.parentId.trim().isNotEmpty) (widget.enfant?.parentLinks ?? const <EnfantParentLink>[])
.toList(); .where((l) => l.parentId.trim().isNotEmpty)
.toList();
Future<void> _openParent(EnfantParentLink link) async { Future<void> _openParent(EnfantParentLink link) async {
if (_busy) return; if (_busy) return;
@ -205,6 +240,17 @@ class _AdminChildDetailModalState extends State<AdminChildDetailModal> {
} }
Widget? _parentsSubtitle() { Widget? _parentsSubtitle() {
if (widget.isCreating) {
final family = _selectedFamily;
if (family == null) return null;
return Text(
family.parentNames.isNotEmpty
? 'Famille : ${family.parentNames.join(', ')}'
: family.displayTitle,
style: const TextStyle(fontSize: 13, color: Colors.black54),
);
}
final links = _parentLinks; final links = _parentLinks;
if (links.isEmpty) return null; if (links.isEmpty) return null;
@ -252,12 +298,21 @@ class _AdminChildDetailModalState extends State<AdminChildDetailModal> {
Future<void> _save() async { Future<void> _save() async {
if (!_dirty) return; if (!_dirty) return;
if (widget.isCreating) {
await _saveCreate();
return;
}
final enfantId = widget.enfant?.id;
if (enfantId == null || enfantId.isEmpty) return;
setState(() => _saving = true); setState(() => _saving = true);
try { try {
await _syncAmPlacement(); await _syncAmPlacement();
await UserService.updateEnfant( await UserService.updateEnfant(
enfantId: widget.enfant.id, enfantId: enfantId,
body: { body: {
'first_name': _prenomCtrl.text.trim(), 'first_name': _prenomCtrl.text.trim(),
'last_name': _nomCtrl.text.trim(), 'last_name': _nomCtrl.text.trim(),
@ -291,21 +346,86 @@ class _AdminChildDetailModalState extends State<AdminChildDetailModal> {
} }
} }
Future<void> _saveCreate() async {
final family = _selectedFamily;
if (family == null) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(
content: Text('Choisissez une famille / un dossier avant d\'enregistrer'),
),
);
return;
}
final prenom = _prenomCtrl.text.trim();
final nom = _nomCtrl.text.trim();
if (prenom.isEmpty || nom.isEmpty) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Prénom et nom sont obligatoires')),
);
return;
}
if (!_isUnborn && _dateToIso(_birthCtrl.text) == null) {
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Date de naissance invalide ou manquante')),
);
return;
}
setState(() => _saving = true);
try {
await UserService.createEnfant(
parentUserId: family.pivotParentUserId,
body: {
'first_name': prenom,
'last_name': nom,
'status': _status,
'gender': _gender,
if (_isUnborn)
if (_dateToIso(_dueCtrl.text) != null)
'due_date': _dateToIso(_dueCtrl.text)
else if (_dateToIso(_birthCtrl.text) != null)
'birth_date': _dateToIso(_birthCtrl.text),
'consent_photo': _consentPhoto,
'is_multiple': _isMultiple,
},
);
if (!mounted) return;
setState(() {
_dirty = false;
_saving = false;
});
widget.onSaved?.call();
Navigator.of(context).pop();
ScaffoldMessenger.of(context).showSnackBar(
const SnackBar(content: Text('Enfant créé')),
);
} catch (e) {
if (!mounted) return;
setState(() => _saving = false);
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text(e.toString().replaceFirst('Exception: ', ''))),
);
}
}
/// Applique rattachement / détachement AM (différé jusqu'au Sauvegarder). /// Applique rattachement / détachement AM (différé jusqu'au Sauvegarder).
Future<void> _syncAmPlacement() async { Future<void> _syncAmPlacement() async {
final enfantId = widget.enfant?.id;
if (enfantId == null || enfantId.isEmpty) return;
final baselineId = _baselineAmUserId; final baselineId = _baselineAmUserId;
final currentId = _linkedAm?.user.id; final currentId = _linkedAm?.user.id;
if (baselineId != null && baselineId != currentId) { if (baselineId != null && baselineId != currentId) {
await UserService.detachEnfantFromAm( await UserService.detachEnfantFromAm(
amUserId: baselineId, amUserId: baselineId,
enfantId: widget.enfant.id, enfantId: enfantId,
); );
} }
if (currentId != null && currentId != baselineId) { if (currentId != null && currentId != baselineId) {
await UserService.attachEnfantToAm( await UserService.attachEnfantToAm(
amUserId: currentId, amUserId: currentId,
enfantId: widget.enfant.id, enfantId: enfantId,
); );
} }
} }
@ -342,7 +462,9 @@ class _AdminChildDetailModalState extends State<AdminChildDetailModal> {
setState(() => _deleting = true); setState(() => _deleting = true);
try { try {
await UserService.deleteEnfant(widget.enfant.id); final id = widget.enfant?.id;
if (id == null || id.isEmpty) return;
await UserService.deleteEnfant(id);
if (!mounted) return; if (!mounted) return;
widget.onDeleted?.call(); widget.onDeleted?.call();
widget.onSaved?.call(); widget.onSaved?.call();
@ -376,10 +498,12 @@ class _AdminChildDetailModalState extends State<AdminChildDetailModal> {
/// Recharge AM + statut après une modale externe (ex. fiche AM). /// Recharge AM + statut après une modale externe (ex. fiche AM).
Future<void> _reloadPlacementFromServer() async { Future<void> _reloadPlacementFromServer() async {
final id = widget.enfant?.id;
if (id == null || id.isEmpty) return;
try { try {
final results = await Future.wait([ final results = await Future.wait([
UserService.findAmForEnfant(widget.enfant.id), UserService.findAmForEnfant(id),
UserService.getEnfant(widget.enfant.id), UserService.getEnfant(id),
]); ]);
if (!mounted) return; if (!mounted) return;
final am = results[0] as AssistanteMaternelleModel?; final am = results[0] as AssistanteMaternelleModel?;
@ -625,7 +749,7 @@ class _AdminChildDetailModalState extends State<AdminChildDetailModal> {
children: [ children: [
Expanded( Expanded(
child: AdminAmPhotoFrame( child: AdminAmPhotoFrame(
photoUrl: widget.enfant.photoUrl, photoUrl: widget.enfant?.photoUrl,
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
@ -893,8 +1017,10 @@ class _AdminChildDetailModalState extends State<AdminChildDetailModal> {
); );
} }
String get _placementTitle => String get _placementTitle {
_isScolarise ? 'Scolarisation' : 'Assistante maternelle'; if (widget.isCreating) return 'Famille / dossier';
return _isScolarise ? 'Scolarisation' : 'Assistante maternelle';
}
Widget _placementBlock() { Widget _placementBlock() {
return Column( return Column(
@ -910,11 +1036,161 @@ class _AdminChildDetailModalState extends State<AdminChildDetailModal> {
), ),
), ),
const SizedBox(height: 8), const SizedBox(height: 8),
_placementSection(), if (widget.isCreating) _familyPlacementSection() else _placementSection(),
], ],
); );
} }
Future<void> _pickFamily() async {
if (_busy) return;
final selected = await AdminSelectFamilleModal.show(
context,
title: 'Choisir une famille',
);
if (selected == null || !mounted) return;
setState(() {
_selectedFamily = selected;
_dirty = true;
});
}
Widget _familyPlacementSection() {
final family = _selectedFamily;
if (family == null) {
return Material(
color: Colors.transparent,
child: InkWell(
onTap: _busy ? null : _pickFamily,
borderRadius: BorderRadius.circular(10),
child: Container(
height: _placementHeight,
width: double.infinity,
decoration: BoxDecoration(
color: Colors.grey.shade50,
borderRadius: BorderRadius.circular(10),
border: Border.all(color: Colors.grey.shade300),
),
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
child: Row(
children: [
Container(
width: 56,
height: 56,
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.grey.shade200),
),
child: Icon(
Icons.family_restroom,
size: 30,
color: Colors.grey.shade400,
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'Aucune famille sélectionnée',
style: TextStyle(
fontSize: 15,
fontWeight: FontWeight.w700,
color: Colors.grey.shade800,
),
),
const SizedBox(height: 4),
Text(
'Cliquer pour choisir un dossier / foyer',
style: TextStyle(
fontSize: 13,
color: Colors.grey.shade600,
),
),
],
),
),
Icon(Icons.add_circle_outline, color: Colors.grey.shade500),
],
),
),
),
);
}
return Material(
color: Colors.transparent,
child: InkWell(
onTap: _busy ? null : _pickFamily,
borderRadius: BorderRadius.circular(10),
child: Container(
height: _placementHeight,
width: double.infinity,
decoration: BoxDecoration(
color: const Color(0xFFF8F5FC),
borderRadius: BorderRadius.circular(10),
border: Border.all(color: const Color(0xFFD8CCE8)),
),
padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),
child: Row(
children: [
Container(
width: 56,
height: 56,
decoration: BoxDecoration(
color: const Color(0xFFEDE5FA),
borderRadius: BorderRadius.circular(12),
),
child: const Icon(
Icons.family_restroom,
size: 30,
color: Color(0xFF6B3FA0),
),
),
const SizedBox(width: 16),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
family.displayTitle,
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w700,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
if (family.parentNames.isNotEmpty) ...[
const SizedBox(height: 4),
Text(
family.parentNames.join(', '),
style: const TextStyle(
fontSize: 13,
color: Colors.black54,
),
maxLines: 1,
overflow: TextOverflow.ellipsis,
),
],
],
),
),
IconButton(
icon: Icon(Icons.swap_horiz, color: Colors.grey.shade700),
tooltip: 'Changer de famille',
onPressed: _busy ? null : _pickFamily,
),
],
),
),
),
);
}
Widget _placementSection() { Widget _placementSection() {
if (!_showsAmPlacement) return _scolariseCard(); if (!_showsAmPlacement) return _scolariseCard();
@ -976,7 +1252,11 @@ class _AdminChildDetailModalState extends State<AdminChildDetailModal> {
color: Colors.white, color: Colors.white,
), ),
) )
: Text(_dirty ? 'Sauvegarder' : 'Aucune modification'), : Text(
widget.isCreating
? 'Créer'
: (_dirty ? 'Sauvegarder' : 'Aucune modification'),
),
), ),
], ],
); );

View File

@ -0,0 +1,127 @@
import 'package:flutter/material.dart';
import 'package:p_tits_pas/models/parent_model.dart';
import 'package:p_tits_pas/services/user_service.dart';
import 'package:p_tits_pas/widgets/admin/common/admin_select_list_modal.dart';
import 'package:p_tits_pas/widgets/admin/common/admin_user_card.dart';
/// Foyer / famille sélectionnable pour rattacher un nouvel enfant (#132).
class AdminFamilleFoyer {
/// Parent pivot pour `POST /enfants` (`parent_user_id`).
final String pivotParentUserId;
final String? numeroDossier;
final String displayTitle;
final List<String> parentNames;
const AdminFamilleFoyer({
required this.pivotParentUserId,
required this.displayTitle,
required this.parentNames,
this.numeroDossier,
});
String get subtitle {
final parts = <String>[];
final dossier = (numeroDossier ?? '').trim();
if (dossier.isNotEmpty) parts.add('Dossier $dossier');
if (parentNames.isNotEmpty) {
parts.add('Responsables : ${parentNames.join(', ')}');
}
return parts.join(' ');
}
}
/// Construit la liste des foyers uniques à partir de `GET /parents`.
List<AdminFamilleFoyer> buildFamilleFoyers(List<ParentModel> parents) {
final seenDossiers = <String>{};
final seenUserIds = <String>{};
final foyers = <AdminFamilleFoyer>[];
for (final p in parents) {
final dossier = (p.user.numeroDossier ?? '').trim();
if (dossier.isNotEmpty) {
if (seenDossiers.contains(dossier)) continue;
seenDossiers.add(dossier);
} else if (seenUserIds.contains(p.user.id)) {
continue;
}
seenUserIds.add(p.user.id);
final co = p.coParent;
if (co != null) seenUserIds.add(co.id);
final names = <String>[
if (p.user.fullName.trim().isNotEmpty) p.user.fullName.trim(),
if (co != null && co.fullName.trim().isNotEmpty) co.fullName.trim(),
];
final title = dossier.isNotEmpty
? 'Dossier $dossier'
: (names.isNotEmpty ? names.first : 'Famille');
foyers.add(
AdminFamilleFoyer(
pivotParentUserId: p.user.id,
numeroDossier: dossier.isNotEmpty ? dossier : null,
displayTitle: title,
parentNames: names,
),
);
}
foyers.sort(
(a, b) => a.displayTitle.toLowerCase().compareTo(b.displayTitle.toLowerCase()),
);
return foyers;
}
/// Sélection d'une famille / dossier pour créer un enfant — ticket #132.
class AdminSelectFamilleModal {
AdminSelectFamilleModal._();
static Future<AdminFamilleFoyer?> show(
BuildContext context, {
String title = 'Choisir une famille',
}) {
return AdminSelectListModal.show<AdminFamilleFoyer>(
context,
title: title,
searchHint: 'Rechercher par dossier, nom…',
emptyMessage: 'Aucune famille disponible',
noResultsMessage: 'Aucun résultat pour cette recherche',
loadItems: () async {
final parents = await UserService.getParents();
return buildFamilleFoyers(parents);
},
matchesQuery: (f, q) {
final dossier = (f.numeroDossier ?? '').toLowerCase();
final title = f.displayTitle.toLowerCase();
final names = f.parentNames.join(' ').toLowerCase();
return dossier.contains(q) || title.contains(q) || names.contains(q);
},
itemBuilder: (context, f, onSelect) {
return AdminUserCard(
title: f.displayTitle,
fallbackIcon: Icons.family_restroom,
subtitleLines: [
if (f.parentNames.isNotEmpty) f.parentNames.join(', '),
if ((f.numeroDossier ?? '').isNotEmpty)
'Dossier ${f.numeroDossier}',
],
onCardTap: onSelect,
margin: const EdgeInsets.only(bottom: 4),
contentPadding: const EdgeInsets.symmetric(
horizontal: 10,
vertical: 5,
),
actions: [
IconButton(
icon: const Icon(Icons.check_circle_outline),
tooltip: 'Choisir',
onPressed: onSelect,
),
],
);
},
);
}
}

View File

@ -3,6 +3,7 @@ import 'package:p_tits_pas/screens/administrateurs/creation/gestionnaires_create
import 'package:p_tits_pas/services/user_service.dart'; import 'package:p_tits_pas/services/user_service.dart';
import 'package:p_tits_pas/widgets/admin/admin_management_widget.dart'; import 'package:p_tits_pas/widgets/admin/admin_management_widget.dart';
import 'package:p_tits_pas/widgets/admin/assistante_maternelle_management_widget.dart'; import 'package:p_tits_pas/widgets/admin/assistante_maternelle_management_widget.dart';
import 'package:p_tits_pas/widgets/admin/common/admin_child_detail_modal.dart';
import 'package:p_tits_pas/widgets/admin/dashboard_admin.dart'; import 'package:p_tits_pas/widgets/admin/dashboard_admin.dart';
import 'package:p_tits_pas/widgets/admin/enfant_management_widget.dart'; import 'package:p_tits_pas/widgets/admin/enfant_management_widget.dart';
import 'package:p_tits_pas/widgets/admin/gestionnaire_management_widget.dart'; import 'package:p_tits_pas/widgets/admin/gestionnaire_management_widget.dart';
@ -26,6 +27,7 @@ class _UserManagementPanelState extends State<UserManagementPanel> {
int _subIndex = 0; int _subIndex = 0;
int _gestionnaireRefreshTick = 0; int _gestionnaireRefreshTick = 0;
int _adminRefreshTick = 0; int _adminRefreshTick = 0;
int _enfantRefreshTick = 0;
final TextEditingController _searchController = TextEditingController(); final TextEditingController _searchController = TextEditingController();
final TextEditingController _amCapacityController = TextEditingController(); final TextEditingController _amCapacityController = TextEditingController();
String? _parentStatus; String? _parentStatus;
@ -264,6 +266,7 @@ class _UserManagementPanelState extends State<UserManagementPanel> {
); );
case 1: case 1:
return EnfantManagementWidget( return EnfantManagementWidget(
key: ValueKey('enfants-$_enfantRefreshTick'),
searchQuery: _searchController.text, searchQuery: _searchController.text,
statusFilter: _enfantStatus, statusFilter: _enfantStatus,
); );
@ -311,6 +314,23 @@ class _UserManagementPanelState extends State<UserManagementPanel> {
Future<void> _handleAddPressed() async { Future<void> _handleAddPressed() async {
final contentIndex = _subIndex - _contentIndexOffset; final contentIndex = _subIndex - _contentIndexOffset;
if (contentIndex == 1) {
await showDialog<void>(
context: context,
barrierDismissible: false,
builder: (dialogContext) {
return AdminChildDetailModal.create(
onSaved: () {
if (!mounted) return;
setState(() => _enfantRefreshTick++);
},
);
},
);
return;
}
if (contentIndex == 3) { if (contentIndex == 3) {
final created = await showDialog<bool>( final created = await showDialog<bool>(
context: context, context: context,