/// Page dédiée à l'envoi de commentaires / feedback /// Permet de soumettre des suggestions, signaler des bugs, ou proposer des idées library feedback_page; import 'package:flutter/material.dart'; import 'package:get_it/get_it.dart'; import 'package:dio/dio.dart'; import '../../../../core/utils/logger.dart'; import '../../../../shared/design_system/unionflow_design_system.dart'; class FeedbackPage extends StatefulWidget { const FeedbackPage({super.key}); @override State createState() => _FeedbackPageState(); } class _FeedbackPageState extends State { final _messageController = TextEditingController(); String _selectedCategory = 'suggestion'; bool _isSending = false; static const _categories = [ _FeedbackCategory('suggestion', 'Suggestion', Icons.lightbulb, Color(0xFF6C5CE7)), _FeedbackCategory('bug', 'Bug / Problème', Icons.bug_report, Color(0xFFE17055)), _FeedbackCategory('amelioration', 'Amélioration', Icons.trending_up, Color(0xFF00B894)), _FeedbackCategory('autre', 'Autre', Icons.help_outline, Color(0xFF0984E3)), ]; @override void dispose() { _messageController.dispose(); super.dispose(); } Future _submitFeedback() async { final message = _messageController.text.trim(); if (message.isEmpty) { _showSnackBar('Veuillez saisir un message.', isError: true); return; } setState(() => _isSending = true); try { await GetIt.I().post( '/api/feedback', data: { 'subject': 'Feedback mobile [$_selectedCategory]', 'message': message, 'categorie': _selectedCategory, }, ); if (mounted) { _messageController.clear(); _showSnackBar('Merci pour votre retour !'); } } catch (e, st) { AppLogger.error('FeedbackPage: envoi feedback échoué', error: e, stackTrace: st); if (mounted) { _showSnackBar('Envoi échoué. Réessayez plus tard.', isError: true); } } finally { if (mounted) setState(() => _isSending = false); } } void _showSnackBar(String message, {bool isError = false}) { ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(message), backgroundColor: isError ? Colors.red : const Color(0xFF00B894), behavior: SnackBarBehavior.floating, ), ); } @override Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFFF8F9FA), body: Column( children: [ _buildHeader(), Expanded( child: SingleChildScrollView( padding: const EdgeInsets.all(12), child: Column( children: [ const SizedBox(height: 16), _buildCategorySection(), const SizedBox(height: 16), _buildMessageSection(), const SizedBox(height: 16), _buildSubmitButton(), const SizedBox(height: 80), ], ), ), ), ], ), ); } Widget _buildHeader() { return Container( margin: const EdgeInsets.all(SpacingTokens.lg), padding: const EdgeInsets.all(SpacingTokens.xxl), decoration: BoxDecoration( gradient: const LinearGradient( colors: ColorTokens.primaryGradient, begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(SpacingTokens.xl), boxShadow: [ BoxShadow( color: ColorTokens.primary.withOpacity(0.3), blurRadius: 20, offset: const Offset(0, 8), ), ], ), child: SafeArea( bottom: false, child: Row( children: [ IconButton( onPressed: () => Navigator.of(context).pop(), icon: const Icon(Icons.arrow_back, color: Colors.white), ), Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: Colors.white.withOpacity(0.2), borderRadius: BorderRadius.circular(12), ), child: const Icon(Icons.feedback, color: Colors.white, size: 24), ), const SizedBox(width: 16), const Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Commentaires', style: TextStyle( fontSize: 20, fontWeight: FontWeight.bold, color: Colors.white, ), ), Text( 'Aidez-nous à améliorer UnionFlow', style: TextStyle( fontSize: 14, color: Colors.white70, ), ), ], ), ), ], ), ), ); } Widget _buildCategorySection() { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: const Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(Icons.category, color: Colors.grey[600], size: 20), const SizedBox(width: 8), Text( 'Type de retour', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: Colors.grey[800], ), ), ], ), const SizedBox(height: 16), Wrap( spacing: 10, runSpacing: 10, children: _categories.map((cat) => _buildCategoryChip(cat)).toList(), ), ], ), ); } Widget _buildCategoryChip(_FeedbackCategory cat) { final isSelected = _selectedCategory == cat.id; return InkWell( onTap: () => setState(() => _selectedCategory = cat.id), borderRadius: BorderRadius.circular(12), child: Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 10), decoration: BoxDecoration( color: isSelected ? cat.color.withOpacity(0.12) : Colors.grey[50], borderRadius: BorderRadius.circular(12), border: Border.all( color: isSelected ? cat.color.withOpacity(0.5) : Colors.grey[200]!, width: isSelected ? 1.5 : 1, ), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(cat.icon, size: 18, color: isSelected ? cat.color : Colors.grey[500]), const SizedBox(width: 8), Text( cat.label, style: TextStyle( fontSize: 13, fontWeight: FontWeight.w600, color: isSelected ? cat.color : Colors.grey[700], ), ), ], ), ), ); } Widget _buildMessageSection() { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white, borderRadius: BorderRadius.circular(16), boxShadow: [ BoxShadow( color: Colors.black.withOpacity(0.05), blurRadius: 10, offset: const Offset(0, 2), ), ], ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Icon(Icons.edit_note, color: Colors.grey[600], size: 20), const SizedBox(width: 8), Text( 'Votre message', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: Colors.grey[800], ), ), ], ), const SizedBox(height: 16), TextField( controller: _messageController, maxLines: 6, decoration: InputDecoration( hintText: 'Décrivez votre suggestion, problème ou idée...', border: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey[300]!), ), enabledBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: BorderSide(color: Colors.grey[300]!), ), focusedBorder: OutlineInputBorder( borderRadius: BorderRadius.circular(12), borderSide: const BorderSide(color: ColorTokens.primary, width: 1.5), ), filled: true, fillColor: Colors.grey[50], alignLabelWithHint: true, ), ), ], ), ); } Widget _buildSubmitButton() { return SizedBox( width: double.infinity, child: ElevatedButton.icon( onPressed: _isSending ? null : _submitFeedback, icon: _isSending ? const SizedBox( width: 18, height: 18, child: CircularProgressIndicator(strokeWidth: 2, color: Colors.white), ) : const Icon(Icons.send, color: Colors.white), label: Text( _isSending ? 'Envoi en cours...' : 'Envoyer le commentaire', style: const TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: Colors.white, ), ), style: ElevatedButton.styleFrom( backgroundColor: ColorTokens.primary, padding: const EdgeInsets.symmetric(vertical: 16), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), elevation: 2, ), ), ); } } class _FeedbackCategory { final String id; final String label; final IconData icon; final Color color; const _FeedbackCategory(this.id, this.label, this.icon, this.color); }