// food_offer_page.dart — شاشة استقبال عرض التوصيل (مستقلة عن شاشة عرض الرحلة) // // شاشة كاملة مقصودة لا بطاقة صغيرة: مهلة العرض 20 ثانية، والسائق يحتاج يقرأ // المطعم والأجرة وهل التحصيل نقدي قبل أن يقرر. تفتح تلقائياً عند وصول العرض // من سوكيت الطعام، وتُغلق نفسها إذا انتهت مهلة كل العروض. import 'package:flutter/material.dart'; import 'package:geolocator/geolocator.dart'; import 'package:get/get.dart'; import '../../constant/colors.dart'; import '../../controller/food_delivery/food_delivery_controller.dart'; import '../../controller/food_delivery/food_delivery_models.dart'; import '../../controller/functions/location_controller.dart'; class FoodOfferPage extends StatelessWidget { const FoodOfferPage({super.key}); @override Widget build(BuildContext context) { return GetBuilder( builder: (c) { if (c.pendingOffers.isEmpty) return const _NoOfferView(); final offer = c.pendingOffers.first; final isResponding = c.isRespondingToOffer(offer.orderId); return Scaffold( backgroundColor: const Color(0xFF12171F), body: SafeArea( child: Column( children: [ _Header(offer: offer, queued: c.pendingOffers.length), Expanded( child: SingleChildScrollView( padding: const EdgeInsets.symmetric(horizontal: 20), child: Column( children: [ const SizedBox(height: 8), _FeeBadge(offer: offer), const SizedBox(height: 20), _RouteCard(offer: offer), const SizedBox(height: 14), _MetaRow(offer: offer), if (offer.isCash) ...[ const SizedBox(height: 14), _CashWarning(amount: offer.cashToCollect), ], const SizedBox(height: 24), ], ), ), ), _Actions( isResponding: isResponding, onAccept: () => c.respondToOffer(offer.orderId, true), onReject: () => c.respondToOffer(offer.orderId, false), ), ], ), ), ); }, ); } } class _NoOfferView extends StatelessWidget { const _NoOfferView(); @override Widget build(BuildContext context) { return Scaffold( backgroundColor: const Color(0xFF12171F), body: Center( child: Column( mainAxisSize: MainAxisSize.min, children: [ const Icon(Icons.timer_off_rounded, size: 64, color: Colors.white38), const SizedBox(height: 12), const Text('انتهت مهلة العرض', style: TextStyle(color: Colors.white70, fontSize: 16)), const SizedBox(height: 20), TextButton(onPressed: Get.back, child: const Text('رجوع')), ], ), ), ); } } class _Header extends StatelessWidget { final FoodDeliveryOffer offer; final int queued; const _Header({required this.offer, required this.queued}); @override Widget build(BuildContext context) { final remaining = offer.secondsRemaining; final progress = offer.ttlSeconds == 0 ? 0.0 : remaining / offer.ttlSeconds; return Padding( padding: const EdgeInsets.fromLTRB(20, 16, 20, 8), child: Row( children: [ SizedBox( width: 62, height: 62, child: Stack( alignment: Alignment.center, children: [ SizedBox( width: 62, height: 62, child: CircularProgressIndicator( value: progress, strokeWidth: 5, backgroundColor: Colors.white12, valueColor: AlwaysStoppedAnimation( remaining <= 5 ? const Color(0xFFE53935) : const Color(0xFFFF6B35), ), ), ), Text('$remaining', style: const TextStyle( color: Colors.white, fontWeight: FontWeight.bold, fontSize: 20)), ], ), ), const SizedBox(width: 14), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ const Text('🍔 عرض توصيل طلب طعام', style: TextStyle( color: Colors.white, fontSize: 18, fontWeight: FontWeight.bold)), Text( queued > 1 ? 'لديك $queued عروض بالانتظار' : 'وافق قبل انتهاء المهلة', style: const TextStyle(color: Colors.white54, fontSize: 13), ), ], ), ), ], ), ); } } class _FeeBadge extends StatelessWidget { final FoodDeliveryOffer offer; const _FeeBadge({required this.offer}); @override Widget build(BuildContext context) { return Container( width: double.infinity, padding: const EdgeInsets.symmetric(vertical: 18), decoration: BoxDecoration( gradient: const LinearGradient( colors: [Color(0xFFFF6B35), Color(0xFFF7931E)], ), borderRadius: BorderRadius.circular(18), ), child: Column( children: [ const Text('أجرة التوصيل', style: TextStyle(color: Colors.white70, fontSize: 13)), const SizedBox(height: 4), Text(foodFormatPrice(offer.deliveryFee), style: const TextStyle( color: Colors.white, fontSize: 30, fontWeight: FontWeight.bold)), ], ), ); } } class _RouteCard extends StatelessWidget { final FoodDeliveryOffer offer; const _RouteCard({required this.offer}); // مسافات تقريبية بخط مستقيم — كافية لقرار القبول خلال ثوانٍ، ولا نستدعي // خدمة مسارات هنا حتى لا نضيف زمناً على مهلة عرض قصيرة أصلاً. double? _distanceKm(double? aLat, double? aLng, double bLat, double bLng) { if (aLat == null || aLng == null || bLat == 0 || bLng == 0) return null; return Geolocator.distanceBetween(aLat, aLng, bLat, bLng) / 1000; } @override Widget build(BuildContext context) { double? driverLat; double? driverLng; if (Get.isRegistered()) { final loc = Get.find().myLocation; if (loc.latitude != 0) { driverLat = loc.latitude; driverLng = loc.longitude; } } final toMerchant = _distanceKm(driverLat, driverLng, offer.merchantLat ?? 0, offer.merchantLng ?? 0); final merchantToCustomer = _distanceKm(offer.merchantLat, offer.merchantLng, offer.deliveryLat, offer.deliveryLng); return Container( padding: const EdgeInsets.all(16), decoration: BoxDecoration( color: Colors.white.withOpacity(0.06), borderRadius: BorderRadius.circular(16), border: Border.all(color: Colors.white12), ), child: Column( children: [ _RouteRow( icon: Icons.storefront_rounded, iconColor: const Color(0xFFFF6B35), title: offer.merchantNameAr, subtitle: offer.merchantAddress ?? 'الاستلام من المطعم', trailing: toMerchant == null ? null : '${toMerchant.toStringAsFixed(1)} كم', ), Padding( padding: const EdgeInsets.symmetric(vertical: 8), child: Row( children: [ const SizedBox(width: 18), Container(width: 2, height: 22, color: Colors.white24), ], ), ), _RouteRow( icon: Icons.location_on_rounded, iconColor: const Color(0xFF4CAF50), title: 'التسليم للزبون', // العنوان النصّي لا يصل في العرض — يظهر بعد القبول فقط (حماية بيانات الزبون) subtitle: 'يظهر العنوان الكامل بعد قبول الطلب', trailing: merchantToCustomer == null ? null : '${merchantToCustomer.toStringAsFixed(1)} كم', ), ], ), ); } } class _RouteRow extends StatelessWidget { final IconData icon; final Color iconColor; final String title; final String subtitle; final String? trailing; const _RouteRow({ required this.icon, required this.iconColor, required this.title, required this.subtitle, this.trailing, }); @override Widget build(BuildContext context) { return Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ Icon(icon, color: iconColor, size: 24), const SizedBox(width: 12), Expanded( child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text(title, style: const TextStyle( color: Colors.white, fontSize: 15, fontWeight: FontWeight.bold), maxLines: 1, overflow: TextOverflow.ellipsis), Text(subtitle, style: const TextStyle(color: Colors.white54, fontSize: 12), maxLines: 2, overflow: TextOverflow.ellipsis), ], ), ), if (trailing != null) Text(trailing!, style: const TextStyle( color: Colors.white70, fontSize: 13, fontWeight: FontWeight.bold)), ], ); } } class _MetaRow extends StatelessWidget { final FoodDeliveryOffer offer; const _MetaRow({required this.offer}); @override Widget build(BuildContext context) { return Row( children: [ Expanded( child: _MetaTile( icon: Icons.shopping_bag_rounded, label: 'عدد الأصناف', value: offer.itemsCount > 0 ? '${offer.itemsCount}' : '—', ), ), const SizedBox(width: 12), Expanded( child: _MetaTile( icon: offer.isCash ? Icons.payments_rounded : Icons.account_balance_wallet_rounded, label: 'طريقة الدفع', value: offer.isCash ? 'نقداً' : 'محفظة', ), ), ], ); } } class _MetaTile extends StatelessWidget { final IconData icon; final String label; final String value; const _MetaTile({required this.icon, required this.label, required this.value}); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.symmetric(vertical: 14), decoration: BoxDecoration( color: Colors.white.withOpacity(0.06), borderRadius: BorderRadius.circular(14), ), child: Column( children: [ Icon(icon, color: Colors.white70, size: 20), const SizedBox(height: 6), Text(label, style: const TextStyle(color: Colors.white54, fontSize: 11)), Text(value, style: const TextStyle( color: Colors.white, fontSize: 15, fontWeight: FontWeight.bold)), ], ), ); } } class _CashWarning extends StatelessWidget { final int amount; const _CashWarning({required this.amount}); @override Widget build(BuildContext context) { return Container( padding: const EdgeInsets.all(14), decoration: BoxDecoration( color: const Color(0xFFF29900).withOpacity(0.15), borderRadius: BorderRadius.circular(14), border: Border.all(color: const Color(0xFFF29900).withOpacity(0.5)), ), child: Row( children: [ const Icon(Icons.info_rounded, color: Color(0xFFF29900)), const SizedBox(width: 10), Expanded( child: Text( 'طلب نقدي — ستحصّل ${foodFormatPrice(amount)} من الزبون عند التسليم', style: const TextStyle(color: Color(0xFFFFD08A), fontSize: 13), ), ), ], ), ); } } class _Actions extends StatelessWidget { final bool isResponding; final VoidCallback onAccept; final VoidCallback onReject; const _Actions({ required this.isResponding, required this.onAccept, required this.onReject, }); @override Widget build(BuildContext context) { if (isResponding) { return const Padding( padding: EdgeInsets.all(24), child: Center(child: CircularProgressIndicator(color: Color(0xFFFF6B35))), ); } return Padding( padding: const EdgeInsets.fromLTRB(20, 8, 20, 20), child: Row( children: [ Expanded( child: SizedBox( height: 56, child: OutlinedButton( style: OutlinedButton.styleFrom( side: const BorderSide(color: Colors.white24), shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)), ), onPressed: onReject, child: const Text('رفض', style: TextStyle(color: Colors.white70, fontSize: 16)), ), ), ), const SizedBox(width: 12), Expanded( flex: 2, child: SizedBox( height: 56, child: ElevatedButton( style: ElevatedButton.styleFrom( backgroundColor: AppColor.greenColor, shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)), ), onPressed: onAccept, child: const Text('قبول الطلب', style: TextStyle( color: Colors.white, fontSize: 17, fontWeight: FontWeight.bold)), ), ), ), ], ), ); } }