import "package:flutter/material.dart"; import "package:google_fonts/google_fonts.dart"; import "../demo_identity.dart"; import "../theme/ensup_colors.dart"; import "../models/emprunt_item.dart"; import "../services/emprunt_service.dart"; import "../services/materiel_service.dart"; import "../widgets/ensup_top_bar.dart"; import "../widgets/brand_corners.dart"; import "../widgets/app_message.dart"; import "checklist_retour_screen.dart"; /// Sélection de l'emprunt en cours à restituer (RG15 : ses propres emprunts). class RestitutionSelectScreen extends StatefulWidget { const RestitutionSelectScreen({super.key}); @override State createState() => _RestitutionSelectScreenState(); } class _RestitutionSelectScreenState extends State { late Future> _future; int? _chargementId; @override void initState() { super.initState(); _future = EmpruntService.getMesEmprunts(); } void _recharger() { setState(() => _future = EmpruntService.getMesEmprunts()); } Future _restituer(EmpruntItem emprunt) async { setState(() => _chargementId = emprunt.id); try { final detail = await MaterielService.getDetail(emprunt.materiel.id); if (!mounted) { return; } Navigator.of(context).push( MaterialPageRoute( builder: (_) => ChecklistRetourScreen( emprunt: emprunt.copyWith(materiel: detail), ), ), ); } catch (error) { if (!mounted) { return; } AppMessage.erreur(context, error); } finally { if (mounted) { setState(() => _chargementId = null); } } } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: EnsupColors.soft, body: Stack( children: [ Center( child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 1280, maxHeight: 820), child: Container( margin: const EdgeInsets.all(24), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: EnsupColors.text.withValues(alpha: 0.14), blurRadius: 60, offset: const Offset(0, 18), ), ], ), clipBehavior: Clip.antiAlias, child: Column( children: [ EnsupTopBar( brandText: "Restitution", campusTag: DemoIdentity.campusTag, userName: DemoIdentity.userName, userInitials: DemoIdentity.userInitials, onBack: () => Navigator.of(context).pop(), ), Expanded( child: Padding( padding: const EdgeInsets.symmetric( horizontal: 32, vertical: 28, ), child: FutureBuilder>( future: _future, builder: (context, snapshot) { if (snapshot.connectionState == ConnectionState.waiting) { return const Center( child: CircularProgressIndicator(), ); } if (snapshot.hasError) { return _ErreurState( message: snapshot.error.toString(), onRetry: _recharger, ); } return _contenu(snapshot.data ?? const []); }, ), ), ), ], ), ), ), ), const Positioned.fill(child: BrandCorners()), ], ), ); } Widget _contenu(List emprunts) { return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( "Sélectionner le matériel à restituer", style: GoogleFonts.darkerGrotesque( fontSize: 28, height: 1, fontWeight: FontWeight.w900, color: EnsupColors.text, ), ), const SizedBox(height: 6), Text( "${emprunts.length} emprunt${emprunts.length > 1 ? "s" : ""} en cours associé${emprunts.length > 1 ? "s" : ""} à votre compte.", style: GoogleFonts.titilliumWeb( fontSize: 13, color: EnsupColors.muted, ), ), const SizedBox(height: 18), Expanded( child: emprunts.isEmpty ? const _EmptyState() : ListView.separated( itemCount: emprunts.length, separatorBuilder: (_, _) => const SizedBox(height: 10), itemBuilder: (context, index) { final emprunt = emprunts[index]; return _EmpruntCard( emprunt: emprunt, loading: _chargementId == emprunt.id, onRestituer: () => _restituer(emprunt), ); }, ), ), ], ); } } class _EmpruntCard extends StatelessWidget { const _EmpruntCard({ required this.emprunt, required this.loading, required this.onRestituer, }); final EmpruntItem emprunt; final bool loading; final VoidCallback onRestituer; @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(horizontal: 18, vertical: 16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(12), border: Border.all(color: EnsupColors.line, width: 1.5), boxShadow: [ BoxShadow( color: EnsupColors.text.withValues(alpha: 0.035), blurRadius: 10, offset: const Offset(0, 3), ), ], ), child: LayoutBuilder( builder: (context, constraints) { final compact = constraints.maxWidth < 650; final information = Row( children: [ Container( width: 46, height: 46, decoration: BoxDecoration( color: EnsupColors.teal.withValues(alpha: 0.09), borderRadius: BorderRadius.circular(10), ), child: Icon( emprunt.materiel.icon, color: EnsupColors.teal, size: 21, ), ), const SizedBox(width: 14), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( emprunt.materiel.nom, maxLines: 2, overflow: TextOverflow.ellipsis, style: GoogleFonts.darkerGrotesque( fontWeight: FontWeight.w800, fontSize: 17, height: 1.05, color: EnsupColors.text, ), ), const SizedBox(height: 4), Text( "Emprunté le ${emprunt.dateEmprunt} · Retour prévu ${emprunt.retourPrevu}", maxLines: 2, overflow: TextOverflow.ellipsis, style: GoogleFonts.titilliumWeb( fontSize: 12, color: EnsupColors.muted, ), ), ], ), ), ], ); final actions = Wrap( spacing: 10, runSpacing: 8, crossAxisAlignment: WrapCrossAlignment.center, alignment: WrapAlignment.end, children: [ Container( padding: const EdgeInsets.symmetric( horizontal: 11, vertical: 5, ), decoration: BoxDecoration( color: emprunt.statut == "EN_RETARD" ? EnsupColors.red.withValues(alpha: 0.09) : const Color(0xFFFEF9C3), borderRadius: BorderRadius.circular(999), ), child: Text( emprunt.statut == "EN_RETARD" ? "En retard" : "En cours", style: GoogleFonts.darkerGrotesque( fontSize: 13, fontWeight: FontWeight.w700, color: emprunt.statut == "EN_RETARD" ? EnsupColors.red : const Color(0xFFA16207), ), ), ), FilledButton.icon( onPressed: loading ? null : onRestituer, style: FilledButton.styleFrom( backgroundColor: EnsupColors.cyan, padding: const EdgeInsets.symmetric( horizontal: 15, vertical: 11, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(9), ), ), icon: loading ? const SizedBox( width: 15, height: 15, child: CircularProgressIndicator( strokeWidth: 2, color: Colors.white, ), ) : const Icon(Icons.assignment_return_outlined, size: 16), label: Text( loading ? "Chargement" : "Restituer", style: GoogleFonts.darkerGrotesque( fontWeight: FontWeight.w700, fontSize: 14, ), ), ), ], ); if (compact) { return Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ information, const SizedBox(height: 14), Align(alignment: Alignment.centerRight, child: actions), ], ); } return Row( children: [ Expanded(child: information), const SizedBox(width: 16), actions, ], ); }, ), ); } } class _EmptyState extends StatelessWidget { const _EmptyState(); @override Widget build(BuildContext context) { return Center( child: Text( "Aucun emprunt en cours à restituer", style: GoogleFonts.titilliumWeb(fontSize: 14, color: EnsupColors.muted), ), ); } } class _ErreurState extends StatelessWidget { const _ErreurState({required this.message, required this.onRetry}); final String message; final VoidCallback onRetry; @override Widget build(BuildContext context) { return Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ const Icon( Icons.cloud_off_outlined, size: 40, color: EnsupColors.muted, ), const SizedBox(height: 12), Text( message, textAlign: TextAlign.center, style: GoogleFonts.titilliumWeb( fontSize: 14, color: EnsupColors.text2, ), ), const SizedBox(height: 14), OutlinedButton( onPressed: onRetry, child: Text( "Réessayer", style: GoogleFonts.darkerGrotesque(fontWeight: FontWeight.w700), ), ), ], ), ); } }