Files
petitspas/frontend/lib/widgets/admin/user_management_panel.dart
T
jmartinandCursor 84e46162fd feat(#153): onglet permanent Dossiers — liste unifiée + pending.
Remplace l’onglet conditionnel « À valider » par un onglet Dossiers
(pending en haut, familles + AM en dessous), avec GET /dossiers,
recherche, et libellés pending NOM Prénom.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-24 19:41:44 +02:00

405 lines
12 KiB
Dart
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:flutter/material.dart';
import 'package:p_tits_pas/screens/administrateurs/creation/gestionnaires_create.dart';
import 'package:p_tits_pas/widgets/admin/admin_management_widget.dart';
import 'package:p_tits_pas/widgets/admin/am_dossier_create_modal.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/dossiers_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/parent_dossier_create_modal.dart';
import 'package:p_tits_pas/widgets/admin/parent_managmant_widget.dart';
class UserManagementPanel extends StatefulWidget {
/// Afficher l'onglet Administrateurs (sinon sans Administrateurs).
final bool showAdministrateursTab;
const UserManagementPanel({
super.key,
this.showAdministrateursTab = true,
});
@override
State<UserManagementPanel> createState() => _UserManagementPanelState();
}
class _UserManagementPanelState extends State<UserManagementPanel> {
int _subIndex = 0;
int _gestionnaireRefreshTick = 0;
int _parentRefreshTick = 0;
int _adminRefreshTick = 0;
int _enfantRefreshTick = 0;
int _amRefreshTick = 0;
int _dossiersRefreshTick = 0;
final TextEditingController _searchController = TextEditingController();
final TextEditingController _amCapacityController = TextEditingController();
String? _parentStatus;
String? _enfantStatus;
@override
void initState() {
super.initState();
_searchController.addListener(_onFilterChanged);
_amCapacityController.addListener(_onFilterChanged);
}
@override
void dispose() {
_searchController.removeListener(_onFilterChanged);
_amCapacityController.removeListener(_onFilterChanged);
_searchController.dispose();
_amCapacityController.dispose();
super.dispose();
}
void _onFilterChanged() {
if (!mounted) return;
setState(() {});
}
/// Ordre #153 : Dossiers | Parents | Enfants | AM | Gestionnaires | (Admin).
List<String> get _tabLabels {
const base = [
'Dossiers',
'Parents',
'Enfants',
'Assistantes maternelles',
'Gestionnaires',
];
if (widget.showAdministrateursTab) {
return [...base, 'Administrateurs'];
}
return base;
}
void _onSubTabChange(int index) {
final maxIndex = _tabLabels.length - 1;
setState(() {
_subIndex = index.clamp(0, maxIndex);
_searchController.clear();
_parentStatus = null;
_enfantStatus = null;
_amCapacityController.clear();
});
}
bool get _isDossiersTab => _subIndex == 0;
String _searchHintForTab() {
switch (_subIndex) {
case 0:
return 'Rechercher un dossier';
case 1:
return 'Rechercher un parent...';
case 2:
return 'Rechercher un enfant...';
case 3:
return 'Rechercher une assistante...';
case 4:
return 'Rechercher un gestionnaire...';
case 5:
return 'Rechercher un administrateur...';
default:
return 'Rechercher...';
}
}
String? _searchTooltipForTab() {
if (_subIndex != 0) return null;
return 'Recherche possible : n° de dossier, nom, prénom ou e-mail.';
}
Widget? _subBarFilterControl() {
// Parents
if (_subIndex == 1) {
return DropdownButtonHideUnderline(
child: DropdownButton<String?>(
value: _parentStatus,
isExpanded: true,
hint: const Padding(
padding: EdgeInsets.only(left: 10),
child: Text('Statut', style: TextStyle(fontSize: 12)),
),
items: const [
DropdownMenuItem<String?>(
value: null,
child: Padding(
padding: EdgeInsets.only(left: 10),
child: Text('Tous', style: TextStyle(fontSize: 12)),
),
),
DropdownMenuItem<String?>(
value: 'actif',
child: Padding(
padding: EdgeInsets.only(left: 10),
child: Text('Actif', style: TextStyle(fontSize: 12)),
),
),
DropdownMenuItem<String?>(
value: 'en_attente',
child: Padding(
padding: EdgeInsets.only(left: 10),
child: Text('En attente', style: TextStyle(fontSize: 12)),
),
),
DropdownMenuItem<String?>(
value: 'suspendu',
child: Padding(
padding: EdgeInsets.only(left: 10),
child: Text('Suspendu', style: TextStyle(fontSize: 12)),
),
),
DropdownMenuItem<String?>(
value: 'refuse',
child: Padding(
padding: EdgeInsets.only(left: 10),
child: Text('Refusé', style: TextStyle(fontSize: 12)),
),
),
],
onChanged: (value) {
setState(() {
_parentStatus = value;
});
},
),
);
}
// Enfants
if (_subIndex == 2) {
return DropdownButtonHideUnderline(
child: DropdownButton<String?>(
value: _enfantStatus,
isExpanded: true,
hint: const Padding(
padding: EdgeInsets.only(left: 10),
child: Text('Statut', style: TextStyle(fontSize: 12)),
),
items: const [
DropdownMenuItem<String?>(
value: null,
child: Padding(
padding: EdgeInsets.only(left: 10),
child: Text('Tous', style: TextStyle(fontSize: 12)),
),
),
DropdownMenuItem<String?>(
value: 'a_naitre',
child: Padding(
padding: EdgeInsets.only(left: 10),
child: Text('À naître', style: TextStyle(fontSize: 12)),
),
),
DropdownMenuItem<String?>(
value: 'sans_garde',
child: Padding(
padding: EdgeInsets.only(left: 10),
child: Text('Sans garde', style: TextStyle(fontSize: 12)),
),
),
DropdownMenuItem<String?>(
value: 'garde',
child: Padding(
padding: EdgeInsets.only(left: 10),
child: Text('Gardé', style: TextStyle(fontSize: 12)),
),
),
DropdownMenuItem<String?>(
value: 'scolarise',
child: Padding(
padding: EdgeInsets.only(left: 10),
child: Text('Scolarisé', style: TextStyle(fontSize: 12)),
),
),
],
onChanged: (value) {
setState(() {
_enfantStatus = value;
});
},
),
);
}
// AM
if (_subIndex == 3) {
return TextField(
controller: _amCapacityController,
decoration: const InputDecoration(
hintText: 'Capacité min',
hintStyle: TextStyle(fontSize: 12),
border: InputBorder.none,
isDense: true,
contentPadding: EdgeInsets.symmetric(horizontal: 10, vertical: 8),
),
keyboardType: TextInputType.number,
);
}
return null;
}
Widget _buildBody() {
switch (_subIndex) {
case 0:
return DossiersManagementWidget(
key: ValueKey('dossiers-$_dossiersRefreshTick'),
searchQuery: _searchController.text,
);
case 1:
return ParentManagementWidget(
key: ValueKey('parents-$_parentRefreshTick'),
searchQuery: _searchController.text,
statusFilter: _parentStatus,
);
case 2:
return EnfantManagementWidget(
key: ValueKey('enfants-$_enfantRefreshTick'),
searchQuery: _searchController.text,
statusFilter: _enfantStatus,
);
case 3:
return AssistanteMaternelleManagementWidget(
key: ValueKey('ams-$_amRefreshTick'),
searchQuery: _searchController.text,
capacityMin: int.tryParse(_amCapacityController.text),
);
case 4:
return GestionnaireManagementWidget(
key: ValueKey('gestionnaires-$_gestionnaireRefreshTick'),
searchQuery: _searchController.text,
);
case 5:
return AdminManagementWidget(
key: ValueKey('admins-$_adminRefreshTick'),
searchQuery: _searchController.text,
);
default:
return const Center(child: Text('Page non trouvée'));
}
}
@override
Widget build(BuildContext context) {
final labels = _tabLabels;
return Column(
children: [
DashboardUserManagementSubBar(
selectedSubIndex: _subIndex,
onSubTabChange: _onSubTabChange,
searchController: _searchController,
searchHint: _searchHintForTab(),
searchTooltip: _searchTooltipForTab(),
filterControl: _subBarFilterControl(),
// Pas de « Créer » sur longlet Dossiers (#153).
onAddPressed: _isDossiersTab ? null : _handleAddPressed,
addLabel: 'Ajouter',
subTabCount: labels.length,
tabLabels: labels,
),
Expanded(child: _buildBody()),
],
);
}
Future<void> _handleAddPressed() async {
// 1 Parents, 2 Enfants, 3 AM, 4 Gestionnaires, 5 Admin
if (_subIndex == 1) {
await showDialog<void>(
context: context,
barrierDismissible: false,
builder: (dialogContext) {
return ParentDossierCreateModal(
onClose: () => Navigator.of(dialogContext).pop(),
onSuccess: () {
Navigator.of(dialogContext).pop();
if (!mounted) return;
setState(() {
_parentRefreshTick++;
_dossiersRefreshTick++;
});
},
);
},
);
return;
}
if (_subIndex == 2) {
await showDialog<void>(
context: context,
barrierDismissible: false,
builder: (dialogContext) {
return AdminChildDetailModal.create(
onSaved: () {
if (!mounted) return;
setState(() => _enfantRefreshTick++);
},
);
},
);
return;
}
if (_subIndex == 3) {
await showDialog<void>(
context: context,
barrierDismissible: false,
builder: (dialogContext) {
return AmDossierCreateModal(
onClose: () => Navigator.of(dialogContext).pop(),
onSuccess: () {
Navigator.of(dialogContext).pop();
if (!mounted) return;
setState(() {
_amRefreshTick++;
_dossiersRefreshTick++;
});
},
);
},
);
return;
}
if (_subIndex == 4) {
final created = await showDialog<bool>(
context: context,
barrierDismissible: false,
builder: (dialogContext) {
return const AdminUserFormDialog();
},
);
if (!mounted) return;
if (created == true) {
setState(() {
_gestionnaireRefreshTick++;
});
}
return;
}
if (_subIndex == 5) {
final created = await showDialog<bool>(
context: context,
barrierDismissible: false,
builder: (dialogContext) {
return const AdminUserFormDialog(
adminMode: true,
withRelais: false,
);
},
);
if (!mounted) return;
if (created == true) {
setState(() {
_adminRefreshTick++;
});
}
}
}
}