Update codebase
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import 'package:tripz_rider/controller/home/map/ride_lifecycle_controller.dart';
|
||||
import 'package:siro_rider/controller/home/map/ride_lifecycle_controller.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:tripz_rider/views/home/map_page_passenger.dart';
|
||||
import 'package:siro_rider/views/home/map_page_passenger.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_rating_bar/flutter_rating_bar.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import 'package:tripz_rider/controller/functions/crud.dart';
|
||||
import 'package:siro_rider/controller/functions/crud.dart';
|
||||
import 'package:flutter/gestures.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_widget_from_html/flutter_widget_from_html.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:tripz_rider/constant/box_name.dart';
|
||||
import 'package:tripz_rider/constant/colors.dart';
|
||||
import 'package:tripz_rider/constant/style.dart';
|
||||
import 'package:tripz_rider/main.dart';
|
||||
import 'package:tripz_rider/views/widgets/my_scafold.dart';
|
||||
import 'package:siro_rider/constant/box_name.dart';
|
||||
import 'package:siro_rider/constant/colors.dart';
|
||||
import 'package:siro_rider/constant/style.dart';
|
||||
import 'package:siro_rider/main.dart';
|
||||
import 'package:siro_rider/views/widgets/my_scafold.dart';
|
||||
import 'package:path/path.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
@@ -236,7 +236,7 @@ class LoginPage extends StatelessWidget {
|
||||
recognizer: TapGestureRecognizer()
|
||||
..onTap = () {
|
||||
launchUrl(Uri.parse(
|
||||
'https://tripz.xyz/tripz/privacy_policy.php'));
|
||||
'https://intaleq.xyz/siro/privacy_policy.php'));
|
||||
},
|
||||
),
|
||||
TextSpan(text: " and acknowledge our Privacy Policy.".tr),
|
||||
@@ -506,7 +506,7 @@ class LoginPage extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 14),
|
||||
Text(
|
||||
'يجمع تطبيق تريبز بيانات الموقع لتفعيل ميزة التتبع الجغرافي (Geofence) ولتمكين السائق من الوصول إليك بدقة وتتبع رحلتك حتى عندما يكون التطبيق في الخلفية أو مغلقاً.'
|
||||
'يجمع تطبيق سيرو بيانات الموقع لتفعيل ميزة التتبع الجغرافي (Geofence) ولتمكين السائق من الوصول إليك بدقة وتتبع رحلتك حتى عندما يكون التطبيق في الخلفية أو مغلقاً.'
|
||||
.tr,
|
||||
textAlign: TextAlign.center,
|
||||
style: AppStyle.title.copyWith(
|
||||
|
||||
@@ -2,7 +2,7 @@ import 'dart:ui';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import 'package:tripz_rider/controller/auth/login_controller.dart';
|
||||
import 'package:siro_rider/controller/auth/login_controller.dart';
|
||||
import '../../constant/colors.dart';
|
||||
import '../../controller/auth/otp_controller.dart';
|
||||
import '../../controller/local/phone_intel/intl_phone_field.dart';
|
||||
@@ -425,7 +425,7 @@ class _PhoneNumberScreenState extends State<PhoneNumberScreen> {
|
||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||
|
||||
return AuthScreen(
|
||||
title: 'welcome to tripz'.tr,
|
||||
title: 'welcome to siro'.tr,
|
||||
subtitle: 'login or register subtitle'.tr,
|
||||
form: Form(
|
||||
key: _formKey,
|
||||
|
||||
@@ -0,0 +1,248 @@
|
||||
// food_cart_page.dart — مراجعة السلة، عرض السعر الموقّع، وإنشاء الطلب
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
|
||||
import '../../constant/box_name.dart';
|
||||
import '../../constant/colors.dart';
|
||||
import '../../constant/style.dart';
|
||||
import '../../main.dart';
|
||||
import '../../views/widgets/error_snakbar.dart';
|
||||
import '../../controller/food/food_controller.dart';
|
||||
import '../../controller/food/food_models.dart';
|
||||
import '../widgets/my_scafold.dart';
|
||||
import 'food_order_tracking_page.dart';
|
||||
|
||||
class FoodCartPage extends StatefulWidget {
|
||||
const FoodCartPage({super.key});
|
||||
|
||||
@override
|
||||
State<FoodCartPage> createState() => _FoodCartPageState();
|
||||
}
|
||||
|
||||
class _FoodCartPageState extends State<FoodCartPage> {
|
||||
final TextEditingController _addressController = TextEditingController();
|
||||
final TextEditingController _noteController = TextEditingController();
|
||||
String _paymentMethod = 'wallet';
|
||||
Position? _position;
|
||||
bool _isFetchingLocation = false;
|
||||
|
||||
bool get _isAr => box.read(BoxName.lang) == 'ar';
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_fetchLocation();
|
||||
}
|
||||
|
||||
Future<void> _fetchLocation() async {
|
||||
setState(() => _isFetchingLocation = true);
|
||||
try {
|
||||
final permission = await Geolocator.checkPermission();
|
||||
if (permission == LocationPermission.denied) {
|
||||
await Geolocator.requestPermission();
|
||||
}
|
||||
_position = await Geolocator.getCurrentPosition(desiredAccuracy: LocationAccuracy.high);
|
||||
} catch (_) {
|
||||
_position = null;
|
||||
}
|
||||
if (mounted) setState(() => _isFetchingLocation = false);
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetBuilder<FoodController>(
|
||||
builder: (c) => MyScafolld(
|
||||
title: _isAr ? 'السلة' : 'Cart',
|
||||
isleading: true,
|
||||
body: [
|
||||
c.cartLines.isEmpty ? _emptyCart() : _cartContent(c),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _emptyCart() {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(Icons.shopping_cart_outlined, size: 72, color: AppColor.grayColor),
|
||||
const SizedBox(height: 16),
|
||||
Text(_isAr ? 'سلتك فارغة' : 'Your cart is empty', style: AppStyle.title),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _cartContent(FoodController c) {
|
||||
return Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(16),
|
||||
children: [
|
||||
...c.cartLines.map((line) => _cartLineTile(c, line)),
|
||||
const SizedBox(height: 16),
|
||||
_summaryRow(_isAr ? 'المجموع الفرعي' : 'Subtotal', foodFormatPrice(c.cartTotal)),
|
||||
if (c.currentQuote != null) ...[
|
||||
_summaryRow(_isAr ? 'رسوم التوصيل' : 'Delivery fee', foodFormatPrice(c.currentQuote!.deliveryFee)),
|
||||
const Divider(),
|
||||
_summaryRow(_isAr ? 'الإجمالي' : 'Total', foodFormatPrice(c.currentQuote!.grandTotal), bold: true),
|
||||
],
|
||||
const SizedBox(height: 20),
|
||||
TextField(
|
||||
controller: _addressController,
|
||||
decoration: InputDecoration(
|
||||
labelText: _isAr ? 'عنوان التوصيل' : 'Delivery address',
|
||||
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
|
||||
suffixIcon: _isFetchingLocation
|
||||
? const Padding(
|
||||
padding: EdgeInsets.all(12),
|
||||
child: SizedBox(width: 16, height: 16, child: CircularProgressIndicator(strokeWidth: 2)),
|
||||
)
|
||||
: Icon(_position != null ? Icons.my_location_rounded : Icons.location_off_rounded,
|
||||
color: _position != null ? AppColor.greenColor : AppColor.redColor),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
TextField(
|
||||
controller: _noteController,
|
||||
decoration: InputDecoration(
|
||||
labelText: _isAr ? 'ملاحظات (اختياري)' : 'Notes (optional)',
|
||||
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
_paymentMethodPicker(),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(16, 8, 16, MediaQuery.of(context).padding.bottom + 16),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
height: 52,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColor.primaryColor,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)),
|
||||
),
|
||||
onPressed: c.isQuoting || c.isPlacingOrder ? null : () => _onCheckoutPressed(c),
|
||||
child: c.isPlacingOrder
|
||||
? const CircularProgressIndicator(color: Colors.white)
|
||||
: Text(
|
||||
c.currentQuote == null
|
||||
? (_isAr ? 'احسب السعر' : 'Get Quote')
|
||||
: '${_isAr ? "اطلب الآن" : "Place Order"} · ${foodFormatPrice(c.currentQuote!.grandTotal)}',
|
||||
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _onCheckoutPressed(FoodController c) async {
|
||||
if (c.currentQuote == null) {
|
||||
await c.refreshQuote();
|
||||
return;
|
||||
}
|
||||
|
||||
if (_addressController.text.trim().isEmpty) {
|
||||
mySnackbarWarning(_isAr ? 'أدخل عنوان التوصيل' : 'Enter a delivery address');
|
||||
return;
|
||||
}
|
||||
if (_position == null) {
|
||||
mySnackbarWarning(_isAr ? 'تعذّر تحديد موقعك — فعّل خدمة الموقع' : 'Could not get your location — enable location services');
|
||||
return;
|
||||
}
|
||||
|
||||
final orderId = await c.placeOrder(
|
||||
deliveryAddress: _addressController.text.trim(),
|
||||
deliveryLat: _position!.latitude,
|
||||
deliveryLng: _position!.longitude,
|
||||
paymentMethod: _paymentMethod,
|
||||
customerNote: _noteController.text.trim(),
|
||||
);
|
||||
|
||||
if (orderId != null) {
|
||||
Get.offAll(() => FoodOrderTrackingPage(orderId: orderId));
|
||||
}
|
||||
}
|
||||
|
||||
Widget _cartLineTile(FoodController c, FoodCartLine line) {
|
||||
return Card(
|
||||
margin: const EdgeInsets.only(bottom: 10),
|
||||
color: AppColor.cardColor,
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
side: BorderSide(color: AppColor.borderColor),
|
||||
),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(line.item.nameAr, style: AppStyle.title.copyWith(fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 4),
|
||||
Text(foodFormatPrice(line.lineTotal), style: AppStyle.subtitle.copyWith(color: AppColor.accentColor)),
|
||||
],
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.remove_circle_outline_rounded),
|
||||
onPressed: () => c.updateQuantity(line.lineKey, line.quantity - 1),
|
||||
),
|
||||
Text(line.quantity.toString(), style: AppStyle.title),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.add_circle_outline_rounded),
|
||||
onPressed: () => c.updateQuantity(line.lineKey, line.quantity + 1),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _summaryRow(String label, String value, {bool bold = false}) {
|
||||
final style = bold
|
||||
? AppStyle.title.copyWith(fontWeight: FontWeight.bold, fontSize: 18)
|
||||
: AppStyle.subtitle;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [Text(label, style: style), Text(value, style: style)],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _paymentMethodPicker() {
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(_isAr ? 'طريقة الدفع' : 'Payment method', style: AppStyle.title.copyWith(fontWeight: FontWeight.bold)),
|
||||
RadioListTile<String>(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
value: 'wallet',
|
||||
groupValue: _paymentMethod,
|
||||
title: Text(_isAr ? 'المحفظة' : 'Wallet'),
|
||||
onChanged: (v) => setState(() => _paymentMethod = v!),
|
||||
),
|
||||
RadioListTile<String>(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
value: 'cash',
|
||||
groupValue: _paymentMethod,
|
||||
title: Text(_isAr ? 'نقداً عند الاستلام' : 'Cash on delivery'),
|
||||
onChanged: (v) => setState(() => _paymentMethod = v!),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,270 @@
|
||||
// food_home_page.dart — الصفحة الرئيسية لتبويب "طعام": تصفح المطاعم
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../constant/box_name.dart';
|
||||
import '../../constant/colors.dart';
|
||||
import '../../constant/style.dart';
|
||||
import '../../main.dart';
|
||||
import '../../controller/food/food_controller.dart';
|
||||
import '../../controller/food/food_models.dart';
|
||||
import '../widgets/my_scafold.dart';
|
||||
import 'food_merchant_page.dart';
|
||||
import 'food_cart_page.dart';
|
||||
import 'food_order_history_page.dart';
|
||||
|
||||
class FoodHomePage extends StatelessWidget {
|
||||
const FoodHomePage({super.key});
|
||||
|
||||
bool get _isAr => box.read(BoxName.lang) == 'ar';
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final c = Get.put(FoodController());
|
||||
c.fetchMerchants();
|
||||
|
||||
return GetBuilder<FoodController>(
|
||||
builder: (c) => MyScafolld(
|
||||
title: _isAr ? 'طعام' : 'Food',
|
||||
isleading: true,
|
||||
action: Row(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.history_rounded, color: AppColor.primaryColor),
|
||||
onPressed: () => Get.to(() => const FoodOrderHistoryPage()),
|
||||
),
|
||||
Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.shopping_cart_rounded, color: AppColor.primaryColor),
|
||||
onPressed: () => Get.to(() => const FoodCartPage()),
|
||||
),
|
||||
if (c.cartItemsCount > 0)
|
||||
Positioned(
|
||||
right: 4,
|
||||
top: 4,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(4),
|
||||
decoration: const BoxDecoration(color: AppColor.redColor, shape: BoxShape.circle),
|
||||
constraints: const BoxConstraints(minWidth: 18, minHeight: 18),
|
||||
child: Text(
|
||||
c.cartItemsCount.toString(),
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(color: Colors.white, fontSize: 11, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
body: [
|
||||
Column(
|
||||
children: [
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
child: _citySearchBar(c),
|
||||
),
|
||||
Expanded(
|
||||
child: RefreshIndicator(
|
||||
onRefresh: () => c.fetchMerchants(),
|
||||
child: c.isLoadingMerchants
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: c.merchants.isEmpty
|
||||
? _emptyState()
|
||||
: ListView.builder(
|
||||
padding: const EdgeInsets.fromLTRB(16, 4, 16, 24),
|
||||
itemCount: c.merchants.length,
|
||||
itemBuilder: (_, i) => _merchantCard(c.merchants[i]),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _citySearchBar(FoodController c) {
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: InkWell(
|
||||
onTap: () => _showCityPicker(c),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 14, vertical: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.cardColor,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: AppColor.borderColor),
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.location_on_rounded, color: AppColor.accentColor, size: 20),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text(c.city, style: AppStyle.title, overflow: TextOverflow.ellipsis),
|
||||
),
|
||||
Icon(Icons.keyboard_arrow_down_rounded, color: AppColor.grayColor),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
InkWell(
|
||||
onTap: () => _showSearchDialog(c),
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.cardColor,
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
border: Border.all(color: AppColor.borderColor),
|
||||
),
|
||||
child: Icon(Icons.search_rounded, color: AppColor.primaryColor),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
void _showCityPicker(FoodController c) {
|
||||
final controller = TextEditingController(text: c.city);
|
||||
Get.dialog(
|
||||
AlertDialog(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
||||
title: Text(_isAr ? 'اختر المدينة' : 'Select City'),
|
||||
content: TextField(
|
||||
controller: controller,
|
||||
decoration: InputDecoration(hintText: _isAr ? 'اسم المدينة' : 'City name'),
|
||||
),
|
||||
actions: [
|
||||
TextButton(onPressed: () => Get.back(), child: Text(_isAr ? 'إلغاء' : 'Cancel')),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
c.setCity(controller.text);
|
||||
},
|
||||
child: Text(_isAr ? 'تم' : 'Done'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showSearchDialog(FoodController c) {
|
||||
final controller = TextEditingController();
|
||||
Get.dialog(
|
||||
AlertDialog(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
||||
title: Text(_isAr ? 'بحث عن مطعم أو صنف' : 'Search restaurant or dish'),
|
||||
content: TextField(
|
||||
controller: controller,
|
||||
autofocus: true,
|
||||
decoration: InputDecoration(hintText: _isAr ? 'اكتب هنا...' : 'Type here...'),
|
||||
),
|
||||
actions: [
|
||||
TextButton(onPressed: () => Get.back(), child: Text(_isAr ? 'إلغاء' : 'Cancel')),
|
||||
ElevatedButton(
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
c.searchMerchants(controller.text);
|
||||
},
|
||||
child: Text(_isAr ? 'بحث' : 'Search'),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _emptyState() {
|
||||
return ListView(
|
||||
padding: const EdgeInsets.all(24),
|
||||
children: [
|
||||
const SizedBox(height: 60),
|
||||
Icon(Icons.restaurant_menu_rounded, size: 72, color: AppColor.grayColor),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
_isAr ? 'لا توجد مطاعم متاحة في هذه المدينة حالياً' : 'No restaurants available in this city yet',
|
||||
textAlign: TextAlign.center,
|
||||
style: AppStyle.title.copyWith(fontWeight: FontWeight.bold),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _merchantCard(FoodMerchant m) {
|
||||
return Card(
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
color: AppColor.cardColor,
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
side: BorderSide(color: AppColor.borderColor),
|
||||
),
|
||||
clipBehavior: Clip.antiAlias,
|
||||
child: InkWell(
|
||||
onTap: () => Get.to(() => FoodMerchantPage(merchantId: m.id)),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
if (m.coverUrl != null && m.coverUrl!.isNotEmpty)
|
||||
Image.network(m.coverUrl!, height: 120, width: double.infinity, fit: BoxFit.cover,
|
||||
errorBuilder: (_, __, ___) => Container(height: 120, color: AppColor.borderColor)),
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
children: [
|
||||
CircleAvatar(
|
||||
radius: 24,
|
||||
backgroundColor: AppColor.accentColor.withOpacity(0.15),
|
||||
backgroundImage: (m.logoUrl != null && m.logoUrl!.isNotEmpty)
|
||||
? NetworkImage(m.logoUrl!)
|
||||
: null,
|
||||
child: (m.logoUrl == null || m.logoUrl!.isEmpty)
|
||||
? Icon(Icons.restaurant_rounded, color: AppColor.accentColor)
|
||||
: null,
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(m.nameAr, style: AppStyle.title.copyWith(fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 4),
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.star_rounded, color: Color(0xFFF59E0B), size: 16),
|
||||
const SizedBox(width: 2),
|
||||
Text(m.ratingAvg.toStringAsFixed(1), style: AppStyle.subtitle),
|
||||
const SizedBox(width: 10),
|
||||
Icon(Icons.access_time_rounded, size: 14, color: AppColor.grayColor),
|
||||
const SizedBox(width: 2),
|
||||
Text('${m.avgPrepMinutes} ${_isAr ? "د" : "min"}', style: AppStyle.subtitle),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (!m.isOpen)
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.redColor.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Text(_isAr ? 'مغلق' : 'Closed',
|
||||
style: TextStyle(color: AppColor.redColor, fontSize: 11, fontWeight: FontWeight.bold)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,312 @@
|
||||
// food_merchant_page.dart — قائمة مطعم واحد + إضافة أصناف للسلة
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../constant/box_name.dart';
|
||||
import '../../constant/colors.dart';
|
||||
import '../../constant/style.dart';
|
||||
import '../../main.dart';
|
||||
import '../../views/widgets/error_snakbar.dart';
|
||||
import '../../controller/food/food_controller.dart';
|
||||
import '../../controller/food/food_models.dart';
|
||||
import '../widgets/my_scafold.dart';
|
||||
import 'food_cart_page.dart';
|
||||
|
||||
class FoodMerchantPage extends StatelessWidget {
|
||||
final int merchantId;
|
||||
const FoodMerchantPage({super.key, required this.merchantId});
|
||||
|
||||
bool get _isAr => box.read(BoxName.lang) == 'ar';
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final c = Get.find<FoodController>();
|
||||
c.openMerchant(merchantId);
|
||||
|
||||
return GetBuilder<FoodController>(
|
||||
builder: (c) {
|
||||
final merchant = c.selectedMerchant;
|
||||
return MyScafolld(
|
||||
title: merchant?.nameAr ?? (_isAr ? 'المطعم' : 'Restaurant'),
|
||||
isleading: true,
|
||||
action: Stack(
|
||||
clipBehavior: Clip.none,
|
||||
children: [
|
||||
IconButton(
|
||||
icon: Icon(Icons.shopping_cart_rounded, color: AppColor.primaryColor),
|
||||
onPressed: () => Get.to(() => const FoodCartPage()),
|
||||
),
|
||||
if (c.cartItemsCount > 0)
|
||||
Positioned(
|
||||
right: 4,
|
||||
top: 4,
|
||||
child: Container(
|
||||
padding: const EdgeInsets.all(4),
|
||||
decoration: const BoxDecoration(color: AppColor.redColor, shape: BoxShape.circle),
|
||||
constraints: const BoxConstraints(minWidth: 18, minHeight: 18),
|
||||
child: Text(c.cartItemsCount.toString(),
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(color: Colors.white, fontSize: 11, fontWeight: FontWeight.bold)),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
body: [
|
||||
c.isLoadingMenu
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: merchant == null
|
||||
? Center(child: Text(_isAr ? 'تعذر تحميل المطعم' : 'Failed to load restaurant'))
|
||||
: _content(c, merchant),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _content(FoodController c, FoodMerchant merchant) {
|
||||
return ListView(
|
||||
padding: const EdgeInsets.fromLTRB(16, 8, 16, 24),
|
||||
children: [
|
||||
if (!merchant.isOpen)
|
||||
Container(
|
||||
width: double.infinity,
|
||||
padding: const EdgeInsets.all(12),
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.redColor.withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Text(
|
||||
_isAr ? 'هذا المطعم مغلق حالياً' : 'This restaurant is currently closed',
|
||||
style: TextStyle(color: AppColor.redColor, fontWeight: FontWeight.bold),
|
||||
),
|
||||
),
|
||||
if (merchant.descriptionAr != null && merchant.descriptionAr!.isNotEmpty)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12),
|
||||
child: Text(merchant.descriptionAr!, style: AppStyle.subtitle),
|
||||
),
|
||||
Text(
|
||||
_isAr
|
||||
? 'الحد الأدنى للطلب: ${(merchant.minOrderAmount / 1000).toStringAsFixed(3)} د.أ'
|
||||
: 'Minimum order: ${(merchant.minOrderAmount / 1000).toStringAsFixed(3)} JOD',
|
||||
style: AppStyle.subtitle.copyWith(color: AppColor.grayColor),
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
...c.categories.map((cat) => _categorySection(c, merchant, cat)),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _categorySection(FoodController c, FoodMerchant merchant, FoodMenuCategory cat) {
|
||||
if (cat.items.isEmpty) return const SizedBox.shrink();
|
||||
return Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(cat.nameAr, style: AppStyle.headTitle2.copyWith(fontSize: 18)),
|
||||
const SizedBox(height: 8),
|
||||
...cat.items.map((item) => _itemTile(c, merchant, item)),
|
||||
const SizedBox(height: 16),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _itemTile(FoodController c, FoodMerchant merchant, FoodMenuItem item) {
|
||||
return Card(
|
||||
margin: const EdgeInsets.only(bottom: 10),
|
||||
color: AppColor.cardColor,
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
side: BorderSide(color: AppColor.borderColor),
|
||||
),
|
||||
child: InkWell(
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
onTap: !item.isAvailable || !merchant.isOpen
|
||||
? null
|
||||
: () => _openItemSheet(c, merchant, item),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(12),
|
||||
child: Row(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(item.nameAr, style: AppStyle.title.copyWith(fontWeight: FontWeight.bold)),
|
||||
if (item.descriptionAr != null && item.descriptionAr!.isNotEmpty) ...[
|
||||
const SizedBox(height: 4),
|
||||
Text(item.descriptionAr!,
|
||||
style: AppStyle.subtitle, maxLines: 2, overflow: TextOverflow.ellipsis),
|
||||
],
|
||||
const SizedBox(height: 6),
|
||||
Text(foodFormatPrice(item.price),
|
||||
style: AppStyle.title.copyWith(color: AppColor.accentColor, fontWeight: FontWeight.bold)),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
if (item.imageUrl != null && item.imageUrl!.isNotEmpty)
|
||||
ClipRRect(
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
child: Image.network(item.imageUrl!, width: 72, height: 72, fit: BoxFit.cover,
|
||||
errorBuilder: (_, __, ___) => Container(width: 72, height: 72, color: AppColor.borderColor)),
|
||||
),
|
||||
if (!item.isAvailable)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(left: 8),
|
||||
child: Text(_isAr ? 'غير متاح' : 'Unavailable',
|
||||
style: TextStyle(color: AppColor.redColor, fontSize: 12, fontWeight: FontWeight.bold)),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _openItemSheet(FoodController c, FoodMerchant merchant, FoodMenuItem item) {
|
||||
final Map<int, List<String>> selected = {};
|
||||
int quantity = 1;
|
||||
|
||||
Get.bottomSheet(
|
||||
isScrollControlled: true,
|
||||
StatefulBuilder(
|
||||
builder: (context, setState) {
|
||||
int unitPrice = item.price;
|
||||
for (final group in item.options) {
|
||||
for (final choiceId in (selected[group.id] ?? [])) {
|
||||
final choice = group.choices.firstWhere((ch) => ch.id == choiceId,
|
||||
orElse: () => FoodOptionChoice(id: '', labelAr: '', price: 0));
|
||||
unitPrice += choice.price;
|
||||
}
|
||||
}
|
||||
|
||||
bool canSubmit = true;
|
||||
for (final group in item.options) {
|
||||
if (group.isRequired && (selected[group.id] ?? []).isEmpty) canSubmit = false;
|
||||
}
|
||||
|
||||
return Container(
|
||||
constraints: BoxConstraints(maxHeight: Get.height * 0.85),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.secondaryColor,
|
||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(24)),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
const SizedBox(height: 12),
|
||||
Container(width: 40, height: 4,
|
||||
decoration: BoxDecoration(color: Colors.grey.withOpacity(0.3), borderRadius: BorderRadius.circular(2))),
|
||||
Expanded(
|
||||
child: ListView(
|
||||
padding: const EdgeInsets.all(20),
|
||||
children: [
|
||||
Text(item.nameAr, style: AppStyle.headTitle2.copyWith(fontSize: 20)),
|
||||
if (item.descriptionAr != null && item.descriptionAr!.isNotEmpty) ...[
|
||||
const SizedBox(height: 8),
|
||||
Text(item.descriptionAr!, style: AppStyle.subtitle),
|
||||
],
|
||||
const SizedBox(height: 16),
|
||||
...item.options.map((group) => _optionGroupWidget(group, selected, setState)),
|
||||
const SizedBox(height: 16),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
IconButton(
|
||||
icon: const Icon(Icons.remove_circle_outline_rounded),
|
||||
onPressed: quantity > 1 ? () => setState(() => quantity--) : null,
|
||||
),
|
||||
Text(quantity.toString(), style: AppStyle.headTitle2.copyWith(fontSize: 20)),
|
||||
IconButton(
|
||||
icon: const Icon(Icons.add_circle_outline_rounded),
|
||||
onPressed: () => setState(() => quantity++),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
Padding(
|
||||
padding: EdgeInsets.fromLTRB(20, 8, 20, MediaQuery.of(context).padding.bottom + 16),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
height: 52,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: canSubmit ? AppColor.primaryColor : AppColor.grayColor,
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)),
|
||||
),
|
||||
onPressed: !canSubmit
|
||||
? null
|
||||
: () {
|
||||
final ok = c.addToCartForMerchant(merchant.id, item,
|
||||
quantity: quantity, options: selected);
|
||||
Get.back();
|
||||
if (ok) {
|
||||
mySnackbarSuccess(_isAr ? 'أُضيف إلى السلة' : 'Added to cart');
|
||||
}
|
||||
},
|
||||
child: Text(
|
||||
'${_isAr ? "إضافة" : "Add"} · ${foodFormatPrice(unitPrice * quantity)}',
|
||||
style: const TextStyle(color: Colors.white, fontWeight: FontWeight.bold, fontSize: 16),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _optionGroupWidget(
|
||||
FoodItemOptionGroup group, Map<int, List<String>> selected, StateSetter setState) {
|
||||
final isSingle = group.maxSelect <= 1;
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
Text(group.groupNameAr, style: AppStyle.title.copyWith(fontWeight: FontWeight.bold)),
|
||||
if (group.isRequired) ...[
|
||||
const SizedBox(width: 6),
|
||||
Text(_isAr ? '(مطلوب)' : '(required)',
|
||||
style: TextStyle(color: AppColor.redColor, fontSize: 12)),
|
||||
],
|
||||
],
|
||||
),
|
||||
...group.choices.map((choice) {
|
||||
final isChecked = (selected[group.id] ?? []).contains(choice.id);
|
||||
return CheckboxListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
dense: true,
|
||||
value: isChecked,
|
||||
controlAffinity: ListTileControlAffinity.leading,
|
||||
title: Text(choice.labelAr),
|
||||
secondary: choice.price > 0 ? Text('+${foodFormatPrice(choice.price)}') : null,
|
||||
onChanged: (checked) {
|
||||
setState(() {
|
||||
final list = List<String>.from(selected[group.id] ?? []);
|
||||
if (checked == true) {
|
||||
if (isSingle) list.clear();
|
||||
if (!isSingle && list.length >= group.maxSelect) return;
|
||||
list.add(choice.id);
|
||||
} else {
|
||||
list.remove(choice.id);
|
||||
}
|
||||
selected[group.id] = list;
|
||||
});
|
||||
},
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
// food_order_history_page.dart — سجل طلبات الطعام
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../constant/box_name.dart';
|
||||
import '../../constant/colors.dart';
|
||||
import '../../constant/style.dart';
|
||||
import '../../main.dart';
|
||||
import '../../controller/food/food_models.dart';
|
||||
import '../../controller/food/food_service.dart';
|
||||
import '../widgets/my_scafold.dart';
|
||||
import 'food_order_tracking_page.dart';
|
||||
|
||||
class FoodOrderHistoryPage extends StatefulWidget {
|
||||
const FoodOrderHistoryPage({super.key});
|
||||
|
||||
@override
|
||||
State<FoodOrderHistoryPage> createState() => _FoodOrderHistoryPageState();
|
||||
}
|
||||
|
||||
class _FoodOrderHistoryPageState extends State<FoodOrderHistoryPage> {
|
||||
bool get _isAr => box.read(BoxName.lang) == 'ar';
|
||||
bool _isLoading = true;
|
||||
List<FoodOrder> _orders = [];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_load();
|
||||
}
|
||||
|
||||
Future<void> _load() async {
|
||||
setState(() => _isLoading = true);
|
||||
final res = await FoodService.getHistory();
|
||||
setState(() {
|
||||
_orders = res.data ?? [];
|
||||
_isLoading = false;
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return MyScafolld(
|
||||
title: _isAr ? 'سجل الطلبات' : 'Order History',
|
||||
isleading: true,
|
||||
body: [
|
||||
_isLoading
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: _orders.isEmpty
|
||||
? Center(
|
||||
child: Text(_isAr ? 'لا يوجد طلبات سابقة' : 'No past orders', style: AppStyle.title),
|
||||
)
|
||||
: RefreshIndicator(
|
||||
onRefresh: _load,
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.all(16),
|
||||
itemCount: _orders.length,
|
||||
itemBuilder: (_, i) => _orderTile(_orders[i]),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _orderTile(FoodOrder order) {
|
||||
final isCancelled = foodOrderIsCancelled(order.status);
|
||||
return Card(
|
||||
margin: const EdgeInsets.only(bottom: 10),
|
||||
color: AppColor.cardColor,
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
side: BorderSide(color: AppColor.borderColor),
|
||||
),
|
||||
child: ListTile(
|
||||
contentPadding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
|
||||
leading: CircleAvatar(
|
||||
backgroundColor: AppColor.accentColor.withOpacity(0.15),
|
||||
backgroundImage: (order.merchantLogoUrl != null && order.merchantLogoUrl!.isNotEmpty)
|
||||
? NetworkImage(order.merchantLogoUrl!)
|
||||
: null,
|
||||
child: (order.merchantLogoUrl == null || order.merchantLogoUrl!.isEmpty)
|
||||
? Icon(Icons.restaurant_rounded, color: AppColor.accentColor)
|
||||
: null,
|
||||
),
|
||||
title: Text(order.merchantNameAr ?? '#${order.id}', style: AppStyle.title.copyWith(fontWeight: FontWeight.bold)),
|
||||
subtitle: Text(
|
||||
isCancelled
|
||||
? (_isAr ? 'مُلغى' : 'Cancelled')
|
||||
: order.status == 'delivered'
|
||||
? (_isAr ? 'تم التسليم' : 'Delivered')
|
||||
: (_isAr ? 'قيد التنفيذ' : 'In progress'),
|
||||
style: AppStyle.subtitle.copyWith(
|
||||
color: isCancelled ? AppColor.redColor : (order.status == 'delivered' ? AppColor.greenColor : AppColor.accentColor),
|
||||
),
|
||||
),
|
||||
trailing: Text(foodFormatPrice(order.grandTotal), style: AppStyle.title.copyWith(fontWeight: FontWeight.bold)),
|
||||
onTap: () => Get.to(() => FoodOrderTrackingPage(orderId: order.id)),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,330 @@
|
||||
// food_order_tracking_page.dart — تتبّع حالة الطلب (polling — نفس مصدر الحقيقة في backend)
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
import '../../constant/box_name.dart';
|
||||
import '../../constant/colors.dart';
|
||||
import '../../constant/links.dart';
|
||||
import '../../constant/style.dart';
|
||||
import '../../main.dart';
|
||||
import '../../controller/food/food_controller.dart';
|
||||
import '../../controller/voice_call_controller.dart';
|
||||
import '../../controller/food/food_models.dart';
|
||||
import '../widgets/my_scafold.dart';
|
||||
import 'food_home_page.dart';
|
||||
import 'food_tracking_map_page.dart';
|
||||
|
||||
class FoodOrderTrackingPage extends StatefulWidget {
|
||||
final int orderId;
|
||||
const FoodOrderTrackingPage({super.key, required this.orderId});
|
||||
|
||||
@override
|
||||
State<FoodOrderTrackingPage> createState() => _FoodOrderTrackingPageState();
|
||||
}
|
||||
|
||||
class _FoodOrderTrackingPageState extends State<FoodOrderTrackingPage> {
|
||||
bool get _isAr => box.read(BoxName.lang) == 'ar';
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
Get.find<FoodController>().startTrackingOrder(widget.orderId);
|
||||
}
|
||||
|
||||
static const Map<String, String> _statusLabelsAr = {
|
||||
'pending': 'بانتظار قبول المطعم',
|
||||
'merchant_accepted': 'المطعم قبل طلبك',
|
||||
'preparing': 'جاري التحضير',
|
||||
'ready': 'جاهز، بانتظار سائق',
|
||||
'courier_assigned': 'تم تعيين سائق',
|
||||
'picked_up': 'السائق في الطريق إليك',
|
||||
'delivered': 'تم التسليم',
|
||||
'rejected': 'رُفض الطلب',
|
||||
'cancelled_by_customer': 'أُلغي الطلب',
|
||||
'cancelled_by_merchant': 'أُلغي من المطعم',
|
||||
'cancelled_system': 'أُلغي تلقائياً',
|
||||
};
|
||||
|
||||
static const Map<String, String> _statusLabelsEn = {
|
||||
'pending': 'Awaiting merchant',
|
||||
'merchant_accepted': 'Accepted by restaurant',
|
||||
'preparing': 'Preparing',
|
||||
'ready': 'Ready, waiting for courier',
|
||||
'courier_assigned': 'Courier assigned',
|
||||
'picked_up': 'Courier on the way',
|
||||
'delivered': 'Delivered',
|
||||
'rejected': 'Order rejected',
|
||||
'cancelled_by_customer': 'Cancelled',
|
||||
'cancelled_by_merchant': 'Cancelled by restaurant',
|
||||
'cancelled_system': 'Auto-cancelled',
|
||||
};
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetBuilder<FoodController>(
|
||||
builder: (c) {
|
||||
final order = c.activeOrder;
|
||||
return MyScafolld(
|
||||
title: _isAr ? 'طلبك #${widget.orderId}' : 'Order #${widget.orderId}',
|
||||
isleading: true,
|
||||
body: [
|
||||
order == null
|
||||
? const Center(child: CircularProgressIndicator())
|
||||
: _content(c, order),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _content(FoodController c, FoodOrder order) {
|
||||
final isCancelled = foodOrderIsCancelled(order.status);
|
||||
final label = _isAr ? _statusLabelsAr[order.status] : _statusLabelsEn[order.status];
|
||||
|
||||
return ListView(
|
||||
padding: const EdgeInsets.all(20),
|
||||
children: [
|
||||
Container(
|
||||
padding: const EdgeInsets.all(20),
|
||||
decoration: BoxDecoration(
|
||||
color: isCancelled ? AppColor.redColor.withOpacity(0.08) : AppColor.accentColor.withOpacity(0.08),
|
||||
borderRadius: BorderRadius.circular(16),
|
||||
),
|
||||
child: Column(
|
||||
children: [
|
||||
Icon(
|
||||
isCancelled
|
||||
? Icons.cancel_rounded
|
||||
: order.status == 'delivered'
|
||||
? Icons.check_circle_rounded
|
||||
: Icons.delivery_dining_rounded,
|
||||
size: 48,
|
||||
color: isCancelled ? AppColor.redColor : AppColor.accentColor,
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(label ?? order.status,
|
||||
style: AppStyle.headTitle2.copyWith(fontSize: 18), textAlign: TextAlign.center),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 20),
|
||||
if (!isCancelled) _statusTimeline(order.status),
|
||||
const SizedBox(height: 20),
|
||||
Text(_isAr ? 'ملخص الطلب' : 'Order Summary', style: AppStyle.title.copyWith(fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 8),
|
||||
...order.items.map((i) => Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Expanded(child: Text('${i.quantity}× ${i.nameArSnapshot}', style: AppStyle.subtitle)),
|
||||
Text(foodFormatPrice(i.lineTotal), style: AppStyle.subtitle),
|
||||
],
|
||||
),
|
||||
)),
|
||||
const Divider(),
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(_isAr ? 'الإجمالي' : 'Total', style: AppStyle.title.copyWith(fontWeight: FontWeight.bold)),
|
||||
Text(foodFormatPrice(order.grandTotal), style: AppStyle.title.copyWith(fontWeight: FontWeight.bold)),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
// خريطة التتبّع تظهر فقط بعد إسناد سائق — قبلها لا يوجد ما يُتتبَّع
|
||||
if (order.status == 'courier_assigned' || order.status == 'picked_up')
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton.icon(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColor.accentColor,
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)),
|
||||
),
|
||||
onPressed: () => Get.to(() => FoodTrackingMapPage(orderId: order.id)),
|
||||
icon: const Icon(Icons.map_rounded, color: Colors.white),
|
||||
label: Text(
|
||||
_isAr ? 'تتبّع السائق على الخريطة' : 'Track courier on map',
|
||||
style: const TextStyle(color: Colors.white),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
// الاتصال بالسائق متاح فقط أثناء التوصيل، وعبر قناة مقنّعة:
|
||||
// لا رقم هاتف يُعرض لأي طرف — جلسة صوتية مؤقتة تُقفل بانتهاء الطلب.
|
||||
if (order.status == 'courier_assigned' || order.status == 'picked_up')
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: OutlinedButton.icon(
|
||||
style: OutlinedButton.styleFrom(
|
||||
side: BorderSide(color: AppColor.primaryColor),
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)),
|
||||
),
|
||||
onPressed: () => _callCourier(order.id),
|
||||
icon: Icon(Icons.phone_in_talk_rounded, color: AppColor.primaryColor),
|
||||
label: Text(
|
||||
_isAr ? 'الاتصال بالسائق' : 'Call the courier',
|
||||
style: TextStyle(color: AppColor.primaryColor),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
if (order.status == 'pending')
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: OutlinedButton(
|
||||
style: OutlinedButton.styleFrom(
|
||||
side: BorderSide(color: AppColor.redColor),
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)),
|
||||
),
|
||||
onPressed: () => _confirmCancel(c),
|
||||
child: Text(_isAr ? 'إلغاء الطلب' : 'Cancel Order', style: TextStyle(color: AppColor.redColor)),
|
||||
),
|
||||
),
|
||||
if (order.status == 'delivered' && order.rating == null)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: AppColor.primaryColor,
|
||||
padding: const EdgeInsets.symmetric(vertical: 14),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)),
|
||||
),
|
||||
onPressed: () => _showRatingDialog(c, order.id),
|
||||
child: Text(_isAr ? 'قيّم الطلب' : 'Rate Order', style: const TextStyle(color: Colors.white)),
|
||||
),
|
||||
),
|
||||
),
|
||||
if (order.status == 'delivered' || isCancelled)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 8),
|
||||
child: SizedBox(
|
||||
width: double.infinity,
|
||||
child: TextButton(
|
||||
onPressed: () => Get.offAll(() => const FoodHomePage()),
|
||||
child: Text(_isAr ? 'العودة للمطاعم' : 'Back to restaurants'),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// القناة نفسها المستعملة في مكالمات الرحلات (WebRTC عبر خادم الإشارات)،
|
||||
// لكن الجلسة تُنشأ من واجهة الطعام التي تتحقق أن الطلب لي وأنه قيد التوصيل.
|
||||
Future<void> _callCourier(int orderId) async {
|
||||
// مسجَّل عالمياً بـ lazyPut(fenix) في app_bindings — والـ put احتياط
|
||||
VoiceCallController voiceCtrl;
|
||||
try {
|
||||
voiceCtrl = Get.find<VoiceCallController>();
|
||||
} catch (_) {
|
||||
voiceCtrl = Get.put(VoiceCallController());
|
||||
}
|
||||
|
||||
await voiceCtrl.startCall(
|
||||
rideIdVal: 'food_$orderId',
|
||||
driverId: '', // هوية السائق لا تصل التطبيق — الخادم يحلّها من الطلب
|
||||
passengerId: box.read(BoxName.passengerID).toString(),
|
||||
remoteNameVal: _isAr ? 'سائق التوصيل' : 'Delivery courier',
|
||||
sessionEndpoint: '${AppLink.server}/food/order/call_courier.php',
|
||||
sessionPayload: {'order_id': orderId.toString()},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _statusTimeline(String currentStatus) {
|
||||
final currentIndex = foodOrderStatusFlow.indexOf(currentStatus);
|
||||
return Column(
|
||||
children: List.generate(foodOrderStatusFlow.length, (i) {
|
||||
final status = foodOrderStatusFlow[i];
|
||||
final isDone = currentIndex >= i;
|
||||
final label = _isAr ? _statusLabelsAr[status] : _statusLabelsEn[status];
|
||||
return Row(
|
||||
children: [
|
||||
Column(
|
||||
children: [
|
||||
Container(
|
||||
width: 16,
|
||||
height: 16,
|
||||
decoration: BoxDecoration(
|
||||
shape: BoxShape.circle,
|
||||
color: isDone ? AppColor.accentColor : AppColor.borderColor,
|
||||
),
|
||||
),
|
||||
if (i < foodOrderStatusFlow.length - 1)
|
||||
Container(width: 2, height: 28, color: isDone ? AppColor.accentColor : AppColor.borderColor),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: 12),
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(bottom: 12),
|
||||
child: Text(label ?? status,
|
||||
style: AppStyle.subtitle.copyWith(
|
||||
color: isDone ? AppColor.writeColor : AppColor.grayColor,
|
||||
fontWeight: isDone ? FontWeight.bold : FontWeight.normal)),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
void _confirmCancel(FoodController c) {
|
||||
Get.dialog(
|
||||
AlertDialog(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
||||
title: Text(_isAr ? 'إلغاء الطلب؟' : 'Cancel order?'),
|
||||
content: Text(_isAr
|
||||
? 'سيتم استرجاع المبلغ إلى محفظتك فوراً إن دفعت من المحفظة.'
|
||||
: 'Your wallet will be refunded immediately if paid by wallet.'),
|
||||
actions: [
|
||||
TextButton(onPressed: () => Get.back(), child: Text(_isAr ? 'تراجع' : 'Back')),
|
||||
ElevatedButton(
|
||||
style: ElevatedButton.styleFrom(backgroundColor: AppColor.redColor),
|
||||
onPressed: () {
|
||||
Get.back();
|
||||
c.cancelActiveOrder();
|
||||
},
|
||||
child: Text(_isAr ? 'إلغاء الطلب' : 'Cancel Order', style: const TextStyle(color: Colors.white)),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _showRatingDialog(FoodController c, int orderId) {
|
||||
int rating = 5;
|
||||
Get.dialog(
|
||||
StatefulBuilder(
|
||||
builder: (context, setState) => AlertDialog(
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
|
||||
title: Text(_isAr ? 'قيّم تجربتك' : 'Rate your experience'),
|
||||
content: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: List.generate(5, (i) {
|
||||
final star = i + 1;
|
||||
return IconButton(
|
||||
icon: Icon(star <= rating ? Icons.star_rounded : Icons.star_border_rounded,
|
||||
color: const Color(0xFFF59E0B), size: 32),
|
||||
onPressed: () => setState(() => rating = star),
|
||||
);
|
||||
}),
|
||||
),
|
||||
actions: [
|
||||
TextButton(onPressed: () => Get.back(), child: Text(_isAr ? 'لاحقاً' : 'Later')),
|
||||
ElevatedButton(
|
||||
onPressed: () async {
|
||||
Get.back();
|
||||
await c.rateActiveOrder(rating);
|
||||
},
|
||||
child: Text(_isAr ? 'إرسال' : 'Submit'),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
// food_tracking_map_page.dart — خريطة تتبّع سائق التوصيل (جهة الراكب)
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intaleq_maps/intaleq_maps.dart';
|
||||
|
||||
import '../../constant/box_name.dart';
|
||||
import '../../constant/colors.dart';
|
||||
import '../../constant/style.dart';
|
||||
import '../../controller/food/food_tracking_controller.dart';
|
||||
import '../../env/env.dart';
|
||||
import '../../main.dart';
|
||||
import '../widgets/my_scafold.dart';
|
||||
|
||||
class FoodTrackingMapPage extends StatelessWidget {
|
||||
final int orderId;
|
||||
const FoodTrackingMapPage({super.key, required this.orderId});
|
||||
|
||||
bool get _isAr => box.read(BoxName.lang) == 'ar';
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final c = Get.put(FoodTrackingController(orderId), tag: 'food_track_$orderId');
|
||||
|
||||
return GetBuilder<FoodTrackingController>(
|
||||
tag: 'food_track_$orderId',
|
||||
builder: (_) => MyScafolld(
|
||||
title: _isAr ? 'تتبّع السائق' : 'Track courier',
|
||||
isleading: true,
|
||||
body: [
|
||||
IntaleqMap(
|
||||
apiKey: Env.mapSaasKey,
|
||||
onMapCreated: c.onMapCreated,
|
||||
onStyleLoaded: c.onStyleLoaded,
|
||||
markers: c.markers,
|
||||
initialCameraPosition: CameraPosition(
|
||||
target: c.courierPosition ??
|
||||
c.merchantPosition ??
|
||||
c.destinationPosition ??
|
||||
const LatLng(31.9539, 35.9106), // عمّان — حتى يصل أول موقع
|
||||
zoom: 14,
|
||||
),
|
||||
),
|
||||
if (c.isLoading)
|
||||
const Positioned.fill(child: Center(child: CircularProgressIndicator())),
|
||||
Positioned(left: 12, right: 12, bottom: 20, child: _statusCard(c)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _statusCard(FoodTrackingController c) {
|
||||
final String message;
|
||||
final Color color;
|
||||
|
||||
if (!c.isTrackingActive) {
|
||||
message = _isAr
|
||||
? 'انتهى تتبّع هذا الطلب'
|
||||
: 'Tracking for this order has ended';
|
||||
color = AppColor.grayColor;
|
||||
} else if (c.courierPosition == null) {
|
||||
message = _isAr
|
||||
? 'بانتظار إشارة موقع السائق…'
|
||||
: 'Waiting for the courier location…';
|
||||
color = AppColor.grayColor;
|
||||
} else if (c.isStale) {
|
||||
// صراحةً بدل إيهام الراكب بحركة حيّة والعلامة مجمّدة على شاشته
|
||||
message = _isAr
|
||||
? 'إشارة السائق ضعيفة — آخر موقع معروف'
|
||||
: 'Weak courier signal — last known position';
|
||||
color = const Color(0xFFF29900);
|
||||
} else if (c.orderStatus == 'picked_up') {
|
||||
message = _isAr ? 'السائق في طريقه إليك' : 'Courier is on the way to you';
|
||||
color = AppColor.accentColor;
|
||||
} else {
|
||||
message = _isAr
|
||||
? 'السائق في طريقه إلى ${c.merchantName}'
|
||||
: 'Courier heading to ${c.merchantName}';
|
||||
color = AppColor.accentColor;
|
||||
}
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(14),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.cardColor,
|
||||
borderRadius: BorderRadius.circular(14),
|
||||
boxShadow: [
|
||||
BoxShadow(color: Colors.black.withOpacity(0.12), blurRadius: 10, offset: const Offset(0, 3)),
|
||||
],
|
||||
),
|
||||
child: Row(
|
||||
children: [
|
||||
Icon(Icons.delivery_dining_rounded, color: color),
|
||||
const SizedBox(width: 10),
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(message, style: AppStyle.title.copyWith(fontWeight: FontWeight.bold)),
|
||||
if (c.destinationAddress != null)
|
||||
Text(
|
||||
'${_isAr ? 'التسليم إلى' : 'Delivering to'}: ${c.destinationAddress}',
|
||||
style: AppStyle.subtitle,
|
||||
maxLines: 2,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:tripz_rider/constant/colors.dart';
|
||||
import 'package:tripz_rider/constant/style.dart';
|
||||
import 'package:tripz_rider/views/widgets/my_scafold.dart';
|
||||
import 'package:siro_rider/constant/colors.dart';
|
||||
import 'package:siro_rider/constant/style.dart';
|
||||
import 'package:siro_rider/views/widgets/my_scafold.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_font_icons/flutter_font_icons.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -107,7 +107,7 @@ class ContactUsPage extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
"Tripz Support".tr,
|
||||
"Siro Support".tr,
|
||||
style: AppStyle.headTitle2.copyWith(
|
||||
fontSize: 22,
|
||||
fontWeight: FontWeight.bold,
|
||||
@@ -229,7 +229,7 @@ class ContactUsPage extends StatelessWidget {
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
"About Tripz".tr,
|
||||
"About Siro".tr,
|
||||
style: TextStyle(
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 18,
|
||||
@@ -239,7 +239,7 @@ class ContactUsPage extends StatelessWidget {
|
||||
IconButton(
|
||||
onPressed: () {
|
||||
Get.find<TextToSpeechController>().speakText(
|
||||
'Tripz is the safest and most reliable ride-sharing app designed especially for passengers in Syria. We provide a comfortable, respectful, and affordable riding experience with features that prioritize your safety and convenience.'
|
||||
'Siro is the safest and most reliable ride-sharing app designed especially for passengers in Syria. We provide a comfortable, respectful, and affordable riding experience with features that prioritize your safety and convenience.'
|
||||
.tr,
|
||||
);
|
||||
},
|
||||
@@ -249,7 +249,7 @@ class ContactUsPage extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Text(
|
||||
'Tripz is the safest and most reliable ride-sharing app designed especially for passengers in Syria. We provide a comfortable, respectful, and affordable riding experience with features that prioritize your safety and convenience. Our trusted captains are verified, insured, and supported by regular car maintenance carried out by top engineers. We also offer on-road support services to make sure every trip is smooth and worry-free. With Tripz, you enjoy quality, safety, and peace of mind—every time you ride.'
|
||||
'Siro is the safest and most reliable ride-sharing app designed especially for passengers in Syria. We provide a comfortable, respectful, and affordable riding experience with features that prioritize your safety and convenience. Our trusted captains are verified, insured, and supported by regular car maintenance carried out by top engineers. We also offer on-road support services to make sure every trip is smooth and worry-free. With Siro, you enjoy quality, safety, and peace of mind—every time you ride.'
|
||||
.tr,
|
||||
style: TextStyle(
|
||||
color: isDark ? Colors.white70 : Colors.black54,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:tripz_rider/views/home/HomePage/contact_us.dart';
|
||||
import 'package:siro_rider/views/home/HomePage/contact_us.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -56,7 +56,7 @@ class FrequentlyQuestionsPage extends StatelessWidget {
|
||||
onTap: () => _showAnswerDialog(
|
||||
context,
|
||||
'How do I request a ride?'.tr,
|
||||
'Simply open the Tripz app, enter your destination, and tap "Request Ride". The app will connect you with a nearby driver.'
|
||||
'Simply open the Siro app, enter your destination, and tap "Request Ride". The app will connect you with a nearby driver.'
|
||||
.tr,
|
||||
),
|
||||
),
|
||||
@@ -78,7 +78,7 @@ class FrequentlyQuestionsPage extends StatelessWidget {
|
||||
onTap: () => _showAnswerDialog(
|
||||
context,
|
||||
'What types of vehicles are available?'.tr,
|
||||
'Tripz offers a variety of options including Economy, Comfort, and Luxury to suit your needs and budget.'
|
||||
'Siro offers a variety of options including Economy, Comfort, and Luxury to suit your needs and budget.'
|
||||
.tr,
|
||||
),
|
||||
),
|
||||
@@ -149,7 +149,7 @@ class FrequentlyQuestionsPage extends StatelessWidget {
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.indigo)),
|
||||
content: Text(
|
||||
'To register as a driver or learn about the requirements, please visit our website or contact Tripz support directly.'
|
||||
'To register as a driver or learn about the requirements, please visit our website or contact Siro support directly.'
|
||||
.tr),
|
||||
actions: <CupertinoDialogAction>[
|
||||
CupertinoDialogAction(
|
||||
@@ -208,12 +208,12 @@ class FrequentlyQuestionsPage extends StatelessWidget {
|
||||
CupertinoIcons.shield_fill,
|
||||
color: Colors.teal,
|
||||
),
|
||||
title: Text('What safety measures does Tripz offer?'.tr),
|
||||
title: Text('What safety measures does Siro offer?'.tr),
|
||||
trailing: const CupertinoListTileChevron(),
|
||||
onTap: () => _showAnswerDialog(
|
||||
context,
|
||||
'What safety measures does Tripz offer?'.tr,
|
||||
'Tripz offers various safety features including driver verification, in-app trip tracking, emergency contact options, and the ability to share your trip status with trusted contacts.'
|
||||
'What safety measures does Siro offer?'.tr,
|
||||
'Siro offers various safety features including driver verification, in-app trip tracking, emergency contact options, and the ability to share your trip status with trusted contacts.'
|
||||
.tr,
|
||||
),
|
||||
),
|
||||
|
||||
@@ -744,7 +744,7 @@ class ShareAppPage extends StatelessWidget {
|
||||
padding: const EdgeInsets.symmetric(horizontal: 24, vertical: 8),
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
onPressed: () {
|
||||
final appName = 'Tripz';
|
||||
final appName = 'Siro';
|
||||
final shareText = 'Get a ride with $appName! Use my referral code: ${rc.referralCode} for a discount.\nDownload: ${AppLink.inviteRedirectUrl}?code=${rc.referralCode}&app=rider\n\n💡 Note: If the link is not clickable, save this number or reply to activate links!';
|
||||
Share.share(shareText);
|
||||
},
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'dart:io';
|
||||
import 'package:tripz_rider/controller/home/trip_monitor_controller.dart';
|
||||
import 'package:tripz_rider/env/env.dart';
|
||||
import 'package:tripz_rider/views/widgets/my_scafold.dart';
|
||||
import 'package:siro_rider/controller/home/trip_monitor_controller.dart';
|
||||
import 'package:siro_rider/env/env.dart';
|
||||
import 'package:siro_rider/views/widgets/my_scafold.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:tripz_rider/controller/home/trip_monitor_controller.dart';
|
||||
import 'package:tripz_rider/env/env.dart';
|
||||
import 'package:tripz_rider/views/widgets/my_scafold.dart';
|
||||
import 'package:siro_rider/controller/home/trip_monitor_controller.dart';
|
||||
import 'package:siro_rider/env/env.dart';
|
||||
import 'package:siro_rider/views/widgets/my_scafold.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tripz_rider/constant/colors.dart';
|
||||
import 'package:siro_rider/constant/colors.dart';
|
||||
|
||||
class DrawerMenuPage extends StatelessWidget {
|
||||
const DrawerMenuPage({super.key});
|
||||
|
||||
@@ -113,7 +113,7 @@ class AttributionMap extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(width: 4),
|
||||
Text(
|
||||
"Tripz Maps",
|
||||
"Intaleq Maps",
|
||||
style: TextStyle(
|
||||
fontSize: 9,
|
||||
color: Colors.grey.shade500,
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import 'package:tripz_rider/constant/currency.dart';
|
||||
import 'package:tripz_rider/constant/colors.dart';
|
||||
import 'package:tripz_rider/constant/links.dart';
|
||||
import 'package:tripz_rider/constant/style.dart';
|
||||
import 'package:tripz_rider/controller/home/map/ride_lifecycle_controller.dart';
|
||||
import 'package:tripz_rider/controller/home/map/ride_state.dart';
|
||||
import 'package:tripz_rider/controller/voice_call_controller.dart';
|
||||
import 'package:siro_rider/constant/currency.dart';
|
||||
import 'package:siro_rider/constant/colors.dart';
|
||||
import 'package:siro_rider/constant/links.dart';
|
||||
import 'package:siro_rider/constant/style.dart';
|
||||
import 'package:siro_rider/controller/home/map/ride_lifecycle_controller.dart';
|
||||
import 'package:siro_rider/controller/home/map/ride_state.dart';
|
||||
import 'package:siro_rider/controller/voice_call_controller.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -47,7 +47,7 @@ class ApplyOrderWidget extends StatelessWidget {
|
||||
bottom: isVisible ? 0 : -400,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: TripzSheetSurface(
|
||||
child: SiroSheetSurface(
|
||||
color: Theme.of(context).cardColor,
|
||||
padding: const EdgeInsets.fromLTRB(
|
||||
AppSpacing.lg, AppSpacing.sm, AppSpacing.lg, AppSpacing.lg),
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:tripz_rider/constant/colors.dart';
|
||||
import 'package:tripz_rider/constant/style.dart';
|
||||
import 'package:tripz_rider/controller/home/map/ride_lifecycle_controller.dart';
|
||||
import 'package:siro_rider/constant/colors.dart';
|
||||
import 'package:siro_rider/constant/style.dart';
|
||||
import 'package:siro_rider/controller/home/map/ride_lifecycle_controller.dart';
|
||||
import '../../widgets/elevated_btn.dart';
|
||||
|
||||
// دالة لإظهار الشيت
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import 'package:tripz_rider/constant/currency.dart';
|
||||
import 'package:siro_rider/constant/currency.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:tripz_rider/constant/box_name.dart';
|
||||
import 'package:tripz_rider/constant/colors.dart';
|
||||
import 'package:tripz_rider/constant/style.dart';
|
||||
import 'package:tripz_rider/main.dart';
|
||||
import 'package:tripz_rider/views/home/profile/passenger_profile_page.dart';
|
||||
import 'package:tripz_rider/views/widgets/elevated_btn.dart';
|
||||
import 'package:tripz_rider/views/widgets/my_textField.dart';
|
||||
import 'package:siro_rider/constant/box_name.dart';
|
||||
import 'package:siro_rider/constant/colors.dart';
|
||||
import 'package:siro_rider/constant/style.dart';
|
||||
import 'package:siro_rider/main.dart';
|
||||
import 'package:siro_rider/views/home/profile/passenger_profile_page.dart';
|
||||
import 'package:siro_rider/views/widgets/elevated_btn.dart';
|
||||
import 'package:siro_rider/views/widgets/my_textField.dart';
|
||||
import 'dart:ui';
|
||||
|
||||
import '../../../constant/design.dart';
|
||||
@@ -142,7 +142,7 @@ class CarDetailsTypeToChoose extends StatelessWidget {
|
||||
// ── Drag Handle ──────────────────────────────────────
|
||||
const Padding(
|
||||
padding: EdgeInsets.only(top: AppSpacing.sm),
|
||||
child: TripzSheetHandle(),
|
||||
child: SiroSheetHandle(),
|
||||
),
|
||||
AppSpacing.vGapXs,
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'package:tripz_rider/constant/currency.dart';
|
||||
import 'package:siro_rider/constant/currency.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:tripz_rider/constant/box_name.dart';
|
||||
import 'package:tripz_rider/constant/style.dart';
|
||||
import 'package:tripz_rider/views/home/my_wallet/passenger_wallet.dart';
|
||||
import 'package:siro_rider/constant/box_name.dart';
|
||||
import 'package:siro_rider/constant/style.dart';
|
||||
import 'package:siro_rider/views/home/my_wallet/passenger_wallet.dart';
|
||||
|
||||
import '../../../constant/colors.dart';
|
||||
import '../../../constant/design.dart';
|
||||
@@ -31,7 +31,7 @@ class CashConfirmPageShown extends StatelessWidget {
|
||||
// التحكم في ظهور اللوحة لم يتغير
|
||||
transform: Matrix4.translationValues(
|
||||
0, controller.isCashConfirmPageShown ? 0 : Get.height, 0),
|
||||
child: TripzSheetSurface(
|
||||
child: SiroSheetSurface(
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -120,15 +120,52 @@ class CashConfirmPageShown extends StatelessWidget {
|
||||
}
|
||||
// في كل الحالات الأخرى (كاش، أو محفظة برصيد كافٍ)
|
||||
else {
|
||||
return MyElevatedButton(
|
||||
title: 'Confirm & Find a Ride'.tr,
|
||||
onPressed: () {
|
||||
// --- نفس منطقك القديم بالضبط ---
|
||||
controller.changeCashConfirmPageShown();
|
||||
// controller.isSearchingWindow = true;
|
||||
controller.startSearchingForDriver();
|
||||
// controller.update();
|
||||
},
|
||||
return Row(
|
||||
children: [
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: MyElevatedButton(
|
||||
title: 'Confirm & Find a Ride'.tr,
|
||||
onPressed: () {
|
||||
// --- نفس منطقك القديم بالضبط ---
|
||||
controller.changeCashConfirmPageShown();
|
||||
// controller.isSearchingWindow = true;
|
||||
controller.startSearchingForDriver();
|
||||
// controller.update();
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
// مسار موازٍ لا بديل: الحجز لا يفتح نافذة بحث ولا
|
||||
// ينتظر سائقاً — يسجّل موعداً ويتولّاه الخادم.
|
||||
// ثانوي بصرياً عمداً: الرحلة الفورية تبقى الافتراض.
|
||||
Expanded(
|
||||
flex: 1,
|
||||
child: SizedBox(
|
||||
height: 52,
|
||||
child: TextButton.icon(
|
||||
style: TextButton.styleFrom(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 4),
|
||||
),
|
||||
onPressed: () {
|
||||
controller.changeCashConfirmPageShown();
|
||||
controller.scheduleRideForLater();
|
||||
},
|
||||
icon: Icon(Icons.schedule,
|
||||
size: 18, color: AppColor.primaryColor),
|
||||
label: FittedBox(
|
||||
child: Text(
|
||||
'Schedule for Later'.tr,
|
||||
style: TextStyle(
|
||||
color: AppColor.primaryColor,
|
||||
fontWeight: FontWeight.bold,
|
||||
fontSize: 14),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}),
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import 'package:tripz_rider/print.dart';
|
||||
import 'package:siro_rider/print.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:tripz_rider/env/env.dart';
|
||||
import 'package:siro_rider/env/env.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intaleq_maps/intaleq_maps.dart';
|
||||
import 'package:tripz_rider/controller/home/points_for_rider_controller.dart';
|
||||
import 'package:tripz_rider/services/offline_map_service.dart';
|
||||
import 'package:siro_rider/controller/home/points_for_rider_controller.dart';
|
||||
import 'package:siro_rider/services/offline_map_service.dart';
|
||||
|
||||
import '../../../controller/home/map/location_search_controller.dart';
|
||||
import '../../../controller/home/map/map_engine_controller.dart';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:tripz_rider/views/widgets/elevated_btn.dart';
|
||||
import 'package:tripz_rider/views/widgets/mycircular.dart';
|
||||
import 'package:siro_rider/views/widgets/elevated_btn.dart';
|
||||
import 'package:siro_rider/views/widgets/mycircular.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_font_icons/flutter_font_icons.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:tripz_rider/controller/payment/payment_controller.dart';
|
||||
import 'package:siro_rider/controller/payment/payment_controller.dart';
|
||||
|
||||
import '../../../constant/style.dart';
|
||||
import '../../../controller/home/map/ride_lifecycle_controller.dart';
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import 'dart:ui';
|
||||
|
||||
import 'package:tripz_rider/constant/box_name.dart';
|
||||
import 'package:tripz_rider/main.dart';
|
||||
import 'package:siro_rider/constant/box_name.dart';
|
||||
import 'package:siro_rider/main.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_font_icons/flutter_font_icons.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:tripz_rider/constant/style.dart';
|
||||
import 'package:tripz_rider/views/home/my_wallet/passenger_wallet.dart';
|
||||
import 'package:tripz_rider/views/home/profile/complaint_page.dart';
|
||||
import 'package:tripz_rider/views/home/profile/order_history.dart';
|
||||
import 'package:tripz_rider/views/home/profile/promos_passenger_page.dart';
|
||||
import 'package:siro_rider/constant/style.dart';
|
||||
import 'package:siro_rider/views/home/my_wallet/passenger_wallet.dart';
|
||||
import 'package:siro_rider/views/home/profile/complaint_page.dart';
|
||||
import 'package:siro_rider/views/home/profile/order_history.dart';
|
||||
import 'package:siro_rider/views/scheduled/scheduled_rides_page.dart';
|
||||
import 'package:siro_rider/views/home/profile/promos_passenger_page.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import '../../../constant/colors.dart';
|
||||
@@ -23,6 +24,7 @@ import '../HomePage/share_app_page.dart';
|
||||
import '../setting_page.dart';
|
||||
import '../profile/passenger_profile_page.dart';
|
||||
import '../../transit/transit_home_page.dart';
|
||||
import '../../food/food_home_page.dart';
|
||||
|
||||
// ─── ألوان النظام (Integrated with AppColor) ──────────────────────────────────
|
||||
Color get _kCyan => AppColor.cyanBlue;
|
||||
@@ -157,6 +159,11 @@ class MapMenuWidget extends StatelessWidget {
|
||||
onTap: () =>
|
||||
Get.to(() => const TransitHomePage()),
|
||||
),
|
||||
MenuListItem(
|
||||
title: 'Food'.tr,
|
||||
icon: Icons.restaurant_rounded,
|
||||
onTap: () => Get.to(() => const FoodHomePage()),
|
||||
),
|
||||
MenuListItem(
|
||||
title: 'My Balance'.tr,
|
||||
icon: Icons.account_balance_wallet_outlined,
|
||||
@@ -168,6 +175,11 @@ class MapMenuWidget extends StatelessWidget {
|
||||
icon: Icons.history_rounded,
|
||||
onTap: () => Get.to(() => const OrderHistory()),
|
||||
),
|
||||
MenuListItem(
|
||||
title: 'Scheduled Rides'.tr,
|
||||
icon: Icons.schedule_rounded,
|
||||
onTap: () => Get.to(() => const ScheduledRidesPage()),
|
||||
),
|
||||
MenuListItem(
|
||||
title: 'Promos'.tr,
|
||||
icon: Icons.local_offer_outlined,
|
||||
@@ -319,7 +331,7 @@ class MapMenuWidget extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
Text(
|
||||
"Tripz Passenger".tr,
|
||||
"Siro Passenger".tr,
|
||||
style: TextStyle(
|
||||
color: _kTextMuted,
|
||||
fontSize: 12,
|
||||
@@ -384,9 +396,9 @@ class MapMenuWidget extends StatelessWidget {
|
||||
final String driverAppUrl;
|
||||
if (defaultTargetPlatform == TargetPlatform.android) {
|
||||
driverAppUrl =
|
||||
'https://play.google.com/store/apps/details?id=com.tripz_driver';
|
||||
'https://play.google.com/store/apps/details?id=com.siro_driver';
|
||||
} else if (defaultTargetPlatform == TargetPlatform.iOS) {
|
||||
driverAppUrl = 'https://apps.apple.com/st/app/tripz-driver/id6482995159';
|
||||
driverAppUrl = 'https://apps.apple.com/st/app/siro-driver/id6482995159';
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import 'package:get/get.dart';
|
||||
import 'package:tripz_rider/constant/box_name.dart';
|
||||
import 'package:tripz_rider/controller/functions/secure_storage.dart';
|
||||
import 'package:tripz_rider/controller/home/payment/credit_card_controller.dart';
|
||||
import 'package:tripz_rider/views/widgets/elevated_btn.dart';
|
||||
import 'package:siro_rider/constant/box_name.dart';
|
||||
import 'package:siro_rider/controller/functions/secure_storage.dart';
|
||||
import 'package:siro_rider/controller/home/payment/credit_card_controller.dart';
|
||||
import 'package:siro_rider/views/widgets/elevated_btn.dart';
|
||||
|
||||
import '../../../constant/colors.dart';
|
||||
import '../../../constant/style.dart';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:tripz_rider/constant/style.dart';
|
||||
import 'package:siro_rider/constant/style.dart';
|
||||
|
||||
import '../../../constant/colors.dart';
|
||||
import '../../../controller/home/map/location_search_controller.dart';
|
||||
@@ -56,8 +56,7 @@ class PointsPageForRider extends StatelessWidget {
|
||||
wayPointController.wayPoints.length > 1
|
||||
? ElevatedButton(
|
||||
onPressed: () async {
|
||||
locationSearch
|
||||
.getMapPointsForAllMethods();
|
||||
locationSearch.getMapPointsForAllMethods();
|
||||
},
|
||||
child: const Text('Get Direction'),
|
||||
)
|
||||
@@ -108,8 +107,8 @@ class PointsPageForRider extends StatelessWidget {
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(),
|
||||
color:
|
||||
AppColor.accentColor.withValues(alpha: 0.5)),
|
||||
color: AppColor.accentColor
|
||||
.withValues(alpha: 0.5)),
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceBetween,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:tripz_rider/constant/currency.dart';
|
||||
import 'package:siro_rider/constant/currency.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_font_icons/flutter_font_icons.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -9,7 +9,7 @@ import '../../../constant/box_name.dart';
|
||||
import '../../../constant/colors.dart';
|
||||
import '../../../constant/design.dart';
|
||||
import '../../../constant/links.dart';
|
||||
import 'package:tripz_rider/controller/functions/country_logic.dart';
|
||||
import 'package:siro_rider/controller/functions/country_logic.dart';
|
||||
import '../../../controller/functions/audio_record1.dart';
|
||||
import '../../../controller/functions/launch.dart';
|
||||
import '../../../controller/functions/toast.dart';
|
||||
@@ -44,7 +44,7 @@ class RideBeginPassenger extends StatelessWidget {
|
||||
bottom: isVisible ? 0 : -300,
|
||||
left: 0,
|
||||
right: 0,
|
||||
child: TripzSheetSurface(
|
||||
child: SiroSheetSurface(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: AppSpacing.lg, vertical: AppSpacing.md),
|
||||
child: Column(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import 'package:tripz_rider/constant/currency.dart';
|
||||
import 'package:tripz_rider/controller/functions/launch.dart';
|
||||
import 'package:siro_rider/constant/currency.dart';
|
||||
import 'package:siro_rider/controller/functions/launch.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_font_icons/flutter_font_icons.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:tripz_rider/constant/colors.dart';
|
||||
import 'package:tripz_rider/constant/design.dart';
|
||||
import 'package:tripz_rider/constant/style.dart';
|
||||
import 'package:tripz_rider/controller/home/map/ride_lifecycle_controller.dart';
|
||||
import 'package:tripz_rider/controller/home/map/ride_state.dart';
|
||||
import 'package:siro_rider/constant/colors.dart';
|
||||
import 'package:siro_rider/constant/design.dart';
|
||||
import 'package:siro_rider/constant/style.dart';
|
||||
import 'package:siro_rider/controller/home/map/ride_lifecycle_controller.dart';
|
||||
import 'package:siro_rider/controller/home/map/ride_state.dart';
|
||||
|
||||
// --- نافذة البحث عن كابتن ---
|
||||
class SearchingCaptainWindow extends StatefulWidget {
|
||||
@@ -50,7 +50,7 @@ class _SearchingCaptainWindowState extends State<SearchingCaptainWindow>
|
||||
left: 0,
|
||||
right: 0,
|
||||
// لا يوجد مقبض سحب: هذه الحالة لا تُغلق بالسحب بل بزر الإلغاء فقط
|
||||
child: TripzSheetSurface(
|
||||
child: SiroSheetSurface(
|
||||
showHandle: false,
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import 'package:tripz_rider/constant/colors.dart';
|
||||
import 'package:tripz_rider/constant/style.dart';
|
||||
import 'package:tripz_rider/controller/home/map/ride_lifecycle_controller.dart';
|
||||
import 'package:tripz_rider/views/widgets/elevated_btn.dart';
|
||||
import 'package:siro_rider/constant/colors.dart';
|
||||
import 'package:siro_rider/constant/style.dart';
|
||||
import 'package:siro_rider/controller/home/map/ride_lifecycle_controller.dart';
|
||||
import 'package:siro_rider/views/widgets/elevated_btn.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import 'package:tripz_rider/constant/links.dart';
|
||||
import 'package:tripz_rider/views/home/profile/complaint_page.dart';
|
||||
import 'package:siro_rider/constant/links.dart';
|
||||
import 'package:siro_rider/views/home/profile/complaint_page.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_font_icons/flutter_font_icons.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:tripz_rider/constant/box_name.dart';
|
||||
import 'package:tripz_rider/controller/profile/profile_controller.dart';
|
||||
import 'package:tripz_rider/main.dart';
|
||||
import 'package:siro_rider/constant/box_name.dart';
|
||||
import 'package:siro_rider/controller/profile/profile_controller.dart';
|
||||
import 'package:siro_rider/main.dart';
|
||||
|
||||
import '../../../constant/colors.dart';
|
||||
import '../../../constant/style.dart';
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:tripz_rider/constant/table_names.dart';
|
||||
import 'package:siro_rider/constant/table_names.dart';
|
||||
import 'package:intaleq_maps/intaleq_maps.dart';
|
||||
|
||||
import '../../../constant/colors.dart';
|
||||
@@ -67,7 +67,8 @@ GetBuilder<LocationSearchController> formSearchPlaces(int index) {
|
||||
},
|
||||
child: Text(
|
||||
'Choose from Map'.tr + ' $index'.tr,
|
||||
style: AppStyle.title.copyWith(color: AppColor.blueColor),
|
||||
style:
|
||||
AppStyle.title.copyWith(color: AppColor.blueColor),
|
||||
),
|
||||
)
|
||||
: const SizedBox(),
|
||||
@@ -126,10 +127,10 @@ GetBuilder<LocationSearchController> formSearchPlaces(int index) {
|
||||
IconButton(
|
||||
onPressed: () async {
|
||||
await sql.insertMapLocation({
|
||||
'latitude': res['geometry']['location']
|
||||
['lat'],
|
||||
'longitude': res['geometry']['location']
|
||||
['lng'],
|
||||
'latitude': res['geometry']
|
||||
['location']['lat'],
|
||||
'longitude': res['geometry']
|
||||
['location']['lng'],
|
||||
'name': res['name'].toString(),
|
||||
'rate': res['rating'].toString(),
|
||||
}, TableName.placesFavorite);
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:tripz_rider/constant/box_name.dart';
|
||||
import 'package:tripz_rider/constant/colors.dart';
|
||||
import 'package:tripz_rider/constant/currency.dart';
|
||||
import 'package:tripz_rider/constant/payment_tiers.dart';
|
||||
import 'package:tripz_rider/constant/style.dart';
|
||||
import 'package:tripz_rider/controller/functions/country_logic.dart';
|
||||
import 'package:tripz_rider/controller/payment/payment_controller.dart';
|
||||
import 'package:tripz_rider/main.dart';
|
||||
import 'package:siro_rider/constant/box_name.dart';
|
||||
import 'package:siro_rider/constant/colors.dart';
|
||||
import 'package:siro_rider/constant/currency.dart';
|
||||
import 'package:siro_rider/constant/payment_tiers.dart';
|
||||
import 'package:siro_rider/constant/style.dart';
|
||||
import 'package:siro_rider/controller/functions/country_logic.dart';
|
||||
import 'package:siro_rider/controller/payment/payment_controller.dart';
|
||||
import 'package:siro_rider/main.dart';
|
||||
|
||||
class CliqPaymentSheet extends StatefulWidget {
|
||||
final double amount;
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'package:tripz_rider/constant/currency.dart';
|
||||
import 'package:siro_rider/constant/currency.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:tripz_rider/views/home/my_wallet/payment_history_passenger_page.dart';
|
||||
import 'package:tripz_rider/views/home/my_wallet/tripz_prime_page.dart';
|
||||
import 'package:siro_rider/views/home/my_wallet/payment_history_passenger_page.dart';
|
||||
import 'package:siro_rider/views/home/my_wallet/siro_prime_page.dart';
|
||||
import 'dart:ui'; // لاستخدام تأثيرات متقدمة
|
||||
|
||||
import '../../../constant/box_name.dart';
|
||||
@@ -79,7 +79,7 @@ class PassengerWallet extends StatelessWidget {
|
||||
Get.find<PaymentController>()), // نفس دالتك القديمة
|
||||
),
|
||||
|
||||
// 👑 Tripz Prime Banner
|
||||
// 👑 Siro Prime Banner
|
||||
const SizedBox(height: 8),
|
||||
_buildPrimeBanner(),
|
||||
],
|
||||
@@ -266,14 +266,14 @@ class PassengerWallet extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
// 👑 Tripz Prime Banner Widget
|
||||
// 👑 Siro Prime Banner Widget
|
||||
Widget _buildPrimeBanner() {
|
||||
return GetBuilder<PrimeController>(
|
||||
builder: (ctrl) {
|
||||
final bool isPrime = ctrl.isPrime;
|
||||
return GestureDetector(
|
||||
onTap: () => Get.to(
|
||||
() => const TripzPrimePage(),
|
||||
() => const SiroPrimePage(),
|
||||
transition: Transition.rightToLeftWithFade,
|
||||
),
|
||||
child: Container(
|
||||
@@ -313,7 +313,7 @@ class PassengerWallet extends StatelessWidget {
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(
|
||||
isPrime ? 'Tripz Prime Active'.tr : 'Tripz Prime'.tr,
|
||||
isPrime ? 'Siro Prime Active'.tr : 'Siro Prime'.tr,
|
||||
style: const TextStyle(
|
||||
color: Colors.white,
|
||||
fontWeight: FontWeight.bold,
|
||||
@@ -346,4 +346,4 @@ class PassengerWallet extends StatelessWidget {
|
||||
}
|
||||
}
|
||||
|
||||
// الكلاس القديم CardTripzWallet لم نعد بحاجة إليه لأنه تم دمجه وتطويره
|
||||
// الكلاس القديم CardSiroWallet لم نعد بحاجة إليه لأنه تم دمجه وتطويره
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
import 'package:tripz_rider/constant/currency.dart';
|
||||
import 'package:tripz_rider/constant/payment_tiers.dart';
|
||||
import 'package:tripz_rider/print.dart';
|
||||
import 'package:tripz_rider/constant/style.dart';
|
||||
import 'package:siro_rider/constant/currency.dart';
|
||||
import 'package:siro_rider/constant/payment_tiers.dart';
|
||||
import 'package:siro_rider/print.dart';
|
||||
import 'package:siro_rider/constant/style.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:tripz_rider/constant/box_name.dart';
|
||||
import 'package:tripz_rider/constant/colors.dart';
|
||||
import 'package:tripz_rider/controller/functions/toast.dart';
|
||||
import 'package:tripz_rider/controller/payment/payment_controller.dart';
|
||||
import 'package:siro_rider/constant/box_name.dart';
|
||||
import 'package:siro_rider/constant/colors.dart';
|
||||
import 'package:siro_rider/controller/functions/toast.dart';
|
||||
import 'package:siro_rider/controller/payment/payment_controller.dart';
|
||||
import 'package:local_auth/local_auth.dart';
|
||||
|
||||
import '../../../main.dart';
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:tripz_rider/constant/colors.dart';
|
||||
import 'package:tripz_rider/constant/style.dart';
|
||||
import 'package:tripz_rider/views/widgets/my_scafold.dart';
|
||||
import 'package:tripz_rider/views/widgets/mycircular.dart';
|
||||
import 'package:siro_rider/constant/colors.dart';
|
||||
import 'package:siro_rider/constant/style.dart';
|
||||
import 'package:siro_rider/views/widgets/my_scafold.dart';
|
||||
import 'package:siro_rider/views/widgets/mycircular.dart';
|
||||
|
||||
import '../../../controller/payment/driver_payment_controller.dart';
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:tripz_rider/constant/colors.dart';
|
||||
import 'package:tripz_rider/constant/style.dart';
|
||||
import 'package:tripz_rider/controller/payment/passenger_wallet_history_controller.dart';
|
||||
import 'package:tripz_rider/views/widgets/my_scafold.dart';
|
||||
import 'package:tripz_rider/views/widgets/mycircular.dart';
|
||||
import 'package:siro_rider/constant/colors.dart';
|
||||
import 'package:siro_rider/constant/style.dart';
|
||||
import 'package:siro_rider/controller/payment/passenger_wallet_history_controller.dart';
|
||||
import 'package:siro_rider/views/widgets/my_scafold.dart';
|
||||
import 'package:siro_rider/views/widgets/mycircular.dart';
|
||||
|
||||
class PaymentHistoryPassengerPage extends StatelessWidget {
|
||||
const PaymentHistoryPassengerPage({super.key});
|
||||
|
||||
@@ -124,7 +124,7 @@ class _PaymentScreenCliqState extends State<PaymentScreenCliq> with SingleTicker
|
||||
children: [
|
||||
Text('Invoice Amount'.tr, style: TextStyle(color: Colors.white.withOpacity(0.6), fontSize: 13)),
|
||||
const SizedBox(height: 6),
|
||||
Text('${widget.amount.toStringAsFixed(widget.amount == widget.amount.roundToDouble() ? 0 : 1)} ${'SYP'}',
|
||||
Text('${widget.amount.toStringAsFixed(widget.amount == widget.amount.roundToDouble() ? 0 : 1)} JOD',
|
||||
style: const TextStyle(color: Colors.white, fontSize: 32, fontWeight: FontWeight.w700, letterSpacing: 1)),
|
||||
],
|
||||
),
|
||||
|
||||
+10
-10
@@ -1,20 +1,20 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:tripz_rider/constant/box_name.dart';
|
||||
import 'package:tripz_rider/constant/colors.dart';
|
||||
import 'package:tripz_rider/constant/style.dart';
|
||||
import 'package:tripz_rider/controller/payment/prime_controller.dart';
|
||||
import 'package:tripz_rider/main.dart';
|
||||
import 'package:siro_rider/constant/box_name.dart';
|
||||
import 'package:siro_rider/constant/colors.dart';
|
||||
import 'package:siro_rider/constant/style.dart';
|
||||
import 'package:siro_rider/controller/payment/prime_controller.dart';
|
||||
import 'package:siro_rider/main.dart';
|
||||
import 'dart:math' as math;
|
||||
|
||||
class TripzPrimePage extends StatefulWidget {
|
||||
const TripzPrimePage({super.key});
|
||||
class SiroPrimePage extends StatefulWidget {
|
||||
const SiroPrimePage({super.key});
|
||||
|
||||
@override
|
||||
State<TripzPrimePage> createState() => _TripzPrimePageState();
|
||||
State<SiroPrimePage> createState() => _SiroPrimePageState();
|
||||
}
|
||||
|
||||
class _TripzPrimePageState extends State<TripzPrimePage>
|
||||
class _SiroPrimePageState extends State<SiroPrimePage>
|
||||
with TickerProviderStateMixin {
|
||||
late AnimationController _shimmerController;
|
||||
late AnimationController _pulseController;
|
||||
@@ -225,7 +225,7 @@ class _TripzPrimePageState extends State<TripzPrimePage>
|
||||
],
|
||||
).createShader(bounds),
|
||||
child: const Text(
|
||||
'TRIPZ PRIME',
|
||||
'SIRO PRIME',
|
||||
style: TextStyle(
|
||||
fontSize: 30,
|
||||
fontWeight: FontWeight.w900,
|
||||
@@ -2,7 +2,7 @@ import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:math';
|
||||
import 'dart:ui' show lerpDouble;
|
||||
import 'package:tripz_rider/views/widgets/error_snakbar.dart';
|
||||
import 'package:siro_rider/views/widgets/error_snakbar.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
@@ -1329,7 +1329,7 @@ class NavigationController extends GetxController
|
||||
if (mapController == null) return;
|
||||
|
||||
try {
|
||||
// ✅ Use searchPlaces from tripz_maps SDK
|
||||
// ✅ Use searchPlaces from intaleq_maps SDK
|
||||
final results = await mapController!.searchPlaces(q);
|
||||
|
||||
if (myLocation != null) {
|
||||
|
||||
@@ -4,7 +4,7 @@ import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intaleq_maps/intaleq_maps.dart';
|
||||
import 'package:tripz_rider/env/env.dart';
|
||||
import 'package:siro_rider/env/env.dart';
|
||||
|
||||
import '../../../constant/box_name.dart';
|
||||
import '../../../constant/colors.dart';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:tripz_rider/constant/colors.dart';
|
||||
import 'package:tripz_rider/constant/style.dart';
|
||||
import 'package:tripz_rider/controller/payment/payment_controller.dart';
|
||||
import 'package:siro_rider/constant/colors.dart';
|
||||
import 'package:siro_rider/constant/style.dart';
|
||||
import 'package:siro_rider/controller/payment/payment_controller.dart';
|
||||
|
||||
import '../../../constant/box_name.dart';
|
||||
import '../../../main.dart';
|
||||
|
||||
@@ -2,12 +2,12 @@ import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:tripz_rider/constant/style.dart';
|
||||
import 'package:tripz_rider/controller/home/profile/complaint_controller.dart';
|
||||
import 'package:tripz_rider/views/widgets/my_scafold.dart'; // سنستخدم السكافولد الخاص بك
|
||||
import 'package:tripz_rider/views/widgets/mycircular.dart';
|
||||
import 'package:tripz_rider/views/widgets/mydialoug.dart';
|
||||
import 'package:tripz_rider/views/widgets/elevated_btn.dart'; // سنستخدم الزر الخاص بك
|
||||
import 'package:siro_rider/constant/style.dart';
|
||||
import 'package:siro_rider/controller/home/profile/complaint_controller.dart';
|
||||
import 'package:siro_rider/views/widgets/my_scafold.dart'; // سنستخدم السكافولد الخاص بك
|
||||
import 'package:siro_rider/views/widgets/mycircular.dart';
|
||||
import 'package:siro_rider/views/widgets/mydialoug.dart';
|
||||
import 'package:siro_rider/views/widgets/elevated_btn.dart'; // سنستخدم الزر الخاص بك
|
||||
|
||||
import '../../../constant/colors.dart';
|
||||
import '../../../controller/functions/audio_record1.dart';
|
||||
@@ -145,7 +145,7 @@ class ComplaintPage extends StatelessWidget {
|
||||
ListTile(
|
||||
leading: const Icon(Icons.support_agent_outlined,
|
||||
color: AppColor.primaryColor),
|
||||
title: Text("Tripz's Response".tr,
|
||||
title: Text("Siro's Response".tr,
|
||||
style: AppStyle.title),
|
||||
subtitle: Text(
|
||||
// --- تعديل هنا لعرض الرد من الخادم ---
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -5,13 +5,13 @@ import 'package:get/get.dart';
|
||||
import 'package:image_cropper/image_cropper.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:tripz_rider/constant/api_key.dart';
|
||||
import 'package:tripz_rider/constant/box_name.dart';
|
||||
import 'package:tripz_rider/constant/colors.dart';
|
||||
import 'package:tripz_rider/controller/profile/profile_controller.dart';
|
||||
import 'package:tripz_rider/main.dart';
|
||||
import 'package:tripz_rider/views/widgets/mycircular.dart';
|
||||
import 'package:tripz_rider/views/widgets/error_snakbar.dart';
|
||||
import 'package:siro_rider/constant/api_key.dart';
|
||||
import 'package:siro_rider/constant/box_name.dart';
|
||||
import 'package:siro_rider/constant/colors.dart';
|
||||
import 'package:siro_rider/controller/profile/profile_controller.dart';
|
||||
import 'package:siro_rider/main.dart';
|
||||
import 'package:siro_rider/views/widgets/mycircular.dart';
|
||||
import 'package:siro_rider/views/widgets/error_snakbar.dart';
|
||||
|
||||
import '../../../constant/style.dart';
|
||||
import '../../../controller/functions/country_logic.dart';
|
||||
@@ -320,7 +320,7 @@ class _PassengerProfilePageState extends State<PassengerProfilePage> {
|
||||
final passengerId = box.read(BoxName.passengerID)?.toString() ?? '';
|
||||
|
||||
String email = box.read(BoxName.email) ?? '';
|
||||
if (email.contains('tripzapp.com')) email = '';
|
||||
if (email.contains('intaleqapp.com')) email = '';
|
||||
|
||||
final initials = fullName.isNotEmpty
|
||||
? fullName
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import 'package:cached_network_image/cached_network_image.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:tripz_rider/constant/box_name.dart';
|
||||
import 'package:tripz_rider/constant/style.dart';
|
||||
import 'package:tripz_rider/controller/profile/captain_profile_controller.dart';
|
||||
import 'package:tripz_rider/main.dart';
|
||||
import 'package:tripz_rider/views/widgets/elevated_btn.dart';
|
||||
import 'package:tripz_rider/views/widgets/my_scafold.dart';
|
||||
import 'package:siro_rider/constant/box_name.dart';
|
||||
import 'package:siro_rider/constant/style.dart';
|
||||
import 'package:siro_rider/controller/profile/captain_profile_controller.dart';
|
||||
import 'package:siro_rider/main.dart';
|
||||
import 'package:siro_rider/views/widgets/elevated_btn.dart';
|
||||
import 'package:siro_rider/views/widgets/my_scafold.dart';
|
||||
|
||||
import '../../../constant/api_key.dart';
|
||||
import '../../widgets/my_textField.dart';
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter/services.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:tripz_rider/controller/home/profile/promos_controller.dart';
|
||||
import 'package:tripz_rider/views/widgets/my_scafold.dart';
|
||||
import 'package:tripz_rider/views/widgets/error_snakbar.dart';
|
||||
import 'package:siro_rider/controller/home/profile/promos_controller.dart';
|
||||
import 'package:siro_rider/views/widgets/my_scafold.dart';
|
||||
import 'package:siro_rider/views/widgets/error_snakbar.dart';
|
||||
import 'dart:ui'; // لاستخدامه في الفاصل
|
||||
|
||||
import '../../../constant/colors.dart';
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'package:tripz_rider/constant/box_name.dart';
|
||||
import 'package:tripz_rider/main.dart';
|
||||
import 'package:siro_rider/constant/box_name.dart';
|
||||
import 'package:siro_rider/main.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:tripz_rider/constant/style.dart';
|
||||
import 'package:tripz_rider/views/widgets/my_scafold.dart';
|
||||
import 'package:siro_rider/constant/style.dart';
|
||||
import 'package:siro_rider/views/widgets/my_scafold.dart';
|
||||
|
||||
class TaarifPage extends StatelessWidget {
|
||||
const TaarifPage({super.key});
|
||||
|
||||
@@ -132,12 +132,10 @@ class RpActions {
|
||||
isScrollControlled: true,
|
||||
builder: (context) {
|
||||
return Container(
|
||||
padding:
|
||||
const EdgeInsets.only(top: 12, left: 16, right: 16, bottom: 24),
|
||||
padding: const EdgeInsets.only(top: 12, left: 16, right: 16, bottom: 24),
|
||||
decoration: BoxDecoration(
|
||||
color: RP.sheetElevated,
|
||||
borderRadius:
|
||||
const BorderRadius.vertical(top: Radius.circular(RP.rSheet)),
|
||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(RP.rSheet)),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
@@ -174,10 +172,8 @@ class RpActions {
|
||||
final modifiedPlace = Map<String, dynamic>.from(place);
|
||||
modifiedPlace['latitude'] = gate['latitude'];
|
||||
modifiedPlace['longitude'] = gate['longitude'];
|
||||
modifiedPlace['name_ar'] =
|
||||
'${place['name_ar'] ?? place['name'] ?? ''} - $gateName';
|
||||
modifiedPlace['name'] =
|
||||
'${place['name'] ?? place['name_ar'] ?? ''} - $gateName';
|
||||
modifiedPlace['name_ar'] = '${place['name_ar'] ?? place['name'] ?? ''} - $gateName';
|
||||
modifiedPlace['name'] = '${place['name'] ?? place['name_ar'] ?? ''} - $gateName';
|
||||
|
||||
if (isOrigin) {
|
||||
selectStart(modifiedPlace);
|
||||
|
||||
@@ -37,7 +37,8 @@ class RpMapPickOverlay extends StatelessWidget {
|
||||
String _title(_PickMode m, LocationSearchController s) {
|
||||
switch (m) {
|
||||
case _PickMode.stop:
|
||||
return 'Move map to set stop'.tr + ' ${s.pickingWaypointIndex + 1}'.tr;
|
||||
return 'Move map to set stop'.tr +
|
||||
' ${s.pickingWaypointIndex + 1}'.tr;
|
||||
case _PickMode.pickupOther:
|
||||
return 'Now set the pickup point for the other person'.tr;
|
||||
case _PickMode.pickup:
|
||||
@@ -338,8 +339,8 @@ class RpMapPickOverlay extends StatelessWidget {
|
||||
s.newStartPointLocation.longitude)));
|
||||
} else {
|
||||
await mapEngine.mapController?.animateCamera(CameraUpdate.newLatLng(
|
||||
LatLng(
|
||||
s.passengerLocation.latitude, s.passengerLocation.longitude)));
|
||||
LatLng(s.passengerLocation.latitude,
|
||||
s.passengerLocation.longitude)));
|
||||
}
|
||||
} catch (e) {
|
||||
Log.print('RP confirm animate error: $e');
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:tripz_rider/controller/home/map/location_search_controller.dart';
|
||||
import 'package:tripz_rider/controller/home/map/map_engine_controller.dart';
|
||||
import 'package:tripz_rider/controller/home/map/ride_lifecycle_controller.dart';
|
||||
import 'package:siro_rider/controller/home/map/location_search_controller.dart';
|
||||
import 'package:siro_rider/controller/home/map/map_engine_controller.dart';
|
||||
import 'package:siro_rider/controller/home/map/ride_lifecycle_controller.dart';
|
||||
|
||||
import 'rp_actions.dart';
|
||||
import 'rp_location_field.dart';
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import 'package:tripz_rider/controller/home/home_page_controller.dart';
|
||||
import 'package:tripz_rider/controller/local/local_controller.dart';
|
||||
import 'package:tripz_rider/constant/colors.dart';
|
||||
import 'package:tripz_rider/constant/style.dart';
|
||||
import 'package:siro_rider/controller/home/home_page_controller.dart';
|
||||
import 'package:siro_rider/controller/local/local_controller.dart';
|
||||
import 'package:siro_rider/constant/colors.dart';
|
||||
import 'package:siro_rider/constant/style.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:tripz_rider/views/lang/languages.dart';
|
||||
import 'package:siro_rider/views/lang/languages.dart';
|
||||
|
||||
import 'HomePage/about_page.dart';
|
||||
import 'HomePage/frequentlyQuestionsPage.dart';
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:tripz_rider/constant/style.dart';
|
||||
import 'package:tripz_rider/controller/notification/notification_captain_controller.dart';
|
||||
import 'package:tripz_rider/views/widgets/elevated_btn.dart';
|
||||
import 'package:tripz_rider/views/widgets/my_scafold.dart';
|
||||
import 'package:tripz_rider/views/widgets/mycircular.dart';
|
||||
import 'package:siro_rider/constant/style.dart';
|
||||
import 'package:siro_rider/controller/notification/notification_captain_controller.dart';
|
||||
import 'package:siro_rider/views/widgets/elevated_btn.dart';
|
||||
import 'package:siro_rider/views/widgets/my_scafold.dart';
|
||||
import 'package:siro_rider/views/widgets/mycircular.dart';
|
||||
|
||||
class NotificationCaptain extends StatelessWidget {
|
||||
const NotificationCaptain({super.key});
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:tripz_rider/constant/colors.dart';
|
||||
import 'package:tripz_rider/constant/style.dart';
|
||||
import 'package:siro_rider/constant/colors.dart';
|
||||
import 'package:siro_rider/constant/style.dart';
|
||||
|
||||
import '../../controller/notification/passenger_notification_controller.dart';
|
||||
import '../widgets/my_scafold.dart';
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import '../../controller/scheduled/scheduled_rides_controller.dart';
|
||||
|
||||
/// شاشة حجوزات الراكب المسبقة.
|
||||
///
|
||||
/// تعرض القادم فقط: الحجز الفائت لا يفيد، والراكب يفتح الشاشة ليرى ما
|
||||
/// ينتظره لا ليتصفّح أرشيفه.
|
||||
class ScheduledRidesPage extends StatelessWidget {
|
||||
const ScheduledRidesPage({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final c = Get.put(ScheduledRidesController());
|
||||
|
||||
return Directionality(
|
||||
textDirection: TextDirection.rtl,
|
||||
child: Scaffold(
|
||||
appBar: AppBar(title: Text('رحلاتي المحجوزة'.tr)),
|
||||
body: Obx(() {
|
||||
if (c.isLoading.value && c.bookings.isEmpty) {
|
||||
return const Center(child: CircularProgressIndicator());
|
||||
}
|
||||
|
||||
if (c.bookings.isEmpty) {
|
||||
return _Empty(maxDays: ScheduledRidesController.maxDaysAhead);
|
||||
}
|
||||
|
||||
return RefreshIndicator(
|
||||
onRefresh: c.fetch,
|
||||
child: ListView.builder(
|
||||
padding: const EdgeInsets.all(16),
|
||||
itemCount: c.bookings.length,
|
||||
itemBuilder: (_, i) => _BookingCard(b: c.bookings[i], c: c),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _Empty extends StatelessWidget {
|
||||
const _Empty({required this.maxDays});
|
||||
final int maxDays;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final cs = Theme.of(context).colorScheme;
|
||||
return ListView(
|
||||
padding: const EdgeInsets.all(32),
|
||||
children: [
|
||||
const SizedBox(height: 80),
|
||||
Icon(Icons.event_available_outlined,
|
||||
size: 64, color: cs.primary.withOpacity(0.4)),
|
||||
const SizedBox(height: 20),
|
||||
Text('لا حجوزات قادمة'.tr,
|
||||
textAlign: TextAlign.center,
|
||||
style: const TextStyle(fontSize: 16, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 10),
|
||||
Text(
|
||||
'احجز رحلتك للمطار أو موعدك الطبي أو دوامك الصباحي'
|
||||
' حتى $maxDays يومين مقدماً، ونرسل لك سائقاً في وقته.',
|
||||
textAlign: TextAlign.center,
|
||||
style: TextStyle(fontSize: 13, height: 1.6, color: cs.onSurfaceVariant),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _BookingCard extends StatelessWidget {
|
||||
const _BookingCard({required this.b, required this.c});
|
||||
final Map<String, dynamic> b;
|
||||
final ScheduledRidesController c;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final cs = Theme.of(context).colorScheme;
|
||||
final when = DateTime.tryParse('${b['scheduled_at']}');
|
||||
|
||||
return Card(
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(14)),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
children: [
|
||||
const Icon(Icons.schedule, size: 18, color: Color(0xFF6A5ACD)),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
when == null ? '${b['scheduled_at']}' : _when(when),
|
||||
style: const TextStyle(
|
||||
fontWeight: FontWeight.bold, fontSize: 15),
|
||||
),
|
||||
const Spacer(),
|
||||
if (when != null) _Countdown(when: when),
|
||||
],
|
||||
),
|
||||
const Divider(height: 20),
|
||||
_row(context, '🟢', b['start_name'] ?? 'موقع الانطلاق'),
|
||||
const SizedBox(height: 6),
|
||||
_row(context, '🔴', b['end_name'] ?? 'الوجهة'),
|
||||
const SizedBox(height: 12),
|
||||
Row(
|
||||
children: [
|
||||
Text('${b['car_type'] ?? ''}',
|
||||
style: TextStyle(fontSize: 12, color: cs.onSurfaceVariant)),
|
||||
const Spacer(),
|
||||
// نصرّح بأن السعر تقديري: التسعير النهائي لحظة إنشاء
|
||||
// الرحلة، فرقم اليوم قد لا يطابق رقم الغد.
|
||||
Text(
|
||||
'تقديري: ${b['estimated_price'] ?? '—'}',
|
||||
style: TextStyle(fontSize: 12, color: cs.onSurfaceVariant),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
Align(
|
||||
alignment: Alignment.centerLeft,
|
||||
child: TextButton.icon(
|
||||
onPressed: () => _confirmCancel(context),
|
||||
icon: Icon(Icons.close, size: 16, color: cs.error),
|
||||
label: Text('إلغاء الحجز'.tr,
|
||||
style: TextStyle(color: cs.error, fontSize: 13)),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _confirmCancel(BuildContext context) {
|
||||
Get.defaultDialog(
|
||||
title: 'إلغاء الحجز'.tr,
|
||||
// بلا رسم: لا سائق قُبِل ولا أحد تحرّك. نقولها صراحةً ليطمئن الراكب.
|
||||
middleText: 'سيُلغى الحجز بلا أي رسوم — لم يُسند سائق بعد.'.tr,
|
||||
textConfirm: 'تأكيد'.tr,
|
||||
textCancel: 'تراجع'.tr,
|
||||
onConfirm: () {
|
||||
Get.back();
|
||||
final id = int.tryParse('${b['id']}');
|
||||
if (id != null) c.cancel(id);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
Widget _row(BuildContext context, String dot, dynamic text) => Row(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(dot, style: const TextStyle(fontSize: 12)),
|
||||
const SizedBox(width: 8),
|
||||
Expanded(
|
||||
child: Text('$text',
|
||||
style: const TextStyle(fontSize: 13, height: 1.4)),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
String _when(DateTime d) {
|
||||
final now = DateTime.now();
|
||||
final isToday =
|
||||
d.year == now.year && d.month == now.month && d.day == now.day;
|
||||
final tomorrow = now.add(const Duration(days: 1));
|
||||
final isTomorrow = d.year == tomorrow.year &&
|
||||
d.month == tomorrow.month &&
|
||||
d.day == tomorrow.day;
|
||||
|
||||
final hh = d.hour.toString().padLeft(2, '0');
|
||||
final mm = d.minute.toString().padLeft(2, '0');
|
||||
|
||||
if (isToday) return 'اليوم $hh:$mm';
|
||||
if (isTomorrow) return 'غداً $hh:$mm';
|
||||
return '${d.day}/${d.month} — $hh:$mm';
|
||||
}
|
||||
}
|
||||
|
||||
/// كم تبقّى للموعد. يجعل الحجز محسوساً بدل تاريخ جامد.
|
||||
class _Countdown extends StatelessWidget {
|
||||
const _Countdown({required this.when});
|
||||
final DateTime when;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final diff = when.difference(DateTime.now());
|
||||
if (diff.isNegative) return const SizedBox.shrink();
|
||||
|
||||
final label = diff.inHours >= 1
|
||||
? 'بعد ${diff.inHours} ساعة'
|
||||
: 'بعد ${diff.inMinutes} دقيقة';
|
||||
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
|
||||
decoration: BoxDecoration(
|
||||
color: const Color(0xFF6A5ACD).withOpacity(0.1),
|
||||
borderRadius: BorderRadius.circular(12),
|
||||
),
|
||||
child: Text(label,
|
||||
style: const TextStyle(fontSize: 11, color: Color(0xFF6A5ACD))),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,8 @@ import '../widgets/elevated_btn.dart';
|
||||
// ── ألوان الصفحة ──────────────────────────────────────────────────────────────
|
||||
Color get _bg =>
|
||||
Get.isDarkMode ? const Color(0xFF0A0F1E) : const Color(0xFFF0F4FF);
|
||||
Color get _card => Get.isDarkMode ? const Color(0xFF151C2E) : Colors.white;
|
||||
Color get _card =>
|
||||
Get.isDarkMode ? const Color(0xFF151C2E) : Colors.white;
|
||||
Color get _text =>
|
||||
Get.isDarkMode ? const Color(0xFFEEF2FF) : const Color(0xFF1E293B);
|
||||
Color get _sub =>
|
||||
@@ -25,8 +26,7 @@ class TransitMapPage extends StatefulWidget {
|
||||
final int routeId;
|
||||
final String routeName;
|
||||
|
||||
const TransitMapPage(
|
||||
{super.key, required this.routeId, required this.routeName});
|
||||
const TransitMapPage({super.key, required this.routeId, required this.routeName});
|
||||
|
||||
@override
|
||||
State<TransitMapPage> createState() => _TransitMapPageState();
|
||||
@@ -108,9 +108,7 @@ class _TransitMapPageState extends State<TransitMapPage> {
|
||||
anchor: const Offset(0.5, 0.5),
|
||||
flat: true,
|
||||
zIndex: 3,
|
||||
icon: _busIconLoaded
|
||||
? _busIcon
|
||||
: InlqBitmap.defaultMarkerWithHue(120),
|
||||
icon: _busIconLoaded ? _busIcon : InlqBitmap.defaultMarkerWithHue(120),
|
||||
));
|
||||
|
||||
// تتبع الكاميرا للباص
|
||||
@@ -442,8 +440,10 @@ class _InfoCard extends StatelessWidget {
|
||||
size: 13, color: Colors.greenAccent.shade400),
|
||||
const SizedBox(width: 3),
|
||||
Text(
|
||||
currentStopName ?? '${'Stop'.tr} $currentStopSeq',
|
||||
style: TextStyle(fontSize: 12, color: _sub),
|
||||
currentStopName ??
|
||||
'${'Stop'.tr} $currentStopSeq',
|
||||
style:
|
||||
TextStyle(fontSize: 12, color: _sub),
|
||||
maxLines: 1,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
),
|
||||
|
||||
@@ -2,7 +2,7 @@ import 'package:flutter/material.dart';
|
||||
|
||||
/// CompetitorPriceBadge
|
||||
/// ─────────────────────
|
||||
/// ويدجت يعرض مقارنة ذكية بين سعر تريبز وسعر المنافسين.
|
||||
/// ويدجت يعرض مقارنة ذكية بين سعر سيرو وسعر المنافسين.
|
||||
/// يُستخدم في شاشة عرض الأسعار (bottomSheet) ويظهر فقط إذا توفرت
|
||||
/// بيانات المنافسين من API (has_competitor_data = true).
|
||||
class CompetitorPriceBadge extends StatelessWidget {
|
||||
|
||||
Reference in New Issue
Block a user