import 'package:flutter/material.dart'; import 'package:p_tits_pas/models/dossier_list_item.dart'; import 'package:p_tits_pas/services/user_service.dart'; import 'package:p_tits_pas/widgets/admin/dossier_list_card.dart'; import 'package:p_tits_pas/widgets/admin/pending_validation_widget.dart'; import 'package:p_tits_pas/widgets/admin/validation_dossier_modal.dart'; /// Onglet permanent « Dossiers » (#153) : pending en haut + liste unifiée en bas. class DossiersManagementWidget extends StatefulWidget { final String searchQuery; final VoidCallback? onRefresh; const DossiersManagementWidget({ super.key, this.searchQuery = '', this.onRefresh, }); @override State createState() => _DossiersManagementWidgetState(); } class _DossiersManagementWidgetState extends State { bool _loading = true; String? _error; List _all = []; Set _pendingNumeros = {}; int _pendingRefreshTick = 0; @override void initState() { super.initState(); _loadAll(); } Future _loadAll() async { setState(() { _loading = true; _error = null; }); try { final parents = await UserService.getParents(); final ams = await UserService.getAssistantesMaternelles(); if (!mounted) return; final items = [ ...DossierListItem.fromParents(parents), ...DossierListItem.fromAssistantes(ams), ]; items.sort((a, b) { final byNum = a.numeroDossier.compareTo(b.numeroDossier); if (byNum != 0) return byNum; return a.typeLabel.compareTo(b.typeLabel); }); setState(() { _all = items; _loading = false; }); } catch (e) { if (!mounted) return; setState(() { _error = e is Exception ? e.toString().replaceFirst('Exception: ', '') : 'Erreur inconnue'; _loading = false; }); } } Future _refreshEverything() async { setState(() => _pendingRefreshTick++); await _loadAll(); widget.onRefresh?.call(); } void _openDossier(String numeroDossier) { final num = numeroDossier.trim(); if (num.isEmpty) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('Numéro de dossier manquant.')), ); return; } showDialog( context: context, builder: (context) => ValidationDossierModal( numeroDossier: num, onClose: () => Navigator.of(context).pop(), onSuccess: () { Navigator.of(context).pop(); _refreshEverything(); }, ), ); } @override Widget build(BuildContext context) { final query = widget.searchQuery; // Pending uniquement en haut — exclus de « Tous les dossiers ». final filtered = _all .where((d) => !_pendingNumeros.contains(d.numeroDossier)) .where((d) => (d.statut ?? '').toLowerCase() != 'en_attente') .where((d) => d.matchesQuery(query)) .toList(growable: false); return RefreshIndicator( onRefresh: _refreshEverything, child: CustomScrollView( physics: const AlwaysScrollableScrollPhysics(), slivers: [ SliverToBoxAdapter( child: PendingValidationWidget( key: ValueKey('pending-$_pendingRefreshTick'), searchQuery: query, compactWhenEmpty: true, onPendingNumerosChanged: (nums) { if (!mounted) return; setState(() => _pendingNumeros = nums); }, onRefresh: () { _loadAll(); widget.onRefresh?.call(); }, ), ), SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.fromLTRB(16, 8, 16, 8), child: Text( 'Tous les dossiers', style: Theme.of(context).textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w600, color: Colors.black87, ), ), ), ), if (_loading) const SliverFillRemaining( hasScrollBody: false, child: Center(child: CircularProgressIndicator()), ) else if (_error != null && _error!.isNotEmpty) SliverFillRemaining( hasScrollBody: false, child: Center( child: Padding( padding: const EdgeInsets.all(24), child: Column( mainAxisSize: MainAxisSize.min, children: [ Text(_error!, style: const TextStyle(color: Colors.red)), const SizedBox(height: 16), ElevatedButton( onPressed: _loadAll, child: const Text('Réessayer'), ), ], ), ), ), ) else if (filtered.isEmpty) SliverToBoxAdapter( child: Padding( padding: const EdgeInsets.fromLTRB(24, 12, 24, 32), child: Text( query.trim().isEmpty ? 'Aucun dossier pour le moment.\n' 'Pour créer un dossier → onglet Parents (+ Parents) ' 'ou Assistantes maternelles (+ Asmat).' : 'Aucun dossier ne correspond à la recherche.', textAlign: TextAlign.center, style: TextStyle(color: Colors.grey.shade600, height: 1.4), ), ), ) else SliverPadding( padding: const EdgeInsets.fromLTRB(16, 0, 16, 24), sliver: SliverList( delegate: SliverChildBuilderDelegate( (context, index) { final item = filtered[index]; return DossierListCard( numeroDossier: item.numeroDossier, namesLine: item.namesLine, isFamille: item.isFamille, onOpen: () => _openDossier(item.numeroDossier), ); }, childCount: filtered.length, ), ), ), ], ), ); } }