import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:go_router/go_router.dart'; import '../../../core/theme/app_colors.dart'; import '../../../core/theme/app_typography.dart'; import '../../../services/alerts/road_alert_models.dart'; import '../../../services/alerts/road_alert_service.dart'; import '../../../services/group_rides/group_ride_models.dart'; import '../../../services/group_rides/group_ride_providers.dart'; import '../../../services/location_service.dart'; /// Top alert and notification banner region for the dashboard. /// /// Subscribes strictly to permission, road alert, and group ride providers. /// Does NOT rebuild on high-frequency telemetry samples. class DashboardAlertRegion extends ConsumerWidget { const DashboardAlertRegion({super.key}); @override Widget build(BuildContext context, WidgetRef ref) { final hasLocPermissionAsync = ref.watch(locationPermissionGrantedProvider); final hasLocPermission = hasLocPermissionAsync.value ?? true; final alertsAsync = ref.watch(activeAlertsStreamProvider); final activeAlerts = alertsAsync.value ?? []; final closestAlert = activeAlerts.isNotEmpty ? activeAlerts.first : null; final activeGroupRide = ref.watch(activeUserGroupRideProvider); return Column( mainAxisSize: MainAxisSize.min, children: [ if (!hasLocPermission) _buildPermissionBanner(context, ref), if (closestAlert != null) _buildRoadAlertBanner(context, closestAlert, ref), if (activeGroupRide != null) _buildGroupRideBanner(context, activeGroupRide), ], ); } Widget _buildPermissionBanner(BuildContext context, WidgetRef ref) { return Container( width: double.infinity, color: AppColors.darkAmber, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), child: Row( children: [ const Icon(Icons.location_off, color: Colors.white, size: 20), const SizedBox(width: 10), Expanded( child: Text( 'Location permission needed for GPS speedometer.', style: TextStyle( fontFamily: AppTypography.fontBody, fontSize: 12, color: Colors.white, fontWeight: FontWeight.bold, ), ), ), const SizedBox(width: 8), ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: Colors.white, foregroundColor: Colors.black, padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), minimumSize: Size.zero, tapTargetSize: MaterialTapTargetSize.shrinkWrap, ), onPressed: () async { final locService = ref.read(locationServiceProvider); await locService.requestPermissions(); ref.invalidate(locationPermissionGrantedProvider); }, child: const Text('Enable', style: TextStyle(fontSize: 12, fontWeight: FontWeight.bold)), ), ], ), ); } Widget _buildRoadAlertBanner(BuildContext context, RoadAlert alert, WidgetRef ref) { return Container( width: double.infinity, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( color: alert.category.color.withValues(alpha: 0.2), border: Border(bottom: BorderSide(color: alert.category.color, width: 2)), ), child: Row( children: [ Icon(alert.category.icon, color: alert.category.color, size: 26), const SizedBox(width: 10), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( alert.category.displayName.toUpperCase(), overflow: TextOverflow.ellipsis, style: TextStyle( fontFamily: AppTypography.fontHeadline, color: alert.category.color, fontWeight: FontWeight.bold, fontSize: 13, letterSpacing: 0.8, ), ), Text( '${alert.distanceFromUser?.toStringAsFixed(0) ?? '0'}m Ahead • ${alert.roadName}', overflow: TextOverflow.ellipsis, style: TextStyle( fontFamily: AppTypography.fontBody, color: Colors.white, fontSize: 12, fontWeight: FontWeight.w600, ), ), ], ), ), const SizedBox(width: 8), IconButton( icon: const Icon(Icons.close, color: Colors.white70, size: 18), padding: EdgeInsets.zero, constraints: const BoxConstraints(minWidth: 32, minHeight: 32), tooltip: 'Dismiss alert', onPressed: () { ref.read(roadAlertServiceProvider).dismissAlert(alert.id); }, ), ], ), ); } Widget _buildGroupRideBanner(BuildContext context, GroupRide ride) { return InkWell( onTap: () => context.push('/group-rides/${ride.id}/live'), child: Container( width: double.infinity, padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8), decoration: BoxDecoration( color: AppColors.cyberCyan.withValues(alpha: 0.15), border: const Border( bottom: BorderSide(color: AppColors.cyberCyan, width: 1.5), ), ), child: Row( children: [ const Icon(Icons.two_wheeler, color: AppColors.cyberCyan, size: 20), const SizedBox(width: 10), Expanded( child: Row( children: [ Flexible( child: Text( 'GROUP RIDE ACTIVE: ${ride.title.toUpperCase()}', overflow: TextOverflow.ellipsis, style: TextStyle( fontFamily: AppTypography.fontHeadline, color: AppColors.cyberCyan, fontWeight: FontWeight.bold, fontSize: 12, letterSpacing: 0.6, ), ), ), const SizedBox(width: 8), Container( padding: const EdgeInsets.symmetric(horizontal: 6, vertical: 2), decoration: BoxDecoration( color: AppColors.cyberCyan.withValues(alpha: 0.25), borderRadius: BorderRadius.circular(4), ), child: Text( '${ride.participantCount} RIDERS', style: TextStyle( fontFamily: AppTypography.fontTelemetry, color: Colors.white, fontSize: 10, fontWeight: FontWeight.bold, ), ), ), ], ), ), const SizedBox(width: 8), const Icon(Icons.chevron_right, color: AppColors.cyberCyan, size: 18), ], ), ), ); } }