import 'package:siro_admin/constant/map_tiles.dart'; import 'package:siro_admin/constant/theme.dart'; import 'dart:async'; import 'dart:convert'; import 'dart:math' as math; import 'package:flutter/material.dart'; import 'package:flutter_map/flutter_map.dart'; import 'package:latlong2/latlong.dart'; import 'package:http/http.dart' as http; import 'package:siro_admin/constant/links.dart'; import 'package:url_launcher/url_launcher.dart'; import '../../../constant/box_name.dart'; import '../../../controller/functions/crud.dart'; import '../../../main.dart'; class SiroTrackerScreen extends StatefulWidget { const SiroTrackerScreen({super.key}); @override State createState() => _SiroTrackerScreenState(); } class _SiroTrackerScreenState extends State with TickerProviderStateMixin { final MapController _mapController = MapController(); List _markers = []; bool isLiveMode = true; bool isLoading = false; String lastUpdated = "جاري التحميل..."; int liveCount = 0; int dayCount = 0; Timer? _timer; late AnimationController _fadeController; late AnimationController _scaleController; String myPhone = box.read(BoxName.adminPhone).toString(); bool get isSuperAdmin => myPhone == '963942542053' || myPhone == '963992952235'; final String _baseDir = "${AppLink.server}/ride/location/"; @override void initState() { super.initState(); _initAnimations(); fetchData(); _timer = Timer.periodic(const Duration(minutes: 5), (timer) { if (mounted) fetchData(); }); } void _initAnimations() { _fadeController = AnimationController( duration: const Duration(milliseconds: 800), vsync: this, ); _scaleController = AnimationController( duration: const Duration(milliseconds: 600), vsync: this, ); _fadeController.forward(); _scaleController.forward(); } @override void dispose() { _timer?.cancel(); _mapController.dispose(); _fadeController.dispose(); _scaleController.dispose(); super.dispose(); } Future _makePhoneCall(String phoneNumber) async { final Uri launchUri = Uri(scheme: 'tel', path: phoneNumber); if (await canLaunchUrl(launchUri)) { await launchUrl(launchUri); } else { _showSnackBar("لا يمكن إجراء الاتصال لهذا الرقم"); } } void _showSnackBar(String message) { final cs = Theme.of(context).colorScheme; ScaffoldMessenger.of(context).showSnackBar( SnackBar( content: Text(message), backgroundColor: cs.surfaceContainerHighest, behavior: SnackBarBehavior.floating, margin: const EdgeInsets.all(16), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), ), ); } Future fetchData() async { if (!mounted) return; setState(() => isLoading = true); try { String updateUrl = "${_baseDir}getUpdatedLocationForAdmin.php?mode=${isLiveMode ? 'live' : 'day'}"; print("📡 Calling Update URL: $updateUrl"); var responseUpdate = await CRUD().post(link: updateUrl, payload: {}); print("📡 Update Response: $responseUpdate"); String v = DateTime.now().millisecondsSinceEpoch.toString(); String liveUrl = "${_baseDir}locations_live.json?v=$v"; print("📡 Calling Live JSON URL: $liveUrl"); final responseLive = await http.get(Uri.parse(liveUrl)); print( "📡 Live JSON Response (${responseLive.statusCode}): ${responseLive.body.length > 100 ? responseLive.body.substring(0, 100) : responseLive.body}"); if (responseLive.statusCode == 200) { final data = json.decode(responseLive.body); List drivers = (data is Map && data.containsKey('drivers')) ? data['drivers'] : data; setState(() { liveCount = drivers.length; if (isLiveMode) _buildMarkers(drivers); }); } String dayUrl = "${_baseDir}locations_day.json?v=$v"; print("📡 Calling Day JSON URL: $dayUrl"); final responseDay = await http.get(Uri.parse(dayUrl)); print( "📡 Day JSON Response (${responseDay.statusCode}): ${responseDay.body.length > 100 ? responseDay.body.substring(0, 100) : responseDay.body}"); if (responseDay.statusCode == 200) { final data = json.decode(responseDay.body); List drivers = (data is Map && data.containsKey('drivers')) ? data['drivers'] : data; setState(() { dayCount = drivers.length; if (!isLiveMode) _buildMarkers(drivers); }); } setState(() { lastUpdated = DateTime.now().toString().substring(11, 19); }); } catch (e) { print("Exception: $e"); setState(() => lastUpdated = "خطأ في الاتصال"); } finally { if (mounted) setState(() => isLoading = false); } } void _buildMarkers(List drivers) { final cs = Theme.of(context).colorScheme; List newMarkers = []; for (var d in drivers) { double lat = double.tryParse((d['lat'] ?? "0").toString()) ?? 0.0; double lon = double.tryParse((d['lon'] ?? "0").toString()) ?? 0.0; double heading = double.tryParse((d['heading'] ?? "0").toString()) ?? 0.0; String id = (d['id'] ?? "Unknown").toString(); String speed = (d['speed'] ?? "0").toString(); String name = (d['name'] ?? "كابتن").toString(); String phone = (d['phone'] ?? "").toString(); String completed = (d['completed'] ?? "0").toString(); String cancelled = (d['cancelled'] ?? "0").toString(); if (lat != 0 && lon != 0) { newMarkers.add( Marker( point: LatLng(lat, lon), width: 60, height: 60, child: GestureDetector( onTap: () { _showDriverInfoDialog( driverId: id, name: name, phone: phone, speed: speed, heading: heading, completed: completed, cancelled: cancelled, ); }, child: _buildMarkerWidget(heading, cs), ), ), ); } } setState(() { _markers = newMarkers; }); } Widget _buildMarkerWidget(double heading, ColorScheme cs) { return Stack( alignment: Alignment.center, children: [ Container( width: 44, height: 44, decoration: BoxDecoration( shape: BoxShape.circle, gradient: LinearGradient( colors: isLiveMode ? [cs.success, cs.success] : [cs.tertiary, cs.tertiary], begin: Alignment.topLeft, end: Alignment.bottomRight, ), boxShadow: [ BoxShadow( color: (isLiveMode ? cs.success : cs.tertiary) .withValues(alpha: 0.5), blurRadius: 12, spreadRadius: 2, ) ], ), ), Transform.rotate( angle: heading * (math.pi / 180), child: Icon( Icons.navigation, color: cs.onPrimary, size: 26, ), ), ], ); } void _showDriverInfoDialog({ required String driverId, required String name, required String phone, required String speed, required double heading, required String completed, required String cancelled, }) { final cs = Theme.of(context).colorScheme; showDialog( context: context, builder: (_) => Dialog( shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(24)), backgroundColor: Colors.transparent, child: Container( decoration: BoxDecoration( gradient: LinearGradient( colors: [cs.surface, cs.surfaceContainerHighest], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(24), boxShadow: [ BoxShadow( color: cs.shadow.withValues(alpha: 0.15), blurRadius: 30, spreadRadius: 5, ) ], ), child: Padding( padding: const EdgeInsets.all(24), child: Column( mainAxisSize: MainAxisSize.min, children: [ Container( padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), decoration: BoxDecoration( gradient: LinearGradient( colors: isLiveMode ? [cs.success, cs.success] : [cs.tertiary, cs.tertiary], ), borderRadius: BorderRadius.circular(12), ), child: Row( mainAxisSize: MainAxisSize.min, children: [ Icon(Icons.person_outline, color: cs.onPrimary, size: 20), const SizedBox(width: 8), Text( "معلومات الكابتن", style: TextStyle( fontSize: 18, fontWeight: FontWeight.bold, color: cs.onPrimary, ), ), ], ), ), const SizedBox(height: 20), _buildInfoCard(Icons.person, "الاسم", name, cs), const SizedBox(height: 12), _buildInfoCard(Icons.badge, "المعرف", driverId, cs), const SizedBox(height: 12), _buildInfoCard(Icons.speed, "السرعة", "$speed كم/س", cs), const SizedBox(height: 20), _buildStatsContainer(completed, cancelled, cs), if (isSuperAdmin) ...[ const SizedBox(height: 16), _buildPhoneButton(phone, cs), ], const SizedBox(height: 20), SizedBox( width: double.infinity, child: ElevatedButton( onPressed: () => Navigator.pop(context), style: ElevatedButton.styleFrom( backgroundColor: cs.onSurface, foregroundColor: cs.onPrimary, padding: const EdgeInsets.symmetric(vertical: 14), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(12), ), elevation: 0, ), child: const Text( "إغلاق", style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, ), ), ), ) ], ), ), ), ), ); } Widget _buildInfoCard( IconData icon, String label, String value, ColorScheme cs) { return Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: cs.surface, borderRadius: BorderRadius.circular(12), border: Border.all( color: cs.outline, width: 1, ), boxShadow: [ BoxShadow( color: cs.shadow.withValues(alpha: 0.05), blurRadius: 8, ) ], ), child: Row( children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: cs.surfaceContainerHighest, borderRadius: BorderRadius.circular(8), ), child: Icon(icon, color: cs.onSurface, size: 20), ), const SizedBox(width: 12), Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, style: TextStyle( fontSize: 12, color: cs.onSurfaceVariant, fontWeight: FontWeight.w500, ), ), const SizedBox(height: 4), Text( value, style: TextStyle( fontSize: 15, fontWeight: FontWeight.bold, color: cs.onSurface, ), ), ], ), ], ), ); } Widget _buildStatsContainer( String completed, String cancelled, ColorScheme cs) { return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( gradient: LinearGradient( colors: [cs.surfaceContainerHighest, cs.surfaceContainerHighest], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(12), border: Border.all(color: cs.outline), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceEvenly, children: [ _buildStatItem("✓ مكتملة", completed, cs.success, cs), Container( width: 1, height: 40, color: cs.outline, ), _buildStatItem("✕ ملغاة", cancelled, cs.error, cs), ], ), ); } Widget _buildStatItem( String label, String value, Color color, ColorScheme cs) { return Column( children: [ Text( value, style: TextStyle( color: color, fontWeight: FontWeight.bold, fontSize: 20, ), ), const SizedBox(height: 4), Text( label, style: TextStyle( fontSize: 12, color: cs.onSurfaceVariant, fontWeight: FontWeight.w500, ), ), ], ); } Widget _buildPhoneButton(String phone, ColorScheme cs) { return InkWell( onTap: () { if (phone.isNotEmpty) _makePhoneCall(phone); }, borderRadius: BorderRadius.circular(12), child: Container( width: double.infinity, padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 14), decoration: BoxDecoration( gradient: LinearGradient( colors: [cs.tertiary, cs.tertiary], begin: Alignment.topLeft, end: Alignment.bottomRight, ), borderRadius: BorderRadius.circular(12), boxShadow: [ BoxShadow( color: cs.tertiary.withValues(alpha: 0.4), blurRadius: 12, spreadRadius: 2, ) ], ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ Icon(Icons.call, color: cs.onPrimary, size: 20), const SizedBox(width: 10), Expanded( child: Text( phone, style: TextStyle( color: cs.onPrimary, fontWeight: FontWeight.bold, fontSize: 15, ), textAlign: TextAlign.center, ), ), ], ), ), ); } @override Widget build(BuildContext context) { final cs = Theme.of(context).colorScheme; return Scaffold( backgroundColor: cs.surface, body: Stack( children: [ FlutterMap( mapController: _mapController, options: const MapOptions( initialCenter: LatLng(33.513, 36.276), initialZoom: 10.0, ), children: [ MapTiles.layer(context), MarkerLayer(markers: _markers), ], ), Positioned( top: 0, left: 0, right: 0, child: Container( padding: const EdgeInsets.fromLTRB(16, 50, 16, 16), child: Row( children: [ GestureDetector( onTap: () => Navigator.pop(context), child: Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: cs.surfaceContainerHighest, borderRadius: BorderRadius.circular(10), border: Border.all(color: cs.outline), ), child: Icon(Icons.arrow_back_ios_new_rounded, color: cs.onSurfaceVariant, size: 16), ), ), const SizedBox(width: 12), Text('نظام تتبع الكابتن', style: TextStyle( color: cs.onSurface, fontSize: 17, fontWeight: FontWeight.bold)), ], ), ), ), _buildDashboard(cs), ], ), ); } Widget _buildDashboard(ColorScheme cs) { return Positioned( top: 100, right: 16, child: FadeTransition( opacity: _fadeController, child: ScaleTransition( scale: _scaleController, child: Container( width: 300, decoration: BoxDecoration( color: cs.surface, borderRadius: BorderRadius.circular(20), boxShadow: [ BoxShadow( color: cs.shadow.withValues(alpha: 0.15), blurRadius: 30, spreadRadius: 5, ) ], ), child: ClipRRect( borderRadius: BorderRadius.circular(20), child: Column( children: [ Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( gradient: LinearGradient( colors: [cs.onSurface, cs.onSurfaceVariant], begin: Alignment.topLeft, end: Alignment.bottomRight, ), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Icon(Icons.dashboard, color: cs.onPrimary, size: 22), Text( "لوحة التحكم", style: TextStyle( color: cs.onPrimary, fontSize: 16, fontWeight: FontWeight.bold, ), ), ], ), ), Padding( padding: const EdgeInsets.all(16), child: Column( crossAxisAlignment: CrossAxisAlignment.end, children: [ Row( children: [ Expanded( child: _buildModeButton( "أرشيف اليوم", !isLiveMode, () { setState(() => isLiveMode = false); fetchData(); }, cs.tertiary, cs, ), ), const SizedBox(width: 10), Expanded( child: _buildModeButton( "مباشر", isLiveMode, () { setState(() => isLiveMode = true); fetchData(); }, cs.success, cs, ), ), ], ), const SizedBox(height: 16), _buildStatRow( icon: Icons.live_tv, label: "نشط الآن (مباشر)", value: liveCount.toString(), color: cs.success, cs: cs, ), const SizedBox(height: 12), _buildStatRow( icon: Icons.history, label: "إجمالي اليوم", value: dayCount.toString(), color: cs.tertiary, cs: cs, ), const SizedBox(height: 14), Container( padding: const EdgeInsets.all(10), decoration: BoxDecoration( color: cs.surfaceContainerHighest, borderRadius: BorderRadius.circular(8), border: Border.all(color: cs.outline), ), child: Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( isLoading ? "جاري التحديث..." : "تحديث: $lastUpdated", style: TextStyle( fontSize: 11, color: cs.onSurfaceVariant, fontWeight: FontWeight.w500, ), ), Icon( isLoading ? Icons.hourglass_bottom : Icons.check_circle, size: 14, color: isLoading ? cs.tertiary : cs.success, ), ], ), ), const SizedBox(height: 12), SizedBox( width: double.infinity, child: ElevatedButton( onPressed: isLoading ? null : fetchData, style: ElevatedButton.styleFrom( backgroundColor: cs.onSurface, foregroundColor: cs.onPrimary, disabledBackgroundColor: cs.outline, padding: const EdgeInsets.symmetric(vertical: 12), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(10), ), elevation: 0, ), child: Row( mainAxisAlignment: MainAxisAlignment.center, children: [ if (isLoading) SizedBox( width: 16, height: 16, child: CircularProgressIndicator( strokeWidth: 2, valueColor: AlwaysStoppedAnimation( cs.onPrimary, ), ), ) else const Icon(Icons.refresh, size: 18), const SizedBox(width: 8), const Text( "تحديث البيانات", style: TextStyle( fontWeight: FontWeight.bold, fontSize: 14, ), ), ], ), ), ), ], ), ), ], ), ), ), ), ), ); } Widget _buildModeButton( String title, bool active, VoidCallback onTap, Color color, ColorScheme cs, ) { return InkWell( onTap: onTap, borderRadius: BorderRadius.circular(10), child: AnimatedContainer( duration: const Duration(milliseconds: 300), padding: const EdgeInsets.symmetric(vertical: 10), alignment: Alignment.center, decoration: BoxDecoration( gradient: active ? LinearGradient( colors: [color, color.withValues(alpha: 0.8)], begin: Alignment.topLeft, end: Alignment.bottomRight, ) : null, color: active ? null : cs.surfaceContainerHighest, borderRadius: BorderRadius.circular(10), border: Border.all( color: active ? color : cs.outline, width: 1.5, ), boxShadow: active ? [ BoxShadow( color: color.withValues(alpha: 0.3), blurRadius: 8, spreadRadius: 1, ) ] : null, ), child: Text( title, style: TextStyle( color: active ? cs.onPrimary : cs.onSurfaceVariant, fontWeight: active ? FontWeight.bold : FontWeight.w600, fontSize: 13, ), ), ), ); } Widget _buildStatRow({ required IconData icon, required String label, required String value, required Color color, required ColorScheme cs, }) { return Row( children: [ Container( padding: const EdgeInsets.all(8), decoration: BoxDecoration( color: color.withValues(alpha: 0.1), borderRadius: BorderRadius.circular(8), ), child: Icon(icon, color: color, size: 18), ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( label, style: TextStyle( fontSize: 12, color: cs.onSurfaceVariant, fontWeight: FontWeight.w500, ), ), const SizedBox(height: 2), Text( value, style: TextStyle( fontSize: 16, fontWeight: FontWeight.bold, color: color, ), ), ], ), ), ], ); } }