import 'package:flutter/material.dart'; import 'package:provider/provider.dart'; import '../../core/app_colors.dart'; import '../../services/auth_service.dart'; import '../../services/route_simulator_service.dart'; import '../../database/db_helper.dart'; import '../../models/models.dart'; import '../../data/routes_data.dart'; import '../../widgets/route_map_widget.dart'; import '../settings_screen.dart'; class DriverHomeScreen extends StatefulWidget { const DriverHomeScreen({super.key}); @override State createState() => _DriverHomeScreenState(); } class _DriverHomeScreenState extends State { int _tab = 0; List _assignments = []; String? _todayRouteId; @override void initState() { super.initState(); _load(); } Future _load() async { final auth = context.read(); if (auth.currentUser == null) return; final list = await DbHelper.getAsignacionesByConductor(auth.currentUser!.id!); final today = _todayDia(); setState(() { _assignments = list; final match = list.where((a) => a.diaSemana == today); _todayRouteId = match.isNotEmpty ? match.first.routeId : null; }); if (_todayRouteId != null) { context.read().startRoute(_todayRouteId!); } } String _todayDia() { const d = ['','LUNES','MARTES','MIERCOLES','JUEVES','VIERNES','SABADO','DOMINGO']; return d[DateTime.now().weekday]; } @override Widget build(BuildContext context) { final auth = context.watch(); final sim = context.watch(); final route = _todayRouteId != null ? getRouteById(_todayRouteId!) : null; // Solo notificaciones de la ruta actual del conductor final lastNotif = _todayRouteId != null ? sim.getNotificationForRoute(_todayRouteId!) : null; final tabs = [ _DriverMainTab(auth:auth, sim:sim, route:route, assignments:_assignments, todayRouteId:_todayRouteId, onRefresh:_load), if (route != null) _DriverMapTab(route:route, sim:sim) else const Center(child:Text('Sin ruta hoy')), _DriverReportesTab(conductorId:auth.currentUser?.id, todayRouteId:_todayRouteId), ]; return Scaffold( body: Stack(children:[ tabs[_tab], if (lastNotif != null) Positioned(top:MediaQuery.of(context).padding.top+8, left:0, right:0, child:_NotifBanner(notif:lastNotif, onDismiss:()=>sim.dismissRouteNotification(_todayRouteId??''))), ]), bottomNavigationBar: NavigationBar( selectedIndex: _tab, onDestinationSelected: (i) => setState(()=>_tab=i), backgroundColor: Colors.white, indicatorColor: AppColors.moradoConductor.withOpacity(0.15), destinations: const [ NavigationDestination(icon:Icon(Icons.dashboard_outlined), selectedIcon:Icon(Icons.dashboard,color:AppColors.moradoConductor),label:'Mi Ruta'), NavigationDestination(icon:Icon(Icons.map_outlined), selectedIcon:Icon(Icons.map,color:AppColors.moradoConductor),label:'Mapa'), NavigationDestination(icon:Icon(Icons.report_problem_outlined), selectedIcon:Icon(Icons.report_problem,color:AppColors.moradoConductor),label:'Incidente'), ], ), ); } } // ── Tab principal ───────────────────────────────────────────────────────── class _DriverMainTab extends StatefulWidget { final AuthService auth; final RouteSimulatorService sim; final route; final assignments; final todayRouteId; final VoidCallback onRefresh; const _DriverMainTab({required this.auth, required this.sim, required this.route, required this.assignments, required this.todayRouteId, required this.onRefresh}); @override State<_DriverMainTab> createState() => _DriverMainTabState(); } class _DriverMainTabState extends State<_DriverMainTab> { List _ciudadanoReportes = []; @override void initState() { super.initState(); _loadReportes(); } Future _loadReportes() async { if (widget.todayRouteId == null) return; final all = await DbHelper.getAllReportes(); final filtered = all.where((r) => r.routeId == widget.todayRouteId).toList(); if (mounted) setState(() => _ciudadanoReportes = filtered.take(5).toList()); } @override Widget build(BuildContext context) { final posIdx = widget.todayRouteId != null ? widget.sim.getPositionIndex(widget.todayRouteId!) : 0; final gpsOk = widget.todayRouteId != null ? widget.sim.isGpsActive(widget.todayRouteId!) : true; return CustomScrollView(slivers:[ SliverAppBar(pinned:true, backgroundColor:AppColors.moradoConductor, foregroundColor:Colors.white, bottom:PreferredSize(preferredSize:const Size.fromHeight(4), child:Container(height:4,color:AppColors.dorado)), title:Text('Conductor: ${widget.auth.currentUser?.nombre.split(' ').first ?? ''}', style:const TextStyle(fontSize:16,fontWeight:FontWeight.bold)), actions:[IconButton(icon:const Icon(Icons.logout), onPressed:()async{ await widget.auth.logout(); if(context.mounted) Navigator.pushReplacementNamed(context,'/login');})]), SliverPadding(padding:const EdgeInsets.all(14),sliver:SliverList(delegate:SliverChildListDelegate([ // Ruta de hoy Card(color:AppColors.moradoConductor.withOpacity(0.08), shape:RoundedRectangleBorder(borderRadius:BorderRadius.circular(12), side:BorderSide(color:AppColors.moradoConductor.withOpacity(0.3))), child:Padding(padding:const EdgeInsets.all(14),child:Column( crossAxisAlignment:CrossAxisAlignment.start, children:[ Row(children:[ const Icon(Icons.today,color:AppColors.moradoConductor), const SizedBox(width:8), Text('Hoy — ${_todayLabel()}', style:const TextStyle(fontWeight:FontWeight.bold,color:AppColors.moradoConductor,fontSize:15)), ]), const Divider(), if (widget.route != null)...[ Text(widget.route.name,style:const TextStyle(fontWeight:FontWeight.bold,fontSize:14)), Text('Camión ${widget.route.truckId} • Turno: ${widget.route.turno}', style:const TextStyle(color:AppColors.grisTexto,fontSize:12)), const SizedBox(height:8), Row(children:[ Icon(gpsOk?Icons.gps_fixed:Icons.gps_off, color:gpsOk?AppColors.verdeExito:AppColors.rojoError,size:16), const SizedBox(width:4), Text(gpsOk?'GPS Activo':'⚠️ GPS Desactivado', style:TextStyle(color:gpsOk?AppColors.verdeExito:AppColors.rojoError, fontWeight:FontWeight.bold,fontSize:12)), const Spacer(), Text('Posición ${posIdx+1}/8',style:const TextStyle(color:AppColors.grisTexto,fontSize:12)), ]), const SizedBox(height:8), LinearProgressIndicator(value:(posIdx+1)/8, backgroundColor:Colors.grey.shade300, valueColor:const AlwaysStoppedAnimation(AppColors.moradoConductor)), const SizedBox(height:6), Text(widget.sim.getEtaText(widget.todayRouteId??''), style:const TextStyle(fontSize:13,fontWeight:FontWeight.w500)), ] else const Text('⚠️ Sin ruta asignada hoy.',style:TextStyle(color:AppColors.rojoError)), ]))), const SizedBox(height:10), // Instrucciones Card(child:Padding(padding:const EdgeInsets.all(12),child:Column( crossAxisAlignment:CrossAxisAlignment.start, children:[ const Text('📋 Instrucciones de Ruta', style:TextStyle(fontWeight:FontWeight.bold,color:AppColors.moradoConductor)), const Divider(), const Text('• Sigue la ruta asignada sin desviaciones\n' '• Mantén el GPS activo en todo momento\n' '• Reporta incidentes desde "Incidente"\n' '• Si hay problema, el admin decidirá si se cancela o retrasa', style:TextStyle(fontSize:12,color:AppColors.grisTexto)), ]))), const SizedBox(height:10), // Reportes ciudadanos de SU ruta if (_ciudadanoReportes.isNotEmpty) ...[ Card(color:Colors.orange.shade50, shape:RoundedRectangleBorder(borderRadius:BorderRadius.circular(10), side:BorderSide(color:Colors.orange.shade200)), child:Padding(padding:const EdgeInsets.all(12),child:Column( crossAxisAlignment:CrossAxisAlignment.start, children:[ const Row(children:[ Icon(Icons.people,color:AppColors.naranjaAlerta,size:16), SizedBox(width:6), Text('Reportes de tu ruta hoy', style:TextStyle(fontWeight:FontWeight.bold,color:AppColors.naranjaAlerta,fontSize:13)), ]), const Divider(), ..._ciudadanoReportes.map((r)=>Padding( padding:const EdgeInsets.symmetric(vertical:3), child:Row(children:[ const Icon(Icons.person_outline,size:12,color:AppColors.grisTexto), const SizedBox(width:4), Expanded(child:Text(r.descripcion,style:const TextStyle(fontSize:11), maxLines:1,overflow:TextOverflow.ellipsis)), ]))), ]))), const SizedBox(height:10), ], // Horario LMV / MJS Card(child:Padding(padding:const EdgeInsets.all(12),child:Column( crossAxisAlignment:CrossAxisAlignment.start, children:[ const Text('Mi Horario', style:TextStyle(fontWeight:FontWeight.bold,color:AppColors.moradoConductor)), const Divider(), if (widget.assignments.isEmpty) const Text('Sin asignaciones. Contacta al administrador.', style:TextStyle(color:AppColors.grisTexto,fontSize:12)) else ...[ _scheduleGroup(widget.assignments,'LUNES','MIERCOLES','VIERNES', 'Lunes, Miércoles y Viernes'), const SizedBox(height:8), _scheduleGroup(widget.assignments,'MARTES','JUEVES','SABADO', 'Martes, Jueves y Sábado'), ], ]))), const SizedBox(height:80), ]))), ]); } Widget _scheduleGroup(List all, String d1, String d2, String d3, String label) { AssignmentModel? found; for (final dia in [d1,d2,d3]) { try { found = all.firstWhere((a)=>a.diaSemana==dia); break; } catch(_){} } return Container(padding:const EdgeInsets.all(10), decoration:BoxDecoration(color:AppColors.moradoConductor.withOpacity(0.06), borderRadius:BorderRadius.circular(8), border:Border.all(color:AppColors.moradoConductor.withOpacity(0.2))), child:Row(children:[ const Icon(Icons.calendar_today,size:14,color:AppColors.moradoConductor), const SizedBox(width:6), Expanded(child:Text(label,style:const TextStyle(fontWeight:FontWeight.w600,fontSize:12))), if (found!=null) Container(padding:const EdgeInsets.symmetric(horizontal:8,vertical:3), decoration:BoxDecoration(color:AppColors.moradoConductor,borderRadius:BorderRadius.circular(8)), child:Text('${found.routeId} • ${found.turno}', style:const TextStyle(fontSize:11,color:Colors.white,fontWeight:FontWeight.bold))) else const Text('Sin asignar',style:TextStyle(fontSize:11,color:AppColors.grisTexto)), ])); } String _todayLabel() { const d=['','Lunes','Martes','Miércoles','Jueves','Viernes','Sábado','Domingo']; return d[DateTime.now().weekday]; } } // ── Tab mapa ────────────────────────────────────────────────────────────── class _DriverMapTab extends StatelessWidget { final route; final sim; const _DriverMapTab({required this.route, required this.sim}); @override Widget build(BuildContext context) => Scaffold( appBar:AppBar(automaticallyImplyLeading:false, backgroundColor:AppColors.moradoConductor,foregroundColor:Colors.white, title:Text(route.name,style:const TextStyle(fontSize:13)), bottom:PreferredSize(preferredSize:const Size.fromHeight(4), child:Container(height:4,color:AppColors.dorado))), body:DriverRouteMap(route:route,simulator:sim)); } // ── Tab reporte incidente — usa routeId actual ──────────────────────────── class _DriverReportesTab extends StatefulWidget { final int? conductorId; final String? todayRouteId; // Ruta actual del conductor const _DriverReportesTab({required this.conductorId, required this.todayRouteId}); @override State<_DriverReportesTab> createState() => _DriverReportesTabState(); } class _DriverReportesTabState extends State<_DriverReportesTab> { String _tipo = 'INCIDENTE_LLANTA'; final _desc = TextEditingController(); bool _loading = false, _sent = false; List _misIncidentes = []; static const _tipos = { 'INCIDENTE_LLANTA': '🔧 Llanta ponchada', 'INCIDENTE_MECANICA': '🔥 Falla mecánica', 'INCIDENTE_ACCIDENTE': '🚑 Accidente', 'INCIDENTE_CAMINO': '🚧 Camino bloqueado', 'INCIDENTE_COMBUSTIBLE':'⛽ Sin combustible', 'INCIDENTE_OTRO': '📝 Otro', }; @override void initState() { super.initState(); _load(); } Future _load() async { final all = await DbHelper.getAlertas(); // Solo incidentes de la ruta actual del conductor final mine = all.where((a) => a.tipo.startsWith('INCIDENTE_') && a.routeId == (widget.todayRouteId ?? '')).toList(); if (mounted) setState(() => _misIncidentes = mine); } Future _enviar() async { if (widget.todayRouteId == null) { ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content:Text('No tienes ruta asignada hoy'), backgroundColor:AppColors.rojoError)); return; } if (_desc.text.trim().isEmpty) { ScaffoldMessenger.of(context).showSnackBar(const SnackBar( content:Text('Describe el incidente'),backgroundColor:AppColors.rojoError)); return; } setState(()=>_loading=true); // Guardar el incidente asociado a la RUTA ACTUAL await DbHelper.insertAlerta(AlertaModel( tipo: _tipo, routeId: widget.todayRouteId!, // ← ID de la ruta actual, no del conductor mensaje: '${_tipos[_tipo]}: ${_desc.text.trim()}', fecha: DateTime.now().toIso8601String(), )); await _load(); if (!mounted) return; setState(() { _loading=false; _sent=true; }); _desc.clear(); await Future.delayed(const Duration(seconds:2)); if (mounted) setState(()=>_sent=false); } @override Widget build(BuildContext context) => Scaffold( backgroundColor:AppColors.grisFondo, appBar:AppBar(automaticallyImplyLeading:false, backgroundColor:AppColors.moradoConductor,foregroundColor:Colors.white, title:const Text('Reportar Incidente'), bottom:PreferredSize(preferredSize:const Size.fromHeight(4), child:Container(height:4,color:AppColors.dorado))), body: _sent ? const Center(child:Column(mainAxisAlignment:MainAxisAlignment.center,children:[ Icon(Icons.check_circle,color:AppColors.verdeExito,size:64), SizedBox(height:12), Text('¡Incidente reportado!',style:TextStyle(fontSize:18,fontWeight:FontWeight.bold,color:AppColors.verdeExito)), Text('El administrador será notificado.',style:TextStyle(color:AppColors.grisTexto)), ])) : SingleChildScrollView(padding:const EdgeInsets.all(16),child:Column(children:[ // Info ruta actual if (widget.todayRouteId != null) Container(margin:const EdgeInsets.only(bottom:12), padding:const EdgeInsets.all(10), decoration:BoxDecoration(color:AppColors.moradoConductor.withOpacity(0.08), borderRadius:BorderRadius.circular(8), border:Border.all(color:AppColors.moradoConductor.withOpacity(0.3))), child:Row(children:[ const Icon(Icons.route,color:AppColors.moradoConductor,size:16), const SizedBox(width:6), Text('Incidente en: ${widget.todayRouteId}', style:const TextStyle(fontWeight:FontWeight.bold,color:AppColors.moradoConductor,fontSize:13)), ])) else Container(margin:const EdgeInsets.only(bottom:12), padding:const EdgeInsets.all(10), decoration:BoxDecoration(color:Colors.orange.shade50,borderRadius:BorderRadius.circular(8)), child:const Text('⚠️ No tienes ruta asignada hoy', style:TextStyle(color:AppColors.naranjaAlerta,fontWeight:FontWeight.bold))), Card(shape:RoundedRectangleBorder(borderRadius:BorderRadius.circular(12)), child:Padding(padding:const EdgeInsets.all(16),child:Column( crossAxisAlignment:CrossAxisAlignment.start, children:[ const Text('Tipo de incidente', style:TextStyle(fontWeight:FontWeight.bold,color:AppColors.moradoConductor,fontSize:15)), const SizedBox(height:8), ..._tipos.entries.map((e)=>RadioListTile(dense:true, value:e.key,groupValue:_tipo, title:Text(e.value,style:const TextStyle(fontSize:13)), activeColor:AppColors.moradoConductor, onChanged:(v)=>setState(()=>_tipo=v!))), const SizedBox(height:10), const Text('Descripción',style:TextStyle(fontWeight:FontWeight.w600,fontSize:13)), const SizedBox(height:6), TextField(controller:_desc,maxLines:3, decoration:const InputDecoration(hintText:'Describe qué pasó...', border:OutlineInputBorder(),filled:true,fillColor:Colors.white)), const SizedBox(height:12), Container(padding:const EdgeInsets.all(10), decoration:BoxDecoration(color:Colors.orange.shade50, borderRadius:BorderRadius.circular(8), border:Border.all(color:Colors.orange.shade200)), child:const Text( '⚠️ El administrador verá este incidente en tu ruta actual ' 'y decidirá si continúa, se retrasa o se cancela.', style:TextStyle(fontSize:11,color:Colors.black87))), const SizedBox(height:14), SizedBox(width:double.infinity,height:48, child:ElevatedButton.icon( onPressed:(_loading||widget.todayRouteId==null)?null:_enviar, style:ElevatedButton.styleFrom(backgroundColor:AppColors.moradoConductor, foregroundColor:Colors.white, shape:RoundedRectangleBorder(borderRadius:BorderRadius.circular(8))), icon:_loading?const SizedBox(width:18,height:18, child:CircularProgressIndicator(color:Colors.white,strokeWidth:2)) :const Icon(Icons.send), label:const Text('ENVIAR INCIDENTE',style:TextStyle(fontWeight:FontWeight.bold)))), ]))), if (_misIncidentes.isNotEmpty)...[ const SizedBox(height:16), const Align(alignment:Alignment.centerLeft, child:Text('Mis incidentes de hoy', style:TextStyle(fontWeight:FontWeight.bold,color:AppColors.moradoConductor,fontSize:14))), const SizedBox(height:8), ..._misIncidentes.take(5).map((a)=>Card(margin:const EdgeInsets.only(bottom:6), child:ListTile(dense:true, leading:CircleAvatar(backgroundColor:AppColors.moradoConductor,radius:16, child:const Icon(Icons.warning,color:Colors.white,size:14)), title:Text(_tipos[a.tipo]??a.tipo, style:const TextStyle(fontSize:12,fontWeight:FontWeight.w600)), subtitle:Text(a.mensaje,maxLines:1,overflow:TextOverflow.ellipsis, style:const TextStyle(fontSize:11)), trailing:Icon(a.resuelta?Icons.check_circle:Icons.pending, color:a.resuelta?AppColors.verdeExito:AppColors.naranjaAlerta,size:18)))), ], ])), ); @override void dispose(){ _desc.dispose(); super.dispose(); } } // ── Notif banner conductor ──────────────────────────────────────────────── class _NotifBanner extends StatelessWidget { final AppNotification notif; final VoidCallback onDismiss; const _NotifBanner({required this.notif, required this.onDismiss}); @override Widget build(BuildContext context) { final color = notif.event==NotifEvent.gpsLost?Colors.red.shade800 :notif.event==NotifEvent.truckStopped?AppColors.naranjaAlerta :notif.event==NotifEvent.routeCancelled?AppColors.rojoError :AppColors.moradoConductor; return Material(color:Colors.transparent, child:Container(margin:const EdgeInsets.all(10), decoration:BoxDecoration(color:color,borderRadius:BorderRadius.circular(12), boxShadow:const[BoxShadow(color:Colors.black26,blurRadius:6)]), child:Padding(padding:const EdgeInsets.all(12),child:Row(children:[ const Icon(Icons.notification_important,color:Colors.white,size:22), const SizedBox(width:8), Expanded(child:Column(crossAxisAlignment:CrossAxisAlignment.start, mainAxisSize:MainAxisSize.min,children:[ Text(notif.title,style:const TextStyle(color:Colors.white,fontWeight:FontWeight.bold,fontSize:13)), Text(notif.body,style:const TextStyle(color:Colors.white70,fontSize:11), maxLines:2,overflow:TextOverflow.ellipsis), ])), IconButton(icon:const Icon(Icons.close,color:Colors.white,size:18),onPressed:onDismiss), ])))); } }