import "package:flutter/material.dart"; import "package:google_fonts/google_fonts.dart"; import "../demo_identity.dart"; import "../services/api_client.dart"; import "../services/auth_service.dart"; import "../theme/ensup_colors.dart"; import "../widgets/ensup_top_bar.dart"; import "../widgets/identification_option.dart"; import "../widgets/brand_corners.dart"; import "home_screen.dart"; import "responsable_screen.dart"; /// Écran de démarrage : identification de l'étudiant (RG01/RG02). class LoginScreen extends StatefulWidget { const LoginScreen({super.key}); @override State createState() => _LoginScreenState(); } class _LoginScreenState extends State { bool _connexionEnCours = false; void _ouvrirEtudiant() { Navigator.of(context).push( MaterialPageRoute( builder: (_) => const HomeScreen(), settings: const RouteSettings(name: "home"), ), ); } void _ouvrirResponsable() { Navigator.of(context).push( MaterialPageRoute( builder: (_) => const ResponsableScreen(), settings: const RouteSettings(name: "responsable"), ), ); } void _identifierParQr() { if (_connexionEnCours) return; if (AuthService.mode == AuthenticationMode.azure) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text( "L'identification QR securisee n'est pas encore configuree.", ), ), ); return; } AuthService.selectDemoStudent(); _ouvrirEtudiant(); } Future _connecterMicrosoft({required bool responsableDemo}) async { if (_connexionEnCours) return; setState(() => _connexionEnCours = true); try { if (AuthService.mode == AuthenticationMode.demo) { if (responsableDemo) { AuthService.selectDemoResponsable(); _ouvrirResponsable(); } else { AuthService.selectDemoStudent(); _ouvrirEtudiant(); } return; } await AuthService.signInWithMicrosoft(); final reponse = await ApiClient.get("/auth/me"); final roleCode = _roleCode(reponse); if (!mounted) return; switch (roleCode) { case "ETUDIANT": _ouvrirEtudiant(); case "RESPONSABLE": _ouvrirResponsable(); default: throw StateError("Role utilisateur non autorise."); } } catch (error) { if (!mounted) return; ScaffoldMessenger.of( context, ).showSnackBar(SnackBar(content: Text(error.toString()))); } finally { if (mounted) { setState(() => _connexionEnCours = false); } } } String _roleCode(dynamic reponse) { if (reponse is! Map || reponse["data"] is! Map) { throw StateError("Profil utilisateur invalide."); } final data = reponse["data"] as Map; if (data["role"] is! Map) { throw StateError("Role utilisateur absent."); } final role = data["role"] as Map; final code = role["code"]; if (code is! String) { throw StateError("Role utilisateur invalide."); } return code; } @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: [ const EnsupTopBar( brandText: "EME — Emprunt Matériel ENSUP", campusTag: DemoIdentity.loginCampusTag, ), Expanded( child: DecoratedBox( decoration: const BoxDecoration( gradient: LinearGradient( begin: Alignment.topLeft, end: Alignment.bottomRight, colors: [Color(0xFFF0F6FF), Color(0xFFF8FAFC)], ), ), child: Center( child: SingleChildScrollView( padding: const EdgeInsets.symmetric( horizontal: 28, vertical: 40, ), child: ConstrainedBox( constraints: const BoxConstraints(maxWidth: 560), child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( width: 72, height: 72, decoration: BoxDecoration( gradient: const LinearGradient( colors: [ EnsupColors.blue3, EnsupColors.cyan, ], ), borderRadius: BorderRadius.circular(22), boxShadow: [ BoxShadow( color: EnsupColors.blue3.withValues( alpha: 0.35, ), blurRadius: 24, offset: const Offset(0, 8), ), ], ), child: const Icon( Icons.badge_outlined, color: Colors.white, size: 34, ), ), const SizedBox(height: 20), Text( "Bienvenue sur EME", style: GoogleFonts.darkerGrotesque( fontSize: 34, fontWeight: FontWeight.w900, color: EnsupColors.text, ), ), const SizedBox(height: 8), Text( "Emprunt Matériel ENSUP · Accès réservé aux membres du groupe ENSUP", textAlign: TextAlign.center, style: GoogleFonts.titilliumWeb( fontSize: 14, color: EnsupColors.muted, ), ), const SizedBox(height: 28), IdentificationOption( icon: Icons.qr_code_2, iconColor: EnsupColors.cyan, label: "Scanner ma carte étudiante", description: "Présentez votre carte étudiante dématérialisée à la douchette ou à la tablette", onTap: _connexionEnCours ? null : _identifierParQr, ), const SizedBox(height: 12), IdentificationOption( icon: Icons.mail_outline, iconColor: EnsupColors.teal, label: "Connexion compte professionnel ENSUP", description: "Utiliser mon adresse e-mail Microsoft 365", onTap: _connexionEnCours ? null : () => _connecterMicrosoft( responsableDemo: false, ), ), const SizedBox(height: 20), Row( children: [ const Expanded( child: Divider(color: EnsupColors.line), ), Padding( padding: const EdgeInsets.symmetric( horizontal: 12, ), child: Text( "ou", style: GoogleFonts.titilliumWeb( fontSize: 12, color: EnsupColors.muted, ), ), ), const Expanded( child: Divider(color: EnsupColors.line), ), ], ), const SizedBox(height: 20), SizedBox( width: double.infinity, child: OutlinedButton.icon( onPressed: _connexionEnCours ? null : () => _connecterMicrosoft( responsableDemo: true, ), icon: const Icon( Icons.person_outline, size: 16, ), label: Text( "Accès responsable matériel", style: GoogleFonts.darkerGrotesque( fontWeight: FontWeight.w700, fontSize: 15, ), ), style: OutlinedButton.styleFrom( foregroundColor: EnsupColors.text2, side: const BorderSide( color: EnsupColors.line, width: 1.5, ), padding: const EdgeInsets.symmetric( vertical: 14, ), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular( 10, ), ), ), ), ), ], ), ), ), ), ), ), ], ), ), ), ), const Positioned.fill(child: BrandCorners()), ], ), ); } }