// lib/features/feedback/feedback_screen.dart // Buzón de retroalimentación. Expone "Unidad 101", nunca el nombre del chofer. import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import '../../core/theme/app_theme.dart'; import '../eta/eta_provider.dart'; // activeAddressIdProvider import '../incidents/providers/incident_providers.dart'; // assignedUnitProvider import 'feedback_model.dart'; import 'feedback_provider.dart'; class FeedbackScreen extends ConsumerStatefulWidget { const FeedbackScreen({super.key}); @override ConsumerState createState() => _FeedbackScreenState(); } class _FeedbackScreenState extends ConsumerState { final _messageController = TextEditingController(); @override void dispose() { _messageController.dispose(); super.dispose(); } @override Widget build(BuildContext context) { final formState = ref.watch(feedbackProvider); if (formState.status == FeedbackFormStatus.success) { return _SuccessView( onReset: () { ref.read(feedbackProvider.notifier).reset(); _messageController.clear(); }, ); } return Scaffold( backgroundColor: AppTheme.background, body: CustomScrollView( slivers: [ SliverToBoxAdapter(child: _buildPageHeader(context)), SliverPadding( padding: const EdgeInsets.fromLTRB(24, 24, 24, 0), sliver: SliverList( delegate: SliverChildListDelegate([ // Tipo de reporte _SectionLabel('Tipo de reporte'), const SizedBox(height: 10), _TypeChips( selected: formState.selectedType, onSelect: (t) => ref.read(feedbackProvider.notifier).setType(t), ), const SizedBox(height: 24), // Rating _SectionLabel('Calificación del servicio'), const SizedBox(height: 12), Center( child: _StarRating( rating: formState.rating, onRate: (r) => ref.read(feedbackProvider.notifier).setRating(r), ), ), const SizedBox(height: 24), // Unidad (sin exponer chofer) _SectionLabel('Unidad involucrada'), const SizedBox(height: 10), const _UnitBadge(), const SizedBox(height: 24), // Mensaje libre _SectionLabel('Descripción (opcional)'), const SizedBox(height: 10), Container( decoration: BoxDecoration( color: AppTheme.surface, borderRadius: BorderRadius.circular(AppTheme.radiusMd), border: Border.all(color: AppTheme.border), boxShadow: AppTheme.softShadow, ), child: TextField( controller: _messageController, maxLines: 4, maxLength: 300, onChanged: (v) => ref.read(feedbackProvider.notifier).setMessage(v), style: const TextStyle( fontSize: 14, color: AppTheme.textPrimary, ), decoration: const InputDecoration( hintText: 'Cuéntanos qué pasó...', border: InputBorder.none, contentPadding: EdgeInsets.all(14), ), ), ), const SizedBox(height: 12), // Error if (formState.status == FeedbackFormStatus.error) _ErrorBanner( message: formState.errorMessage ?? 'Error', ), const SizedBox(height: 32), ]), ), ), ], ), bottomNavigationBar: _buildSubmitButton(context, formState, ref), ); } Widget _buildPageHeader(BuildContext context) { return Container( padding: EdgeInsets.fromLTRB( 20, MediaQuery.of(context).padding.top + 12, 20, 24, ), decoration: const BoxDecoration( gradient: LinearGradient( colors: [Color(0xFF4A0E26), Color(0xFF9B1B4A)], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.only( bottomLeft: Radius.circular(28), bottomRight: Radius.circular(28), ), ), child: Row( children: [ GestureDetector( onTap: () => Navigator.of(context).pop(), child: Container( width: 36, height: 36, decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(10), ), child: const Icon( Icons.arrow_back, color: Colors.white, size: 20, ), ), ), const SizedBox(width: 14), const Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 'Buzón de retroalimentación', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w700, color: Colors.white, ), ), SizedBox(height: 2), Text( 'Tu opinión mejora el servicio', style: TextStyle(fontSize: 13, color: Colors.white70), ), ], ), ), Container( width: 44, height: 44, decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(12), ), child: const Icon( Icons.rate_review_outlined, color: Colors.white, size: 22, ), ), ], ), ); } Widget _buildSubmitButton( BuildContext context, FeedbackFormState formState, WidgetRef ref, ) { return SafeArea( child: Padding( padding: const EdgeInsets.fromLTRB(24, 12, 24, 16), child: SizedBox( width: double.infinity, child: FilledButton( onPressed: formState.status == FeedbackFormStatus.loading ? null : () { final addressId = ref.read(activeAddressIdProvider); final unit = ref.read(assignedUnitProvider).value; if (addressId == null) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text( 'Selecciona una dirección activa antes de enviar.', ), ), ); return; } if (unit == null) { ScaffoldMessenger.of(context).showSnackBar( const SnackBar( content: Text( 'Tu zona aún no tiene una unidad asignada.', ), ), ); return; } ref.read(feedbackProvider.notifier).submit( addressId: addressId, unitId: unit.id.toString(), ); }, child: formState.status == FeedbackFormStatus.loading ? const SizedBox( height: 18, width: 18, child: CircularProgressIndicator( strokeWidth: 2, color: Colors.white, ), ) : const Text('Enviar reporte'), ), ), ), ); } } // ── Chips de tipo ───────────────────────────────────────────────────────────── class _TypeChips extends StatelessWidget { final FeedbackType selected; final ValueChanged onSelect; const _TypeChips({required this.selected, required this.onSelect}); @override Widget build(BuildContext context) { return SingleChildScrollView( scrollDirection: Axis.horizontal, child: Row( children: FeedbackType.values.map((t) { final isSelected = t == selected; return Padding( padding: const EdgeInsets.only(right: 8), child: ChoiceChip( label: Text(t.label), selected: isSelected, onSelected: (_) => onSelect(t), selectedColor: AppTheme.primaryLight, backgroundColor: AppTheme.surface, side: BorderSide( color: isSelected ? AppTheme.primary : AppTheme.border, width: isSelected ? 1.5 : 0.5, ), labelStyle: TextStyle( color: isSelected ? AppTheme.primaryDark : AppTheme.textSecondary, fontSize: 13, fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal, ), padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 8), ), ); }).toList(), ), ); } } // ── Stars ───────────────────────────────────────────────────────────────────── class _StarRating extends StatelessWidget { final int rating; final ValueChanged onRate; const _StarRating({required this.rating, required this.onRate}); @override Widget build(BuildContext context) { return Row( mainAxisSize: MainAxisSize.min, children: List.generate(5, (i) { final filled = i < rating; return GestureDetector( onTap: () => onRate(i + 1), child: Padding( padding: const EdgeInsets.symmetric(horizontal: 6), child: Icon( filled ? Icons.star_rounded : Icons.star_outline_rounded, size: 42, color: filled ? const Color(0xFFEF9F27) : AppTheme.border, ), ), ); }), ); } } // ── Badge de unidad (sin exponer chofer) ────────────────────────────────────── class _UnitBadge extends ConsumerWidget { const _UnitBadge(); @override Widget build(BuildContext context, WidgetRef ref) { final unitAsync = ref.watch(assignedUnitProvider); final unitLabel = unitAsync.when( loading: () => 'Detectando unidad…', error: (_, _) => 'Unidad no disponible', data: (u) => u == null ? 'Sin unidad asignada' : 'Unidad ${u.id}', ); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Container( padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 10), decoration: BoxDecoration( color: AppTheme.primaryLight, borderRadius: BorderRadius.circular(AppTheme.radiusMd), border: Border.all(color: AppTheme.primaryMid), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ const Icon( Icons.local_shipping_outlined, size: 18, color: AppTheme.primaryDark, ), const SizedBox(width: 8), Text( unitLabel, style: const TextStyle( fontSize: 13, fontWeight: FontWeight.w500, color: AppTheme.primaryDark, ), ), ], ), ), const SizedBox(height: 8), Row( children: [ const Icon( Icons.shield_outlined, size: 13, color: AppTheme.textSecondary, ), const SizedBox(width: 4), const Expanded( child: Text( 'Solo se registra el número de unidad. El operador no es identificado.', style: TextStyle( fontSize: 11, color: AppTheme.textSecondary, ), ), ), ], ), ], ); } } // ── Error banner ────────────────────────────────────────────────────────────── class _ErrorBanner extends StatelessWidget { final String message; const _ErrorBanner({required this.message}); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.all(12), decoration: BoxDecoration( color: AppTheme.dangerLight, borderRadius: BorderRadius.circular(AppTheme.radiusMd), border: Border.all(color: AppTheme.danger.withValues(alpha: 0.3)), ), child: Row( children: [ const Icon(Icons.error_outline, size: 16, color: AppTheme.danger), const SizedBox(width: 8), Expanded( child: Text( message, style: const TextStyle( fontSize: 12, color: AppTheme.danger, ), ), ), ], ), ); } } // ── Success view ────────────────────────────────────────────────────────────── class _SuccessView extends StatelessWidget { final VoidCallback onReset; const _SuccessView({required this.onReset}); @override Widget build(BuildContext context) { return Scaffold( backgroundColor: AppTheme.background, body: Column( children: [ Container( padding: EdgeInsets.fromLTRB( 20, MediaQuery.of(context).padding.top + 12, 20, 24, ), decoration: const BoxDecoration( gradient: LinearGradient( colors: [Color(0xFF4A0E26), Color(0xFF9B1B4A)], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.only( bottomLeft: Radius.circular(28), bottomRight: Radius.circular(28), ), ), child: Row( children: [ GestureDetector( onTap: () => Navigator.of(context).pop(), child: Container( width: 36, height: 36, decoration: BoxDecoration( color: Colors.white.withValues(alpha: 0.15), borderRadius: BorderRadius.circular(10), ), child: const Icon( Icons.arrow_back, color: Colors.white, size: 20, ), ), ), const SizedBox(width: 14), const Text( 'Buzón de retroalimentación', style: TextStyle( fontSize: 18, fontWeight: FontWeight.w700, color: Colors.white, ), ), ], ), ), Expanded( child: Center( child: Padding( padding: const EdgeInsets.all(32), child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( width: 72, height: 72, decoration: const BoxDecoration( color: AppTheme.primaryLight, shape: BoxShape.circle, ), child: const Icon( Icons.check_circle_outline_rounded, size: 40, color: AppTheme.primary, ), ), const SizedBox(height: 20), const Text( 'Reporte enviado', style: TextStyle( fontSize: 20, fontWeight: FontWeight.w700, color: AppTheme.textPrimary, ), ), const SizedBox(height: 10), const Text( 'Gracias. Tu retroalimentación ayuda a mejorar el servicio. El reporte fue registrado de forma anónima.', textAlign: TextAlign.center, style: TextStyle( fontSize: 14, color: AppTheme.textSecondary, height: 1.5, ), ), const SizedBox(height: 28), SizedBox( width: double.infinity, child: OutlinedButton( onPressed: onReset, style: OutlinedButton.styleFrom( foregroundColor: AppTheme.primary, side: const BorderSide(color: AppTheme.primary), ), child: const Text('Enviar otro reporte'), ), ), ], ), ), ), ), ], ), ); } } // ── Helpers ─────────────────────────────────────────────────────────────────── class _SectionLabel extends StatelessWidget { final String text; const _SectionLabel(this.text); @override Widget build(BuildContext context) { return Text( text.toUpperCase(), style: const TextStyle( fontSize: 11, fontWeight: FontWeight.w600, color: AppTheme.textSecondary, letterSpacing: 0.8, ), ); } }