import 'dart:async'; import 'package:flutter/material.dart'; import 'package:google_fonts/google_fonts.dart'; import 'package:flutter/foundation.dart' show kIsWeb; import 'package:p_tits_pas/services/api/tokenService.dart'; import 'package:p_tits_pas/services/auth_service.dart'; import 'package:url_launcher/url_launcher.dart'; import 'package:p_tits_pas/services/bug_report_service.dart'; import '../../widgets/image_button.dart'; import '../../widgets/custom_app_text_field.dart'; class LoginPage extends StatefulWidget { const LoginPage({super.key}); @override State createState() => _LoginPageState(); } class _LoginPageState extends State { final _formKey = GlobalKey(); final _emailController = TextEditingController(); final _passwordController = TextEditingController(); final AuthService _authService = AuthService(); bool _isLoading = false; @override void dispose() { _emailController.dispose(); _passwordController.dispose(); super.dispose(); } String? _validateEmail(String? value) { if (value == null || value.isEmpty) { return 'Veuillez entrer votre email'; } if (!RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$').hasMatch(value)) { return 'Veuillez entrer un email valide'; } return null; } String? _validatePassword(String? value) { if (value == null || value.isEmpty) { return 'Veuillez entrer votre mot de passe'; } if (value.length < 6) { return 'Le mot de passe doit contenir au moins 6 caractères'; } return null; } Future _handleLogin() async { if (_formKey.currentState?.validate() ?? false) { setState(() { _isLoading = true; }); try { final response = await _authService.login( _emailController.text.trim(), _passwordController.text, ); print('Login response: ${response}'); if (!mounted) return; // Navigation selon le rôle final role = await TokenService.getRole(); print('User role: $role'); if (role != null) { switch (role.toLowerCase()) { case 'parent': Navigator.pushReplacementNamed(context, '/parent-dashboard'); break; case 'assistante_maternelle': Navigator.pushReplacementNamed( context, '/assistante_maternelle_dashboard'); break; case 'super_admin' || 'administrateur': Navigator.pushReplacementNamed(context, '/admin_dashboard'); break; case 'gestionnaire': Navigator.pushReplacementNamed( context, '/gestionnaire_dashboard'); break; default: _showErrorSnackBar('Rôle utilisateur non reconnu: $role'); return; } } else { _showErrorSnackBar('Rôle utilisateur non trouvé'); } } catch (e) { print('Login error: $e'); if (!mounted) return; String errorMessage = e.toString(); String errorString = e.toString(); if (errorString.contains('Failed to login:')) { // Extraire le message d'erreur réel errorMessage = errorString.replaceFirst('Exception: Failed to login: ', ''); } _showErrorSnackBar(errorMessage); } finally { if (mounted) { setState(() { _isLoading = false; // AJOUT : Fin du chargement }); } } } } void _showErrorSnackBar(String message) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(message), backgroundColor: Colors.red, duration: const Duration(seconds: 4), // Plus long pour lire l'erreur ), ); } void _showSuccessSnackBar(String message) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(message), backgroundColor: Colors.green, duration: const Duration(seconds: 2), ), ); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: Colors.transparent, body: LayoutBuilder( builder: (context, constraints) { // Version desktop (web) if (kIsWeb) { final w = constraints.maxWidth; final h = constraints.maxHeight; return FutureBuilder( future: _getImageDimensions(), builder: (context, snapshot) { if (!snapshot.hasData) { return const Center(child: CircularProgressIndicator()); } final imageDimensions = snapshot.data!; final imageHeight = h; final imageWidth = imageHeight * (imageDimensions.width / imageDimensions.height); final remainingWidth = w - imageWidth; final leftMargin = remainingWidth / 4; return Stack( children: [ // Fond en papier Positioned.fill( child: Image.asset( 'assets/images/paper2.png', fit: BoxFit.cover, repeat: ImageRepeat.repeat, ), ), // Image principale Positioned( left: leftMargin, top: 0, height: imageHeight, width: imageWidth, child: Image.asset( 'assets/images/river_logo_desktop.png', fit: BoxFit.contain, ), ), // Formulaire dans le cadran en bas à droite Positioned( right: 0, bottom: 0, width: w * 0.6, // 60% de la largeur de l'écran height: h * 0.5, // 50% de la hauteur de l'écran child: Padding( padding: EdgeInsets.all(w * 0.02), // 2% de padding child: Form( key: _formKey, child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ // Champs côte à côte Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Expanded( child: CustomAppTextField( controller: _emailController, labelText: 'Email', hintText: 'Votre adresse email', validator: _validateEmail, style: CustomAppTextFieldStyle.lavande, fieldHeight: 53, fieldWidth: double.infinity, enabled: !_isLoading, ), ), const SizedBox(width: 20), Expanded( child: CustomAppTextField( controller: _passwordController, labelText: 'Mot de passe', hintText: 'Votre mot de passe', obscureText: true, validator: _validatePassword, style: CustomAppTextFieldStyle.jaune, fieldHeight: 53, fieldWidth: double.infinity, enabled: !_isLoading, ), ), ], ), const SizedBox(height: 20), // Bouton centré Center( child: _isLoading ? const SizedBox( width: 300, height: 40, child: Center( child: CircularProgressIndicator(), ), ) : ImageButton( bg: 'assets/images/btn_green.png', width: 300, height: 40, text: 'Se connecter', textColor: const Color(0xFF2D6A4F), onPressed: _handleLogin, ), ), const SizedBox(height: 10), // Lien mot de passe oublié Center( child: TextButton( onPressed: () { // TODO: Implémenter la logique de récupération de mot de passe }, child: Text( 'Mot de passe oublié ?', style: GoogleFonts.merienda( fontSize: 14, color: const Color(0xFF2D6A4F), decoration: TextDecoration.underline, ), ), ), ), const SizedBox(height: 10), // Lien de création de compte Center( child: TextButton( onPressed: () { Navigator.pushNamed( context, '/register-choice'); }, child: Text( 'Créer un compte', style: GoogleFonts.merienda( fontSize: 16, color: const Color(0xFF2D6A4F), decoration: TextDecoration.underline, ), ), ), ), const SizedBox( height: 20), // Réduit l'espacement en bas ], ), ), ), ), // Pied de page Positioned( left: 0, right: 0, bottom: 0, child: Container( padding: const EdgeInsets.symmetric(vertical: 8.0), decoration: BoxDecoration( color: Colors.transparent, ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ _FooterLink( text: 'Contact support', onTap: () async { final Uri emailLaunchUri = Uri( scheme: 'mailto', path: 'support@supernounou.local', ); if (await canLaunchUrl(emailLaunchUri)) { await launchUrl(emailLaunchUri); } else { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text( 'Impossible d\'ouvrir le client mail', style: GoogleFonts.merienda(), ), ), ); } }, ), _FooterLink( text: 'Signaler un bug', onTap: () { _showBugReportDialog(context); }, ), _FooterLink( text: 'Mentions légales', onTap: () { Navigator.pushNamed(context, '/legal'); }, ), _FooterLink( text: 'Politique de confidentialité', onTap: () { Navigator.pushNamed(context, '/privacy'); }, ), ], ), ), ), ], ); }, ); } // Version mobile (à implémenter) return const Center( child: Text('Version mobile à implémenter'), ); }, ), ); } void _showBugReportDialog(BuildContext context) { final TextEditingController controller = TextEditingController(); showDialog( context: context, builder: (context) => AlertDialog( title: Text( 'Signaler un bug', style: GoogleFonts.merienda(), ), content: Column( mainAxisSize: MainAxisSize.min, children: [ TextField( controller: controller, maxLines: 5, decoration: InputDecoration( hintText: 'Décrivez le problème rencontré...', border: OutlineInputBorder( borderRadius: BorderRadius.circular(10), ), ), ), ], ), actions: [ TextButton( onPressed: () => Navigator.pop(context), child: Text( 'Annuler', style: GoogleFonts.merienda(), ), ), TextButton( onPressed: () async { if (controller.text.trim().isEmpty) { _showErrorSnackBar('Veuillez décrire le problème'); return; } try { await BugReportService.sendReport(controller.text); if (context.mounted) { Navigator.pop(context); _showSuccessSnackBar('Rapport envoyé avec succès'); } } catch (e) { if (context.mounted) { _showErrorSnackBar('Erreur lors de l\'envoi du rapport'); } } }, child: Text( 'Envoyer', style: GoogleFonts.merienda(), ), ), ], ), ); } Future _getImageDimensions() async { final image = Image.asset('assets/images/river_logo_desktop.png'); final completer = Completer(); image.image.resolve(const ImageConfiguration()).addListener( ImageStreamListener((info, _) { completer.complete(ImageDimensions( width: info.image.width.toDouble(), height: info.image.height.toDouble(), )); }), ); return completer.future; } } class ImageDimensions { final double width; final double height; ImageDimensions({required this.width, required this.height}); } // ─────────────────────────────────────────────────────────────── // Lien du pied de page // ─────────────────────────────────────────────────────────────── class _FooterLink extends StatelessWidget { final String text; final VoidCallback onTap; const _FooterLink({ required this.text, required this.onTap, }); @override Widget build(BuildContext context) { return InkWell( onTap: onTap, child: Padding( padding: const EdgeInsets.symmetric(horizontal: 8.0), child: Text( text, style: GoogleFonts.merienda( fontSize: 14, color: Colors.black87, decoration: TextDecoration.underline, ), ), ), ); } }