import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../core/theme/app_theme.dart'; import '../../core/widgets/app_widgets.dart'; import '../../core/network/api_client.dart'; // ── Provider de ETA ─────────────────────────────────────────────────────────── final etaProvider = FutureProvider.autoDispose<_EtaResult>((ref) async { final dio = ref.read(apiClientProvider); final addressesResp = await dio.get('/addresses'); final raw = addressesResp.data; List items = const []; if (raw is List) { items = raw; } else if (raw is Map && raw['data'] is List) { items = raw['data'] as List; } else if (raw is Map && raw['addresses'] is List) { items = raw['addresses'] as List; } if (items.isEmpty) { return const _EtaResult.noAddress(); } final addressId = items.first['id'] as String; final etaResp = await dio.get( '/eta', queryParameters: {'address_id': addressId}, ); final data = etaResp.data as Map; return _EtaResult( mensaje: data['mensaje'] as String? ?? '', status: data['status'] as String? ?? '', direccion: items.first['calle'] as String? ?? '', colonia: items.first['colonia'] as String? ?? '', hasAddress: true, ); }); class _EtaResult { final String mensaje; final String status; final String direccion; final String colonia; final bool hasAddress; const _EtaResult({ required this.mensaje, required this.status, required this.direccion, required this.colonia, required this.hasAddress, }); const _EtaResult.noAddress() : mensaje = '', status = '', direccion = '', colonia = '', hasAddress = false; double get progreso { if (mensaje.contains('15 minutos') || mensaje.contains('Está atendiendo')) { return 0.85; } if (mensaje.contains('finalizado')) return 1.0; return 0.35; } String get etiquetaEstado { if (status == 'completada') return 'Finalizado'; if (status == 'en_ruta') return 'En ruta'; return 'Pendiente'; } } // ── Pantalla ETA ────────────────────────────────────────────────────────────── class EtaScreen extends ConsumerWidget { const EtaScreen({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final etaAsync = ref.watch(etaProvider); return Scaffold( backgroundColor: AppTheme.background, appBar: AppBar( title: const Text('Estado del camión'), actions: [ IconButton( icon: const Icon(Icons.refresh), tooltip: 'Actualizar', onPressed: () => ref.invalidate(etaProvider), ), ], ), body: etaAsync.when( loading: () => const _EtaLoading(), error: (error, _) => _EtaError( error: error.toString(), onRetry: () => ref.invalidate(etaProvider), ), data: (result) => result.hasAddress ? _EtaContent(result: result) : _NoAddressState( onAdd: () => context.go('/addresses/new'), ), ), ); } } // ── Contenido ETA ───────────────────────────────────────────────────────────── class _EtaContent extends StatelessWidget { final _EtaResult result; const _EtaContent({required this.result}); @override Widget build(BuildContext context) { return ListView( padding: const EdgeInsets.all(16), children: [ // ── Tarjeta de estado principal ──────────────────────────────── Container( padding: const EdgeInsets.all(20), decoration: BoxDecoration( color: AppTheme.primaryLight, borderRadius: BorderRadius.circular(AppTheme.radiusLg), border: Border.all(color: AppTheme.primaryMid), boxShadow: AppTheme.softShadow, ), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Row( children: [ Container( width: 44, height: 44, decoration: BoxDecoration( color: AppTheme.primary, borderRadius: BorderRadius.circular(12), ), child: const Icon( Icons.delete_outline_rounded, color: Colors.white, size: 24, ), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text( 'Camión recolector', style: TextStyle( fontSize: 15, fontWeight: FontWeight.w700, color: AppTheme.primaryDark, ), ), const SizedBox(height: 2), AppStatusBadge.green(result.etiquetaEstado), ], ), ), _LiveDot(active: result.status == 'en_ruta'), ], ), const SizedBox(height: 20), // Mensaje ETA Text( result.mensaje, style: const TextStyle( fontSize: 22, fontWeight: FontWeight.w700, color: AppTheme.primaryDark, height: 1.3, ), ), const SizedBox(height: 16), // Barra de progreso ClipRRect( borderRadius: BorderRadius.circular(4), child: LinearProgressIndicator( value: result.progreso, backgroundColor: AppTheme.primaryMid.withValues(alpha: 0.35), valueColor: const AlwaysStoppedAnimation(AppTheme.primary), minHeight: 8, ), ), const SizedBox(height: 6), const Row( children: [ Text('Inicio de ruta', style: TextStyle( fontSize: 10, color: AppTheme.primaryDark)), Spacer(), Text('Tu casa', style: TextStyle( fontSize: 10, color: AppTheme.primaryDark)), ], ), ], ), ), const SizedBox(height: 16), // ── Domicilio registrado ─────────────────────────────────────── AppInfoRow( icon: Icons.home_outlined, label: 'Col. ${result.colonia}', value: result.direccion.isEmpty ? 'Mi domicilio' : result.direccion, trailing: AppStatusBadge.green('Activo'), ), const SizedBox(height: 16), // ── Aviso de privacidad ──────────────────────────────────────── Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: AppTheme.blueLight, borderRadius: BorderRadius.circular(AppTheme.radiusMd), ), child: const Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(Icons.shield_outlined, color: AppTheme.blue, size: 18), SizedBox(width: 10), Expanded( child: Text( 'Tu ubicación exacta y la del camión no se comparten. Solo ves el estado de tu ruta.', style: TextStyle( fontSize: 12, color: AppTheme.blue, height: 1.5), ), ), ], ), ), const SizedBox(height: 16), // ── Horario estimado de la semana ────────────────────────────── AppSectionTitle(title: 'Horario del camión'), _HorarioCard(), ], ); } } // ── Punto animado "en vivo" ─────────────────────────────────────────────────── class _LiveDot extends StatefulWidget { final bool active; const _LiveDot({required this.active}); @override State<_LiveDot> createState() => _LiveDotState(); } class _LiveDotState extends State<_LiveDot> with SingleTickerProviderStateMixin { late final AnimationController _anim; @override void initState() { super.initState(); _anim = AnimationController( vsync: this, duration: const Duration(milliseconds: 900), )..repeat(reverse: true); } @override void dispose() { _anim.dispose(); super.dispose(); } @override Widget build(BuildContext context) { if (!widget.active) { return const SizedBox.shrink(); } return AnimatedBuilder( animation: _anim, builder: (_, child) => Container( width: 10, height: 10, decoration: BoxDecoration( shape: BoxShape.circle, color: AppTheme.primary .withValues(alpha: 0.5 + _anim.value * 0.5), ), ), ); } } // ── Horario ─────────────────────────────────────────────────────────────────── class _HorarioCard extends StatelessWidget { final List<_HorarioDia> _dias = const [ _HorarioDia(dia: 'Lunes', hora: '8:00 – 10:00 a.m.', activo: true), _HorarioDia(dia: 'Martes', hora: '8:00 – 10:00 a.m.', activo: true), _HorarioDia(dia: 'Miércoles',hora: 'Sin servicio', activo: false), _HorarioDia(dia: 'Jueves', hora: '8:00 – 10:00 a.m.', activo: true), _HorarioDia(dia: 'Viernes', hora: '8:00 – 10:00 a.m.', activo: true), _HorarioDia(dia: 'Sábado', hora: '9:00 – 11:00 a.m.', activo: true), _HorarioDia(dia: 'Domingo', hora: 'Sin servicio', activo: false), ]; @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: AppTheme.surface, borderRadius: BorderRadius.circular(AppTheme.radiusLg), border: Border.all(color: AppTheme.border, width: 0.5), boxShadow: AppTheme.softShadow, ), child: Column( children: _dias.map((d) { return Padding( padding: const EdgeInsets.symmetric(vertical: 7), child: Row( children: [ Text( d.dia, style: TextStyle( fontSize: 13, fontWeight: FontWeight.w500, color: d.activo ? AppTheme.textPrimary : AppTheme.textSecondary, ), ), const Spacer(), Text( d.hora, style: TextStyle( fontSize: 13, color: d.activo ? AppTheme.primary : AppTheme.textSecondary, ), ), ], ), ); }).toList(), ), ); } } class _HorarioDia { final String dia; final String hora; final bool activo; const _HorarioDia( {required this.dia, required this.hora, required this.activo}); } // ── Sin domicilio ───────────────────────────────────────────────────────────── class _NoAddressState extends StatelessWidget { final VoidCallback onAdd; const _NoAddressState({required this.onAdd}); @override Widget build(BuildContext context) { return Center( child: Padding( padding: const EdgeInsets.all(32), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ Container( width: 80, height: 80, decoration: const BoxDecoration( color: AppTheme.primaryLight, shape: BoxShape.circle, ), child: const Icon(Icons.home_outlined, color: AppTheme.primary, size: 40), ), const SizedBox(height: 20), const Text( 'Sin domicilio registrado', style: TextStyle( fontSize: 17, fontWeight: FontWeight.w700, color: AppTheme.textPrimary), ), const SizedBox(height: 8), const Text( 'Registra tu domicilio para\nrecibir el ETA de tu ruta.', textAlign: TextAlign.center, style: TextStyle( fontSize: 13, color: AppTheme.textSecondary, height: 1.5), ), const SizedBox(height: 24), SizedBox( width: 200, child: ElevatedButton( onPressed: onAdd, child: const Text('Agregar domicilio'), ), ), ], ), ), ); } } // ── Cargando ────────────────────────────────────────────────────────────────── class _EtaLoading extends StatelessWidget { const _EtaLoading(); @override Widget build(BuildContext context) { return const Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ CircularProgressIndicator(color: AppTheme.primary), SizedBox(height: 16), Text('Consultando estado del camión…', style: TextStyle(color: AppTheme.textSecondary, fontSize: 14)), ], ), ); } } // ── Error ───────────────────────────────────────────────────────────────────── class _EtaError extends StatelessWidget { final String error; final VoidCallback onRetry; const _EtaError({required this.error, required this.onRetry}); @override Widget build(BuildContext context) { return Center( child: Padding( padding: const EdgeInsets.all(32), child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const Icon(Icons.wifi_off_outlined, color: AppTheme.textSecondary, size: 48), const SizedBox(height: 16), const Text('No se pudo obtener el estado', style: TextStyle( fontSize: 16, fontWeight: FontWeight.w600, color: AppTheme.textPrimary)), const SizedBox(height: 8), Text(error, textAlign: TextAlign.center, style: const TextStyle( fontSize: 12, color: AppTheme.textSecondary)), const SizedBox(height: 20), SizedBox( width: 160, child: ElevatedButton( onPressed: onRetry, child: const Text('Reintentar'), ), ), ], ), ), ); } }