import 'dart:convert'; import 'package:flutter/cupertino.dart'; import 'package:flutter/material.dart'; import 'package:get/get.dart'; import 'package:google_maps_flutter/google_maps_flutter.dart'; // لتحديد الأنواع إذا لزم import '../../constant/box_name.dart'; import '../../constant/colors.dart'; import '../../constant/links.dart'; import '../../constant/style.dart'; import '../../controller/functions/crud.dart'; import '../../controller/home/captin/home_captain_controller.dart'; import '../../controller/notification/ride_available_controller.dart'; import '../../main.dart'; // للوصول للـ box import '../home/Captin/driver_map_page.dart'; import '../widgets/my_scafold.dart'; import '../widgets/mycircular.dart'; import '../widgets/mydialoug.dart'; class AvailableRidesPage extends StatelessWidget { const AvailableRidesPage({super.key}); @override Widget build(BuildContext context) { // حقن الكنترولر (تأكد أنك تستخدم الكود الجديد الذي أعطيتك إياه للكنترولر) Get.lazyPut(() => RideAvailableController()); Get.lazyPut(() => HomeCaptainController()); return GetBuilder( builder: (controller) { return MyScafolld( title: 'Available for rides'.tr, isleading: true, body: [ controller.isLoading ? const Center( child: Padding( padding: EdgeInsets.only(top: 50.0), child: MyCircularProgressIndicator(), )) : Builder( builder: (context) { // 1. الفلترة حسب نوع السيارة (تم نقل المنطق للكنترولر، لكن هنا للعرض فقط) // الكنترولر الجديد يفلتر عند الإضافة، لكن لا ضرر من التأكيد هنا final ridesList = controller.availableRides; if (ridesList.isEmpty) { return Center( child: Column( mainAxisAlignment: MainAxisAlignment.center, children: [ const SizedBox(height: 100), Icon(CupertinoIcons.car_detailed, size: 60, color: AppColor.primaryColor.withOpacity(0.5)), const SizedBox(height: 20), Text( "No rides available right now.".tr, style: AppStyle.subtitle, ), const SizedBox(height: 20), ElevatedButton.icon( onPressed: () => controller.getRideAvailable( forceRefresh: true), icon: const Icon(Icons.refresh), label: Text("Refresh Market".tr), style: ElevatedButton.styleFrom( backgroundColor: AppColor.primaryColor, foregroundColor: Colors.white, ), ) ], ), ); } // 2. عرض القائمة return RefreshIndicator( onRefresh: () async { await controller.getRideAvailable(forceRefresh: true); }, child: ListView.builder( padding: const EdgeInsets.symmetric( horizontal: 12, vertical: 16), itemCount: ridesList.length, itemBuilder: (context, index) { return RideAvailableCard( rideInfo: ridesList[index], ); }, ), ); }, ) ], ); }, ); } } // ============================================================================= // بطاقة الرحلة (The Card) // ============================================================================= class RideAvailableCard extends StatelessWidget { final Map rideInfo; const RideAvailableCard({Key? key, required this.rideInfo}) : super(key: key); @override Widget build(BuildContext context) { return Card( margin: const EdgeInsets.only(bottom: 16.0), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(16)), elevation: 4, shadowColor: Colors.black.withOpacity(0.1), child: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ _buildHeader(), const SizedBox(height: 16), _buildRouteInfo(), const Divider(height: 24, thickness: 0.5), _buildRideDetails(), const SizedBox(height: 20), _buildAcceptButton(), ], ), ), ); } // --------------------------------------------------------------------------- // تصميم البطاقة (Header, Route, Details) // --------------------------------------------------------------------------- Widget _buildHeader() { return Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text('Price'.tr, style: AppStyle.subtitle.copyWith(fontSize: 12)), Text( '${rideInfo['price']} ${'SYP'.tr}', // العملة style: AppStyle.title.copyWith( fontSize: 20, color: AppColor.primaryColor, height: 1.2), ), ], ), Container( padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6), decoration: BoxDecoration( color: AppColor.greenColor.withOpacity(0.1), borderRadius: BorderRadius.circular(20), border: Border.all(color: AppColor.greenColor.withOpacity(0.3)), ), child: Text( rideInfo['carType'] ?? 'Fixed Price'.tr, style: AppStyle.title .copyWith(color: AppColor.greenColor, fontSize: 13), ), ), ], ); } Widget _buildRouteInfo() { return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Column( children: [ const Icon(Icons.my_location, color: AppColor.primaryColor, size: 18), Container( height: 30, width: 1, color: Colors.grey.shade300, margin: const EdgeInsets.symmetric(vertical: 4), ), const Icon(Icons.location_on, color: Colors.red, size: 18), ], ), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(rideInfo['startName'] ?? 'Unknown Location'.tr, maxLines: 1, overflow: TextOverflow.ellipsis, style: AppStyle.title.copyWith(fontSize: 14)), const SizedBox(height: 22), Text(rideInfo['endName'] ?? 'Destination'.tr, maxLines: 1, overflow: TextOverflow.ellipsis, style: AppStyle.title.copyWith(fontSize: 14)), ], ), ) ], ); } Widget _buildRideDetails() { return Row( mainAxisAlignment: MainAxisAlignment.spaceAround, children: [ _infoItem(Icons.social_distance, '${rideInfo['distance']} KM'), _infoItem(Icons.access_time, '${rideInfo['duration']} Min'), _infoItem(Icons.star, '${rideInfo['passengerRate'] ?? 5.0}', iconColor: Colors.amber), ], ); } Widget _infoItem(IconData icon, String text, {Color iconColor = Colors.grey}) { return Row( children: [ Icon(icon, size: 16, color: iconColor), const SizedBox(width: 4), Text(text, style: AppStyle.subtitle.copyWith(fontSize: 13)), ], ); } // --------------------------------------------------------------------------- // زر القبول والمنطق الكامل (Accept Logic) 🔥 // --------------------------------------------------------------------------- Widget _buildAcceptButton() { return SizedBox( width: double.infinity, height: 50, child: ElevatedButton( onPressed: _acceptRideNewLogic, style: ElevatedButton.styleFrom( backgroundColor: AppColor.greenColor, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), elevation: 2, ), child: Text( 'Accept Ride'.tr, style: const TextStyle( color: Colors.white, fontSize: 16, fontWeight: FontWeight.bold), ), ), ); } // 🔥🔥🔥 الوظيفة الأهم: قبول الرحلة وتجهيز البيانات 🔥🔥🔥 void _acceptRideNewLogic() async { // 1. إظهار Loading Get.dialog( const Center(child: MyCircularProgressIndicator()), barrierDismissible: false, ); try { String driverId = box.read(BoxName.driverID).toString(); // 2. إرسال الطلب للسيرفر (acceptRide.php الجديد) var response = await CRUD().post( link: "${AppLink.ride}/rides/acceptRide.php", payload: { 'id': rideInfo['id'].toString(), 'driver_id': driverId, 'status': 'Apply', // الحالة المتفق عليها 'passengerToken': rideInfo['passengerToken'].toString(), }, ); // إخفاء الـ Loading Get.back(); // 3. تحليل الرد var jsonResponse = jsonDecode(response); if (jsonResponse['status'] == 'success') { // ✅ نجاح: أنت الفائز بالرحلة // تحديث حالة السائق محلياً Get.find().changeRideId(); box.write(BoxName.statusDriverLocation, 'on'); // 🔥 تجهيز الـ Arguments كاملة (Mapping) 📦 // نأخذ البيانات من rideInfo (القادمة من getRideWaiting) ونمررها للخريطة Map fullRideArgs = { // معرفات الرحلة 'rideId': rideInfo['id'].toString(), 'passengerId': rideInfo['passengerId'].toString(), 'driverId': driverId, // المواقع (يجب أن تكون Strings بصيغة "lat,lng") 'passengerLocation': rideInfo['start_location'].toString(), 'passengerDestination': rideInfo['end_location'].toString(), // الأسماء والعناوين 'startNameLocation': rideInfo['startName'].toString(), 'endNameLocation': rideInfo['endName'].toString(), // تفاصيل الراكب 'name': rideInfo['first_name'] ?? 'Passenger', 'phone': rideInfo['phone'].toString(), 'email': rideInfo['email'] ?? '', 'tokenPassenger': rideInfo['passengerToken'].toString(), 'passengerWalletBurc': rideInfo['bruc'].toString(), // رصيد الراكب // التفاصيل المالية والرحلة 'totalCost': rideInfo['price'].toString(), // السعر الكلي 'paymentAmount': rideInfo['price'].toString(), // المبلغ المطلوب 'Distance': rideInfo['distance'].toString(), 'Duration': rideInfo['duration'].toString(), 'durationOfRideValue': rideInfo['duration'].toString(), // تكرار للتأكد 'carType': rideInfo['carType'].toString(), // الدفع والمحفظة 'paymentMethod': (rideInfo['payment_method'] == 'visa' || rideInfo['payment_method'] == 'wallet') ? 'visa' : 'cash', 'WalletChecked': rideInfo['passenger_wallet'].toString() != '0' ? 'true' : 'false', 'kazan': Get.find() .kazan .toString(), // نسبة الشركة (من الكنترولر) // بيانات إضافية (لتجنب الـ Null Safety errors) 'direction': 'http://googleusercontent.com/maps.google.com/maps?saddr=${rideInfo['start_location']}&daddr=${rideInfo['end_location']}', 'timeOfOrder': DateTime.now().toString(), 'isHaveSteps': 'false', // لو كان عندك خطوات في الـ waitingRides ضيفها 'step0': '', 'step1': '', 'step2': '', 'step3': '', 'step4': '', }; // حفظ البيانات في الصندوق احتياطياً (Crash Recovery) box.write(BoxName.rideArguments, fullRideArgs); // الانتقال لصفحة الخريطة ومسح الصفحات السابقة لضمان عدم الرجوع للسوق Get.offAll(() => PassengerLocationMapPage(), arguments: fullRideArgs); } else { // ❌ فشل: الرحلة أخذها سائق آخر // نقوم بتحديث القائمة فوراً Get.find() .getRideAvailable(forceRefresh: true); MyDialog().getDialog( "Trip taken".tr, "This ride was just accepted by another driver.".tr, () => Get.back(), // زر الموافقة ); } } catch (e) { Get.back(); // إخفاء اللودينج في حال الخطأ print("Accept Ride Error: $e"); MyDialog().getDialog( "Error".tr, "An unexpected error occurred. Please try again.".tr, () => Get.back(), ); } } }