chore: استيراد أولي من سيرو (ecfe7568) — بلا أي تعديل

نسخة كاملة من مستودع سيرو عند ecfe7568 لتكون أساس تطبيق «انطلق».
نُسخ المتعقَّب في git فقط (12,509 ملفاً / 302 م.ب) بـ git archive، لا
`cp -r` — فاستُثنيت تلقائياً مخلفات البناء (build · node_modules ·
.dart_tool · .gradle · Pods ≈ 10.7 غ.ب) وكل ما يستثنيه .gitignore.

هذا الكوميت **بلا أي تعديل عمداً** حتى يكون كل ما يليه فرقاً مقروءاً
مقابل سيرو الأصلي. سيرو نفسه لم يُمسّ.

⚠️ لا يبني بعد: `.env` و`lib/env/env.g.dart` غير متعقَّبين في سيرو (وهذا
صحيح — أسرار لكل مستأجر). كل تطبيق فلاتر هنا يحتاج .env خاصاً بانطلق ثم
توليد env.g.dart عبر build_runner. لا تُنسخ أسرار سيرو.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Hamza-Ayed
2026-07-27 05:10:29 +03:00
co-authored by Claude Opus 5
commit 92dc6b3641
3654 changed files with 896478 additions and 0 deletions
@@ -0,0 +1,378 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:siro_service/constant/style.dart';
import 'package:siro_service/controller/functions/launch.dart';
import 'package:siro_service/views/widgets/my_scafold.dart';
import '../../../constant/colors.dart';
import '../../../constant/links.dart';
import '../../../views/widgets/my_textField.dart';
import '../../functions/encrypt_decrypt.dart';
import '../../functions/image.dart';
import '../main_controller.dart';
class AddCar extends StatelessWidget {
const AddCar({super.key});
@override
Widget build(BuildContext context) {
Get.put(MainController());
return GetBuilder<MainController>(builder: (mainController) {
return MyScaffold(
title: 'Edit car details'.tr,
isleading: true,
body: [
Expanded(
child: ListView.builder(
padding: const EdgeInsets.all(16.0),
itemCount: mainController
.driverWithoutCar.length, // 10 fields + 1 save button
itemBuilder: (context, index) {
var carData = mainController.driverWithoutCar[index];
return Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: () {
Get.to(AddCarForm(carData: carData));
},
child: Container(
decoration: AppStyle.boxDecoration1,
child: Text((carData['name_arabic']))),
),
);
}),
),
],
);
});
}
}
class AddCarForm extends StatelessWidget {
final Map carData;
const AddCarForm({super.key, required this.carData});
@override
Widget build(BuildContext context) {
Get.put(MainController());
return GetBuilder<MainController>(builder: (mainController) {
return MyScaffold(
title: 'Add Car',
action: Row(
children: [
IconButton(
onPressed: () {
makePhoneCall(carData['phone']);
},
icon: const Icon(Icons.phone),
),
IconButton(
onPressed: () {
launchCommunication('whatsapp', carData['phone'], '');
},
icon: const Icon(
Icons.message,
color: AppColor.greenColor,
),
),
],
),
isleading: true,
body: [
ListView(
children: [
Column(
children: [
GestureDetector(
onLongPress: () async {
await ImageController().choosImage(
AppLink.uploadEgypt, carData['id'], 'car_front');
},
child: Image.network(
'${AppLink.server}/card_image/car_front-${carData['id']}.jpg',
height: 200,
width: double.maxFinite,
fit: BoxFit.fill,
errorBuilder: (BuildContext context, Object exception,
StackTrace? stackTrace) {
// If the image fails to load, use the _copy version
return Image.network(
'${AppLink.server}/card_image/car_front-${carData['id']}_copy.jpg',
height: 200,
width: double.maxFinite,
fit: BoxFit.fill,
);
},
),
),
GestureDetector(
onLongPress: () async {
await ImageController().choosImage(
AppLink.uploadEgypt, carData['id'], 'car_back');
},
child: Image.network(
'${AppLink.server}/card_image/car_back-${carData['id']}.jpg',
height: 200,
width: double.maxFinite,
fit: BoxFit.fill,
errorBuilder: (BuildContext context, Object exception,
StackTrace? stackTrace) {
// If the image fails to load, use the _copy version
return Image.network(
'${AppLink.server}/card_image/car_back-${carData['id']}_copy.jpg',
height: 200,
width: double.maxFinite,
fit: BoxFit.fill,
);
},
),
),
],
),
const SizedBox(height: 9),
Form(
key: mainController.formKey,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
SizedBox(
width: Get.width * .6,
child: MyTextForm(
controller: mainController.carplateController,
label: 'car plate'.tr,
hint: 'car plate'.tr,
type: TextInputType.name,
),
),
IconButton(
onPressed: () async {
if (mainController.formKey.currentState!
.validate()) {
await mainController
.addRegistrationCarEgyptHandling(
driverId: carData['id'].toString(),
carPlate:
mainController.carplateController.text,
color: mainController.colorController.text,
colorHex:
mainController.colorHex.value.toString(),
year: mainController.yearController.text,
make: mainController.makeController.text,
model: mainController.modelController.text,
expirationDate: mainController
.expirationDateController.text,
owner: mainController.ownerController.text,
);
}
},
icon: const Icon(
Icons.upload_outlined,
color: AppColor.blueColor,
),
),
],
),
// Other fields
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: Get.width * .4,
child: MyTextForm(
controller: mainController.yearController,
label: 'Year'.tr,
hint: 'Year'.tr,
type: TextInputType.number,
),
),
SizedBox(
width: Get.width * .4,
child: DropdownButtonFormField<String>(
decoration: InputDecoration(
labelText: 'Color'.tr, // Localized label
),
value: mainController.colorHex.value.isEmpty
? null
: mainController.colorHex
.value, // Use the hex value as the current value
items: [
{'red'.tr: '#FF0000'},
{'green'.tr: '#008000'},
{'blue'.tr: '#0000FF'},
{'black'.tr: '#000000'},
{'white'.tr: '#FFFFFF'},
{'yellow'.tr: '#FFFF00'},
{'purple'.tr: '#800080'},
{'orange'.tr: '#FFA500'},
{'pink'.tr: '#FFC0CB'},
{'brown'.tr: '#A52A2A'},
{'gray'.tr: '#808080'},
{'cyan'.tr: '#00FFFF'},
{'magenta'.tr: '#FF00FF'},
{'lime'.tr: '#00FF00'},
{'indigo'.tr: '#4B0082'},
{'violet'.tr: '#EE82EE'},
{'gold'.tr: '#FFD700'},
{'silver'.tr: '#C0C0C0'},
{'teal'.tr: '#008080'},
{'navy'.tr: '#000080'},
].map((colorMap) {
String colorName = colorMap.keys.first;
String colorValue = colorMap.values.first;
return DropdownMenuItem<String>(
value: colorValue,
child: Text(colorName),
);
}).toList(),
onChanged: (value) {
if (value != null) {
// Find the selected color name based on the hex value
String selectedColorName = '';
for (var colorMap in [
{'red'.tr: '#FF0000'},
{'green'.tr: '#008000'},
{'blue'.tr: '#0000FF'},
{'black'.tr: '#000000'},
{'white'.tr: '#FFFFFF'},
{'yellow'.tr: '#FFFF00'},
{'purple'.tr: '#800080'},
{'orange'.tr: '#FFA500'},
{'pink'.tr: '#FFC0CB'},
{'brown'.tr: '#A52A2A'},
{'gray'.tr: '#808080'},
{'cyan'.tr: '#00FFFF'},
{'magenta'.tr: '#FF00FF'},
{'lime'.tr: '#00FF00'},
{'indigo'.tr: '#4B0082'},
{'violet'.tr: '#EE82EE'},
{'gold'.tr: '#FFD700'},
{'silver'.tr: '#C0C0C0'},
{'teal'.tr: '#008080'},
{'navy'.tr: '#000080'},
]) {
if (colorMap.values.first == value) {
selectedColorName = colorMap.keys.first;
break;
}
}
mainController.colorController.text =
selectedColorName;
mainController.colorHex.value = value;
}
},
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: Get.width * .4,
child: MyTextForm(
controller: mainController.makeController,
label: 'Make'.tr,
hint: 'Make'.tr,
type: TextInputType.name,
),
),
SizedBox(
width: Get.width * .4,
child: MyTextForm(
controller: mainController.modelController,
label: 'Model'.tr,
hint: 'Model'.tr,
type: TextInputType.name,
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: Get.width * .4,
child: TextField(
controller:
mainController.expirationDateController,
decoration: InputDecoration(
labelText: 'Expiration Date'.tr,
hintText: 'Expiration Date'.tr,
),
readOnly:
true, // Make the field read-only to prevent manual input
onTap: () async {
DateTime pickedDate =
DateTime.now(); // Declare the variable here
await showCupertinoModalPopup<void>(
context: context,
builder: (context) => Container(
height: 250,
color: Colors.white,
child: Column(
children: [
SizedBox(
height: 150,
child: CupertinoDatePicker(
initialDateTime: pickedDate,
minimumDate: DateTime(
1955), // Set the starting date
maximumDate: DateTime(
2034), // Set the ending date
mode: CupertinoDatePickerMode.date,
onDateTimeChanged:
(DateTime dateTime) {
pickedDate = dateTime;
},
),
),
CupertinoButton(
child: Text('Done'.tr),
onPressed: () {
String formattedDate =
DateFormat('yyyy-MM-dd')
.format(pickedDate);
mainController
.expirationDateController
.text =
formattedDate.toString();
Navigator.of(context).pop();
},
),
],
),
),
);
},
),
),
SizedBox(
width: Get.width * .4,
child: MyTextForm(
controller: mainController.ownerController,
label: 'Owner'.tr,
hint: 'Owner'.tr,
type: TextInputType.name,
),
),
],
),
],
),
)
],
)
]);
});
}
}
@@ -0,0 +1,56 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_service/views/widgets/my_scafold.dart';
import '../../best_driver_controllers.dart';
import '../../functions/encrypt_decrypt.dart';
class DriverTheBestAlexandria extends StatelessWidget {
const DriverTheBestAlexandria({super.key});
@override
Widget build(BuildContext context) {
Get.put(DriverTheBestAlexandriaController(), permanent: true);
return MyScaffold(
title: 'Alexandria'.tr,
body: [
GetBuilder<DriverTheBestAlexandriaController>(builder: (driverthebest) {
return driverthebest.driver.isNotEmpty
? ListView.builder(
itemCount: driverthebest.driver.length,
itemBuilder: (context, index) {
final driver = driverthebest.driver[index];
return ListTile(
leading: CircleAvatar(
child: Text(
(int.parse(driver['driver_count'] * 5) / 3600)
.toStringAsFixed(0),
),
),
title: Text((driver['name_arabic']) ??
'Unknown Name'.tr),
subtitle: Text(
'${'Phone'.tr}: ${(driver['phone']) ?? 'N/A'.tr}'),
trailing: IconButton(
onPressed: () async {
Get.defaultDialog(
title:
'are you sure to pay to this driver gift'.tr,
middleText: '',
onConfirm: () async {},
onCancel: () => Get.back());
},
icon: const Icon(Icons.wallet_giftcard_rounded),
),
);
},
)
: Center(
child: Text('No drivers available.'.tr),
);
})
],
isleading: true,
);
}
}
@@ -0,0 +1,112 @@
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_service/views/widgets/mycircular.dart';
import 'package:siro_service/controller/functions/encrypt_decrypt.dart';
import 'package:siro_service/views/widgets/my_scafold.dart';
import '../../../constant/colors.dart';
import '../../../constant/links.dart';
import '../../../views/widgets/elevated_btn.dart';
import '../../functions/crud.dart';
import 'alexandria_besr_driver.dart';
import 'giza_best_driver.dart';
class DriverTheBest extends StatelessWidget {
const DriverTheBest({super.key});
@override
Widget build(BuildContext context) {
Get.put(Driverthebest());
return MyScaffold(
title: 'Best Drivers'.tr,
body: [
Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
MyElevatedButton(
title: 'Giza',
onPressed: () {
Get.to(() => DriverTheBestGiza());
}),
MyElevatedButton(
title: 'Alexandria',
onPressed: () {
Get.to(() => DriverTheBestAlexandria());
}),
],
),
GetBuilder<Driverthebest>(builder: (driverthebest) {
return driverthebest.driver.isNotEmpty
? SizedBox(
height: Get.height * .7,
child: ListView.builder(
itemCount: driverthebest.driver.length,
itemBuilder: (context, index) {
final driver = driverthebest.driver[index];
return ListTile(
leading: CircleAvatar(
child: Text(
((int.parse(driver['driver_count']) * 5) / 3600)
.toStringAsFixed(0),
),
),
title: Text((driver['name_arabic']) ??
'Unknown Name'.tr),
subtitle: Text(
'${'Phone'.tr}: ${(driver['phone']) ?? 'N/A'.tr}'),
trailing: IconButton(
onPressed: () async {
// Get.defaultDialog(
// title:
// 'are you sure to pay to this driver gift'.tr,
// middleText: '',
// onConfirm: () async {
// // final wallet = Get.put(WalletController());
// // await wallet.addPaymentToDriver('100',
// // driver['id'].toString(), driver['token']);
// // await wallet.addSeferWallet(
// // '100', driver['id'].toString());
// },
// onCancel: () => Get.back());
},
icon: const Icon(Icons.wallet_giftcard_rounded),
),
);
},
),
)
: Center(
child: Text('No drivers available.'.tr),
);
}),
],
)
],
isleading: true,
);
}
}
class Driverthebest extends GetxController {
bool isLoading = false;
List driver = [];
getBestDriver() async {
var res = await CRUD().get(link: AppLink.getBestDriver, payload: {});
if (res != 'failure') {
driver = res['message'];
update();
} else {
mySnackbarError('Error'.tr);
}
}
@override
void onInit() {
getBestDriver();
super.onInit();
}
}
@@ -0,0 +1,12 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_service/views/widgets/my_scafold.dart';
class Complaint extends StatelessWidget {
const Complaint({super.key});
@override
Widget build(BuildContext context) {
return MyScaffold(title: "View complaint".tr, isleading: true, body: []);
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,226 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import '../main_controller.dart';
class DriverPage extends StatelessWidget {
DriverPage({super.key});
final MainController mainController = Get.find<MainController>();
@override
Widget build(BuildContext context) {
return Scaffold(
body: GetBuilder<MainController>(builder: (mainController) {
Map data = mainController.driverData['message'][0];
return CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('${data['first_name']} ${data['last_name']}'),
),
child: SafeArea(
child: CupertinoScrollbar(
child: ListView(
children: [
_buildDriverInfoSection(data),
_buildCommunicationSection(data, context),
_buildStatisticsSection(data),
_buildCarInfoSection(data),
_buildLicenseInfoSection(data),
_buildBankInfoSection(data),
const SizedBox(height: 40),
],
),
),
),
);
}),
);
}
// ============================================================
// REUSABLE EDIT ROW
// ============================================================
Widget _buildEditableRow(String label, String key, Map data) {
return CupertinoListTile(
title: Text(label),
trailing: Text(
data[key].toString(),
style: const TextStyle(color: CupertinoColors.systemGrey),
),
onTap: () => _openEditSheet(label, key, data[key]),
);
}
void _openEditSheet(String label, String key, dynamic value) {
final TextEditingController controller =
TextEditingController(text: value.toString());
Get.bottomSheet(
CupertinoPageScaffold(
navigationBar: CupertinoNavigationBar(
middle: Text('${'Edit'.tr} $label'),
trailing: GestureDetector(
child: Text(
'Save'.tr,
style: const TextStyle(color: CupertinoColors.activeBlue),
),
onTap: () {
mainController.updateDriverField(key, controller.text);
Get.back();
},
),
),
child: SafeArea(
child: Padding(
padding: const EdgeInsets.all(16),
child: CupertinoTextField(
controller: controller,
autofocus: true,
),
),
),
),
);
}
// ============================================================
// SECTIONS
// ============================================================
Widget _buildDriverInfoSection(Map data) {
return CupertinoListSection.insetGrouped(
header: Text('Driver Information'.tr),
children: [
_buildEditableRow('Name Arabic'.tr, 'name_arabic', data),
// _buildEditableRow('Name English'.tr, 'name_english', data),
_buildEditableRow('Phone'.tr, 'phone', data),
_buildEditableRow('Email'.tr, 'email', data),
_buildEditableRow('Gender'.tr, 'gender', data),
_buildEditableRow('Birthdate'.tr, 'birthdate', data),
_buildEditableRow('National Number'.tr, 'national_number', data),
// _buildEditableRow('Religion'.tr, 'religion', data),
// _buildEditableRow('Occupation'.tr, 'occupation', data),
// _buildEditableRow('Education'.tr, 'education', data),
],
);
}
Widget _buildStatisticsSection(Map data) {
return CupertinoListSection.insetGrouped(
header: Text('Driver Statistics'.tr),
children: [
_buildInfoRow('Total Rides'.tr, data['countRide'].toString()),
_buildInfoRow('Average Rating'.tr, data['rating'].toString()),
_buildInfoRow('Total Payments'.tr, data['totalPayment'].toString()),
_buildInfoRow(
'Wallet Balance'.tr, data['totalDriverWallet'].toString()),
_buildInfoRow('Complaints'.tr, data['countComplaint'].toString()),
_buildInfoRow('Scam Reports'.tr, data['countScam'].toString()),
_buildInfoRow(
'Passengers Rated'.tr, data['DRatingPassengersCount'].toString()),
_buildInfoRow(
'Avg Passenger Rating'.tr, data['avgDRatingPassenger'].toString()),
],
);
}
// Read-only row widget
Widget _buildInfoRow(String label, String value) {
return CupertinoListTile(
title: Text(label),
trailing: Text(
value,
style: const TextStyle(color: CupertinoColors.systemGrey),
),
);
}
Widget _buildCarInfoSection(Map data) {
return CupertinoListSection.insetGrouped(
header: Text('Vehicle Information'.tr),
children: [
// _buildEditableRow('VIN'.tr, 'vin', data),
_buildEditableRow('Plate Number'.tr, 'car_plate', data),
_buildEditableRow('Make'.tr, 'make', data),
_buildEditableRow('Model'.tr, 'model', data),
_buildEditableRow('Year'.tr, 'year', data),
_buildEditableRow('Color'.tr, 'color', data),
_buildEditableRow('Fuel Type'.tr, 'fuel', data),
// _buildEditableRow('Displacement'.tr, 'displacement', data),
_buildEditableRow('Registration Date'.tr, 'registration_date', data),
_buildEditableRow('Expiration Date'.tr, 'expiration_date', data),
],
);
}
Widget _buildLicenseInfoSection(Map data) {
return CupertinoListSection.insetGrouped(
header: Text('License Information'.tr),
children: [
_buildEditableRow('License Type'.tr, 'license_type', data),
_buildEditableRow('Card ID'.tr, 'card_id', data),
_buildEditableRow('Issue Date'.tr, 'issue_date', data),
_buildEditableRow('Expiry Date'.tr, 'expiry_date', data),
_buildEditableRow('Categories'.tr, 'license_categories', data),
],
);
}
Widget _buildBankInfoSection(Map data) {
return CupertinoListSection.insetGrouped(
header: Text('Bank Information'.tr),
children: [
_buildEditableRow('Account Number'.tr, 'accountBank', data),
_buildEditableRow('Bank Code'.tr, 'bankCode', data),
],
);
}
Widget _buildCommunicationSection(Map data, BuildContext context) {
String phone = data['phone'] ?? '';
String name = data['first_name'] ?? '';
return CupertinoListSection.insetGrouped(
header: Text('Quick Communication'.tr),
children: [
CupertinoListTile(
title: Text('Call Driver'.tr),
leading: const Icon(CupertinoIcons.phone_fill, color: Colors.green),
onTap: () => mainController.makePhoneCall(phone),
),
CupertinoListTile(
title: Text('WhatsApp: Activation'.tr),
leading: const Icon(Icons.send, color: Colors.green),
onTap: () => mainController.launchCommunication(
'whatsapp',
phone,
'أهلاً بك يا كابتن $name في سيرو! تم تفعيل حسابك بنجاح وأصبحت مستعداً لاستقبال الرحلات.',
),
),
CupertinoListTile(
title: Text('WhatsApp: Missing Docs'.tr),
leading: const Icon(Icons.send, color: Colors.orange),
onTap: () => mainController.launchCommunication(
'whatsapp',
phone,
'مرحباً كابتن $name، يرجى تزويدنا بالأوراق الناقصة أو غير الواضحة عبر الواتساب لإكمال تفعيل حسابك.',
),
),
CupertinoListTile(
title: Text('WhatsApp: Support'.tr),
leading: const Icon(Icons.send, color: Colors.blue),
onTap: () => mainController.launchCommunication(
'whatsapp',
phone,
'مرحباً كابتن $name، معك الدعم الفني من شركة سيرو. كيف يمكنني مساعدتك اليوم؟',
),
),
],
);
}
}
@@ -0,0 +1,224 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_service/constant/colors.dart';
import 'package:siro_service/controller/mainController/main_controller.dart';
import 'package:siro_service/views/widgets/my_scafold.dart';
import 'package:siro_service/main.dart';
import 'package:siro_service/constant/box_name.dart';
import 'review_driver_page.dart';
class DriversCantRegister extends StatelessWidget {
DriversCantRegister({super.key});
//
@override
Widget build(BuildContext context) {
Get.put(MainController());
// Unified action button (WhatsApp - Edit - etc)
Widget buildActionButton({
required IconData icon,
required Color color,
required VoidCallback onPressed,
}) {
return CupertinoButton(
padding: const EdgeInsets.symmetric(horizontal: 6.0),
onPressed: onPressed,
child: Icon(icon, color: color, size: 28),
);
}
return MyScaffold(
title: 'Drivers Want Register'.tr,
isleading: true,
body: [
GetBuilder<MainController>(builder: (mainController) {
return Column(
children: [
// Search
Padding(
padding: const EdgeInsets.fromLTRB(16, 8, 16, 8),
child: CupertinoSearchTextField(
keyboardType: TextInputType.phone,
onChanged: (value) => mainController.searchDrivers(value),
placeholder: 'Search by phone number'.tr,
),
),
// List
Expanded(
child: mainController.filteredDrivers.isEmpty
? Center(
child: Text(
'No drivers found'.tr,
style:
TextStyle(color: Colors.grey[600], fontSize: 16),
),
)
: ListView.builder(
itemCount: mainController.filteredDrivers.length,
itemBuilder: (context, index) {
final driver = mainController.filteredDrivers[index];
final notesController =
TextEditingController(text: driver['note'] ?? '');
return Card(
margin: const EdgeInsets.symmetric(
horizontal: 12.0, vertical: 8.0),
elevation: 3,
clipBehavior: Clip.antiAlias,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12),
side: BorderSide(
color: driver['note'] != null
? AppColor.secondaryColor
: Colors.transparent,
width: 2.5,
),
),
child: Padding(
padding:
const EdgeInsets.fromLTRB(16, 12, 16, 12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header
Text(
'Driver Phone: ${driver['first_name'] ?? ''} ${driver['last_name'] ?? ''}',
style: const TextStyle(
fontWeight: FontWeight.bold,
fontSize: 16,
color: Colors.black87,
),
),
const Divider(height: 20),
// Phone Row
Row(
children: [
const Icon(
CupertinoIcons.phone_fill,
color: AppColor.primaryColor,
size: 22,
),
const SizedBox(width: 12),
Expanded(
child: InkWell(
onTap: () {
mainController.makePhoneCall(
driver['phone_number']);
},
child: Text(
driver['phone_number'] ?? 'N/A',
style: const TextStyle(
fontSize: 17,
letterSpacing: 1.2,
),
),
),
),
// WhatsApp button
buildActionButton(
icon: Icons.send,
color: const Color(0xFF25D366),
onPressed: () {
String message = "مرحباً،\n\n"
"يظهر لدينا في نظام تطبيق *سيرو* أنك لم تكمل عملية التسجيل بعد.\n"
"ندعوك لإكمال التسجيل للاستفادة من مزايا التطبيق والبدء بالعمل معنا.\n\n"
"إذا احتجت لأي مساعدة، تواصل معنا على خدمة العملاء:\n"
"+963 952 475 742\n\n"
"+963 952 475 740\n\n"
"فريق سيرو يتمنى لك يوماً سعيداً.";
mainController.launchCommunication(
'whatsapp',
driver['phone_number'],
message,
);
},
),
// Edit button → go to review page (only if driverId exists)
if (driver['driverId'] != null)
buildActionButton(
icon: CupertinoIcons
.pencil_ellipsis_rectangle,
color: AppColor.gold,
onPressed: () {
Get.to(
() => const ReviewDriverPage(),
arguments: {
"phone": driver['phone_number'],
"driverId": driver['driverId'],
},
);
},
),
buildActionButton(
icon: CupertinoIcons
.pencil_ellipsis_rectangle,
color: AppColor.redColor,
onPressed: () {
mainController
.deleteDriverNotCompleteRegistration(
driver['phone_number']);
},
),
],
),
const SizedBox(height: 16),
// Notes box
CupertinoTextField(
controller: notesController,
placeholder: "Additional comments".tr,
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.grey.shade100,
borderRadius: BorderRadius.circular(8),
),
maxLines: 2,
),
const SizedBox(height: 12),
// Save button
Align(
alignment: AlignmentDirectional.centerEnd,
child: CupertinoButton(
padding: const EdgeInsets.symmetric(
horizontal: 20),
color: AppColor.secondaryColor,
onPressed: () {
mainController
.saveNoteForDriverNotCompleteRegistration(
driver['phone_number'],
box.read(BoxName.employeename) ??
'none',
notesController.text,
);
},
child: Text('Save Notes'.tr),
),
),
],
),
),
);
},
),
),
],
);
}),
],
);
}
}
@@ -0,0 +1,334 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:intl/intl.dart';
import 'package:siro_service/controller/mainController/main_controller.dart';
import 'package:siro_service/views/widgets/my_scafold.dart';
import '../../../constant/colors.dart';
import '../../../constant/links.dart';
import '../../../views/widgets/my_textField.dart';
import '../../functions/image.dart';
import '../../functions/launch.dart';
class EditCar extends StatelessWidget {
final Map carData;
const EditCar({super.key, required this.carData});
@override
Widget build(BuildContext context) {
Get.put(MainController());
return GetBuilder<MainController>(builder: (mainController) {
return MyScaffold(
title: 'Edit',
isleading: true,
action: Row(
children: [
IconButton(
onPressed: () {
makePhoneCall(carData['phone']);
},
icon: const Icon(Icons.phone),
),
IconButton(
onPressed: () {
launchCommunication('whatsapp', carData['phone'], '');
},
icon: const Icon(
Icons.message,
color: AppColor.greenColor,
),
),
],
),
body: [
ListView(
children: [
Column(
children: [
GestureDetector(
onLongPress: () async {
await ImageController().choosImage(AppLink.uploadEgypt,
carData['driverID'], 'car_front');
},
child: Image.network(
'https://sefer.click/sefer/card_image/car_front-${carData['driverID']}.jpg',
height: 200,
width: double.maxFinite,
fit: BoxFit.fill,
errorBuilder: (BuildContext context, Object exception,
StackTrace? stackTrace) {
// If the image fails to load, use the _copy version
return Image.network(
'https://sefer.click/sefer/card_image/car_front-${carData['driverID']}_copy.jpg',
height: 200,
width: double.maxFinite,
fit: BoxFit.fill,
);
},
),
),
GestureDetector(
onLongPress: () async {
await ImageController().choosImage(
AppLink.uploadEgypt, carData['id'], 'car_back');
},
child: Image.network(
'https://sefer.click/sefer/card_image/car_back-${carData['driverID']}.jpg',
height: 200,
width: double.maxFinite,
fit: BoxFit.fill,
errorBuilder: (BuildContext context, Object exception,
StackTrace? stackTrace) {
// If the image fails to load, use the _copy version
return Image.network(
'https://sefer.click/sefer/card_image/car_back-${carData['driverID']}_copy.jpg',
height: 200,
width: double.maxFinite,
fit: BoxFit.fill,
);
},
),
),
],
),
const SizedBox(height: 9),
Form(
key: mainController.formKey,
child: Column(
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
SizedBox(
width: Get.width * .6,
child: MyTextForm(
controller: mainController.carplateController,
label: 'car plate'.tr,
hint: 'car plate'.tr,
type: TextInputType.name,
),
),
IconButton(
onPressed: () async {
if (mainController.formKey.currentState!
.validate()) {
await mainController.editCarPlateNotEdit(
carData['driverID'].toString(),
mainController.carplateController.text,
mainController.colorController.text,
mainController.colorHex.value.toString(),
mainController.yearController.text,
mainController.makeController.text,
mainController.modelController.text,
mainController.expirationDateController.text,
mainController.ownerController.text,
);
}
},
icon: const Icon(
Icons.upload_outlined,
color: AppColor.blueColor,
),
),
],
),
// Other fields
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: Get.width * .4,
child: MyTextForm(
controller: mainController.yearController,
label: 'Year'.tr,
hint: 'Year'.tr,
type: TextInputType.number,
),
),
SizedBox(
width: Get.width * .4,
child: DropdownButtonFormField<String>(
decoration: InputDecoration(
labelText: 'Color'.tr, // Localized label
),
value: mainController.colorHex.value.isEmpty
? null
: mainController.colorHex
.value, // Use the hex value as the current value
items: [
{'red'.tr: '#FF0000'},
{'green'.tr: '#008000'},
{'blue'.tr: '#0000FF'},
{'black'.tr: '#000000'},
{'white'.tr: '#FFFFFF'},
{'yellow'.tr: '#FFFF00'},
{'purple'.tr: '#800080'},
{'orange'.tr: '#FFA500'},
{'pink'.tr: '#FFC0CB'},
{'brown'.tr: '#A52A2A'},
{'gray'.tr: '#808080'},
{'cyan'.tr: '#00FFFF'},
{'magenta'.tr: '#FF00FF'},
{'lime'.tr: '#00FF00'},
{'indigo'.tr: '#4B0082'},
{'violet'.tr: '#EE82EE'},
{'gold'.tr: '#FFD700'},
{'silver'.tr: '#C0C0C0'},
{'teal'.tr: '#008080'},
{'navy'.tr: '#000080'},
].map((colorMap) {
String colorName = colorMap.keys.first;
String colorValue = colorMap.values.first;
return DropdownMenuItem<String>(
value: colorValue,
child: Text(colorName),
);
}).toList(),
onChanged: (value) {
if (value != null) {
// Find the selected color name based on the hex value
String selectedColorName = '';
for (var colorMap in [
{'red'.tr: '#FF0000'},
{'green'.tr: '#008000'},
{'blue'.tr: '#0000FF'},
{'black'.tr: '#000000'},
{'white'.tr: '#FFFFFF'},
{'yellow'.tr: '#FFFF00'},
{'purple'.tr: '#800080'},
{'orange'.tr: '#FFA500'},
{'pink'.tr: '#FFC0CB'},
{'brown'.tr: '#A52A2A'},
{'gray'.tr: '#808080'},
{'cyan'.tr: '#00FFFF'},
{'magenta'.tr: '#FF00FF'},
{'lime'.tr: '#00FF00'},
{'indigo'.tr: '#4B0082'},
{'violet'.tr: '#EE82EE'},
{'gold'.tr: '#FFD700'},
{'silver'.tr: '#C0C0C0'},
{'teal'.tr: '#008080'},
{'navy'.tr: '#000080'},
]) {
if (colorMap.values.first == value) {
selectedColorName = colorMap.keys.first;
break;
}
}
mainController.colorController.text =
selectedColorName;
mainController.colorHex.value = value;
}
},
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: Get.width * .4,
child: MyTextForm(
controller: mainController.makeController,
label: 'Make'.tr,
hint: 'Make'.tr,
type: TextInputType.name,
),
),
SizedBox(
width: Get.width * .4,
child: MyTextForm(
controller: mainController.modelController,
label: 'Model'.tr,
hint: 'Model'.tr,
type: TextInputType.name,
),
),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
SizedBox(
width: Get.width * .4,
child: TextField(
controller:
mainController.expirationDateController,
decoration: InputDecoration(
labelText: 'Expiration Date'.tr,
hintText: 'Expiration Date'.tr,
),
readOnly:
true, // Make the field read-only to prevent manual input
onTap: () async {
DateTime pickedDate =
DateTime.now(); // Declare the variable here
await showCupertinoModalPopup<void>(
context: context,
builder: (context) => Container(
height: 250,
color: Colors.white,
child: Column(
children: [
SizedBox(
height: 150,
child: CupertinoDatePicker(
initialDateTime: pickedDate,
minimumDate: DateTime(
1955), // Set the starting date
maximumDate: DateTime(
2034), // Set the ending date
mode: CupertinoDatePickerMode.date,
onDateTimeChanged:
(DateTime dateTime) {
pickedDate = dateTime;
},
),
),
CupertinoButton(
child: Text('Done'.tr),
onPressed: () {
String formattedDate =
DateFormat('yyyy-MM-dd')
.format(pickedDate);
mainController
.expirationDateController
.text =
formattedDate.toString();
Navigator.of(context).pop();
},
),
],
),
),
);
},
),
),
SizedBox(
width: Get.width * .4,
child: MyTextForm(
controller: mainController.ownerController,
label: 'Owner'.tr,
hint: 'Owner'.tr,
type: TextInputType.name,
),
),
],
),
],
),
)
],
)
]);
});
}
}
@@ -0,0 +1,48 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_service/constant/style.dart';
import 'package:siro_service/controller/mainController/pages/edit_car.dart';
import 'package:siro_service/views/widgets/my_scafold.dart';
import '../../functions/encrypt_decrypt.dart';
import '../main_controller.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
class EditCarPlate extends StatelessWidget {
const EditCarPlate({super.key});
@override
Widget build(BuildContext context) {
Get.put(MainController());
return GetBuilder<MainController>(builder: (mainController) {
return MyScaffold(
title: 'Edit car details'.tr,
isleading: true,
body: [
Expanded(
child: ListView.builder(
padding: const EdgeInsets.all(16.0),
itemCount: mainController
.carPlateNotEdit.length, // 10 fields + 1 save button
itemBuilder: (context, index) {
var carData = mainController.carPlateNotEdit[index];
return Padding(
padding: const EdgeInsets.all(8.0),
child: InkWell(
onTap: () {
Get.to(EditCar(carData: carData));
},
child: Container(
decoration: AppStyle.boxDecoration1,
child: Text((carData['owner']))),
),
);
}),
),
],
);
});
}
}
@@ -0,0 +1,56 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_service/views/widgets/my_scafold.dart';
import '../../best_driver_controllers.dart';
import '../../functions/encrypt_decrypt.dart';
class DriverTheBestGiza extends StatelessWidget {
const DriverTheBestGiza({super.key});
@override
Widget build(BuildContext context) {
Get.put(DriverTheBestGizaController(), permanent: true);
return MyScaffold(
title: 'Giza'.tr,
body: [
GetBuilder<DriverTheBestGizaController>(builder: (driverthebest) {
return driverthebest.driver.isNotEmpty
? ListView.builder(
itemCount: driverthebest.driver.length,
itemBuilder: (context, index) {
final driver = driverthebest.driver[index];
return ListTile(
leading: CircleAvatar(
child: Text(
(int.parse(driver['driver_count'] * 5) / 3600)
.toStringAsFixed(0),
),
),
title: Text((driver['name_arabic']) ??
'Unknown Name'.tr),
subtitle: Text(
'${'Phone'.tr}: ${(driver['phone']) ?? 'N/A'.tr}'),
trailing: IconButton(
onPressed: () async {
Get.defaultDialog(
title:
'are you sure to pay to this driver gift'.tr,
middleText: '',
onConfirm: () async {},
onCancel: () => Get.back());
},
icon: const Icon(Icons.wallet_giftcard_rounded),
),
);
},
)
: Center(
child: Text('No drivers available.'.tr),
);
})
],
isleading: true,
);
}
}
@@ -0,0 +1,485 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_service/constant/colors.dart';
// Importa tu nuevo controlador de servicio
import 'package:siro_service/controller/mainController/ragister_service_controller.dart';
import 'package:siro_service/views/widgets/elevated_btn.dart';
import 'package:siro_service/views/widgets/my_scafold.dart';
// El import del antiguo controlador ya no es necesario
// import '../registration_captain_controller.dart';
class RegisterCaptain extends StatelessWidget {
const RegisterCaptain({super.key});
@override
Widget build(BuildContext context) {
// Instancia el NUEVO controlador
final controller = Get.put(RegisterCaptainServiceController());
return MyScaffold(
title: 'Syrian Documents Check'.tr,
isleading: true,
body: [
Obx(() {
if (controller.isLoading.value) {
return const Center(child: CircularProgressIndicator());
}
return Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: StepIndicator(
currentStep: controller.currentPageIndex.value,
totalSteps: 4,
),
),
Expanded(
child: PageView(
controller: controller.pageController,
onPageChanged: (index) {
controller.currentPageIndex.value = index;
},
children: [
// <-- PÁGINAS REACTIVADAS CON EL NUEVO CONTROLADOR -->
_buildSyrianDriverLicenseFront(context, controller),
_buildSyrianDriverLicenseBack(context, controller),
_buildSyrianCarLicenseFront(context, controller),
_buildSyrianCarLicenseBack(controller),
],
),
),
_buildNavigationControls(controller),
],
);
}),
],
);
}
// Este método ya estaba usando el controlador correcto
Widget _buildNavigationControls(RegisterCaptainServiceController controller) {
bool isLastPage = controller.currentPageIndex.value == 3;
bool isFirstPage = controller.currentPageIndex.value == 0;
return Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (!isFirstPage)
MyElevatedButton(
title: 'Back'.tr,
onPressed: controller.previousPage,
kolor: Colors.grey,
),
if (isFirstPage) const Spacer(),
MyElevatedButton(
title: isLastPage ? 'Save and Activate'.tr : 'Next'.tr,
onPressed: isLastPage
? controller.updateAndActivateSyrianDriver
: controller.nextPage,
kolor: isLastPage ? AppColor.greenColor : AppColor.primaryColor,
),
],
),
);
}
Widget _buildTextField({
required String label,
required TextEditingController controller,
IconData? icon,
TextInputType keyboardType = TextInputType.text,
VoidCallback? onTap,
String? hintText,
}) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: TextFormField(
controller: controller,
keyboardType: keyboardType,
readOnly: onTap != null,
onTap: onTap,
decoration: InputDecoration(
labelText: label.tr,
hintText: hintText?.tr,
prefixIcon:
icon != null ? Icon(icon, color: AppColor.secondaryColor) : null,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide:
const BorderSide(color: AppColor.secondaryColor, width: 2),
),
),
),
);
}
Widget _buildPageContent({
required RxString imageUrl,
required List<Widget> formFields,
}) {
return SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// ClipRRect(
// borderRadius: BorderRadius.circular(12),
// child: Image.network(
// imageUrl.value,
// fit: BoxFit.fitWidth,
// errorBuilder: (context, error, stackTrace) {
// return Container(
// height: 200,
// color: Colors.grey[200],
// child: Center(
// child: Text(
// 'Image not available'.tr,
// style: const TextStyle(color: Colors.grey),
// ),
// ),
// );
// },
// ),
// ),
// const SizedBox(height: 20),
const Divider(),
...formFields,
],
),
);
}
// <-- MODIFICADO: Usa RegisterCaptainServiceController -->
Widget _buildColorDropdown(RegisterCaptainServiceController controller) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Obx(
() => DropdownButtonFormField<String>(
value: controller.colorHex.value.isEmpty
? null
: controller.colorHex.value,
isExpanded: true,
decoration: InputDecoration(
labelText: 'Car Color'.tr,
prefixIcon: Icon(Icons.color_lens, color: AppColor.secondaryColor),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
// <-- MODIFICADO: Usa la referencia estática del nuevo controlador -->
items: RegisterCaptainServiceController.kCarColorOptions.map((opt) {
final hex = opt['hex']!;
final key = opt['key']!;
return DropdownMenuItem<String>(
value: hex,
child: Row(
children: [
Container(
width: 18,
height: 18,
decoration: BoxDecoration(
color: controller.hexToColor(hex),
shape: BoxShape.circle,
border: Border.all(color: Colors.black12),
),
),
const SizedBox(width: 12),
Expanded(child: Text(key.tr)),
],
),
);
}).toList(),
onChanged: (hex) {
if (hex != null) {
controller.updateColorSelection(hex);
}
},
),
),
);
}
// <-- MODIFICADO: Usa RegisterCaptainServiceController -->
Widget _buildGenderDropdown(RegisterCaptainServiceController controller) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Obx(
() => DropdownButtonFormField<String>(
value: controller.selectedGender.value.isEmpty
? null
: controller.selectedGender.value,
isExpanded: true,
decoration: InputDecoration(
labelText: 'Gender'.tr,
prefixIcon: Icon(Icons.wc, color: AppColor.secondaryColor),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
items: ['Male', 'Female'].map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value.tr),
);
}).toList(),
onChanged: (String? newValue) {
if (newValue != null) {
controller.selectedGender.value = newValue;
}
},
),
),
);
}
// <-- MODIFICADO: Usa RegisterCaptainServiceController -->
Widget _buildFuelDropdown(RegisterCaptainServiceController controller) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Obx(
() => DropdownButtonFormField<String>(
// <-- MODIFICADO: Usa la referencia estática del nuevo controlador -->
value: RegisterCaptainServiceController.kFuelOptions
.contains(controller.selectedFuel.value)
? controller.selectedFuel.value
: null,
isExpanded: true,
decoration: InputDecoration(
labelText: 'Fuel Type'.tr,
prefixIcon:
Icon(Icons.local_gas_station, color: AppColor.secondaryColor),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
// <-- MODIFICADO: Usa la referencia estática del nuevo controlador -->
items:
RegisterCaptainServiceController.kFuelOptions.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value.tr),
);
}).toList(),
onChanged: (String? newValue) {
if (newValue != null) {
controller.selectedFuel.value = newValue;
}
},
),
),
);
}
// --- PAGE 1: Driver License Front ---
// <-- MODIFICADO: Usa RegisterCaptainServiceController -->
Widget _buildSyrianDriverLicenseFront(
BuildContext context, RegisterCaptainServiceController controller) {
return _buildPageContent(
imageUrl: controller.docUrls['driver_license_front']!,
formFields: [
Row(
children: [
Expanded(
child: _buildTextField(
label: 'First Name',
controller: controller.firstNameController,
keyboardType: TextInputType.name),
),
const SizedBox(width: 8),
Expanded(
child: _buildTextField(
label: 'Last Name',
controller: controller.lastNameController,
keyboardType: TextInputType.name),
),
],
),
_buildTextField(
label: 'Phone Number',
controller: controller.phoneController,
hintText: 'e.g., 963992952235',
icon: Icons.phone,
keyboardType: TextInputType.phone,
),
_buildTextField(
label: 'Place of Registration',
controller: controller.siteController,
icon: Icons.location_city),
Row(
children: [
Expanded(
child: _buildTextField(
label: 'National Number',
controller: controller.nationalNumberController,
keyboardType: TextInputType.number,
icon: Icons.fingerprint),
),
const SizedBox(width: 8),
Expanded(child: _buildGenderDropdown(controller)),
],
),
_buildTextField(
label: 'Birthdate',
controller: controller.birthdateController,
icon: Icons.cake,
onTap: () =>
controller.selectDate(context, controller.birthdateController)),
],
);
}
// --- PAGE 2: Driver License Back ---
// <-- MODIFICADO: Usa RegisterCaptainServiceController -->
Widget _buildSyrianDriverLicenseBack(
BuildContext context, RegisterCaptainServiceController controller) {
return _buildPageContent(
imageUrl: controller.docUrls['driver_license_back']!,
formFields: [
_buildTextField(
label: 'License Category',
controller: controller.licenseCategoriesController),
_buildTextField(
label: 'Expiry Date',
controller: controller.expiryDateController,
icon: Icons.event_busy,
onTap: () => controller.selectDate(
context, controller.expiryDateController)),
],
);
}
// --- PAGE 3: Car License Front ---
// <-- MODIFICADO: Usa RegisterCaptainServiceController -->
Widget _buildSyrianCarLicenseFront(
BuildContext context, RegisterCaptainServiceController controller) {
return _buildPageContent(
imageUrl: controller.docUrls['car_license_front']!,
formFields: [
_buildTextField(
label: 'Owner Name',
controller: controller.ownerController,
keyboardType: TextInputType.name,
icon: Icons.person_search),
Row(
children: [
Expanded(child: _buildColorDropdown(controller)),
const SizedBox(width: 8),
Expanded(
child: _buildTextField(
label: 'Car Plate',
controller: controller.carPlateController,
),
),
],
),
// _buildTextField(
// label: 'VIN',
// controller: controller.vinController,
// icon: Icons.confirmation_number),
Row(
children: [
Expanded(
child: _buildTextField(
label: 'License Issue Date',
controller: controller.licenseIssueDateController,
onTap: () => controller.selectDate(
context, controller.licenseIssueDateController))),
const SizedBox(width: 8),
Expanded(
child: _buildTextField(
label: 'License Expiry Date',
controller: controller.carLicenseExpiryDateController,
onTap: () => controller.selectDate(
context, controller.carLicenseExpiryDateController))),
],
),
],
);
}
// --- PAGE 4: Car License Back ---
// <-- MODIFICADO: Usa RegisterCaptainServiceController -->
Widget _buildSyrianCarLicenseBack(
RegisterCaptainServiceController controller) {
return _buildPageContent(
imageUrl: controller.docUrls['car_license_back']!,
formFields: [
Row(
children: [
Expanded(
child: _buildTextField(
label: 'Make',
controller: controller.makeController,
)),
const SizedBox(width: 8),
Expanded(
child: _buildTextField(
label: 'Model',
controller: controller.modelController,
)),
],
),
Row(
children: [
Expanded(
child: _buildTextField(
label: 'Year',
controller: controller.yearController,
keyboardType: TextInputType.number)),
const SizedBox(width: 8),
Expanded(child: _buildFuelDropdown(controller)),
],
),
],
);
}
}
class StepIndicator extends StatelessWidget {
final int currentStep;
final int totalSteps;
const StepIndicator({
super.key,
required this.currentStep,
required this.totalSteps,
});
@override
Widget build(BuildContext context) {
return Column(
children: [
Text(
'${'Step'.tr} ${currentStep + 1} ${'of'.tr} $totalSteps',
style: const TextStyle(
fontSize: 18, fontWeight: FontWeight.bold, color: Colors.black54),
),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(totalSteps, (index) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 4),
width: 30,
height: 8,
decoration: BoxDecoration(
color: index <= currentStep
? AppColor.primaryColor
: Colors.grey.shade300,
borderRadius: BorderRadius.circular(4),
),
);
}),
),
],
);
}
}
@@ -0,0 +1,79 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_service/controller/mainController/main_controller.dart';
import 'package:siro_service/views/widgets/my_scafold.dart';
import '../../functions/encrypt_decrypt.dart';
class PassengersCantRegister extends StatelessWidget {
PassengersCantRegister({super.key});
@override
Widget build(BuildContext context) {
Get.put(MainController());
return MyScaffold(
title: 'Passengers Cant Register'.tr,
isleading: true,
body: [
GetBuilder<MainController>(builder: (mainController) {
return ListView.builder(
itemCount: mainController.passengerNotCompleteRegistration.length,
itemBuilder: (context, index) {
final passenger =
mainController.passengerNotCompleteRegistration[index];
return Padding(
padding: const EdgeInsets.all(8.0),
child: CupertinoFormSection(
header: Text('${'Passenger ID'.tr}: ${passenger['id']}'),
children: [
InkWell(
onTap: () =>
mainController.makePhoneCall(passenger['phone']),
child: CupertinoFormRow(
prefix: Text(' phone'.tr),
child: CupertinoTextFormFieldRow(
initialValue: ((passenger['phone'])),
readOnly: true,
placeholder: 'Phone Number'.tr,
),
),
),
CupertinoFormRow(
prefix: Text('Created At'.tr),
child: CupertinoTextFormFieldRow(
initialValue: passenger['created_at'],
readOnly: true,
placeholder: 'Created At'.tr,
),
),
CupertinoFormRow(
prefix: Text('Notes'.tr),
child: CupertinoTextFormFieldRow(
controller: mainController.notesController,
placeholder:
passenger['note'] ?? 'Enter notes after call'.tr,
),
),
CupertinoButton(
child: Text('Save Notes'.tr),
onPressed: () {
// Save the notes for the Passenger
String notes = mainController.notesController.text;
mainController
.saveNoteForPassengerNotCompleteRegistration(
passenger['phone_number'], 'girls name', notes);
print('Notes for Passenger ${passenger['id']}: $notes');
},
),
],
),
);
},
);
}),
],
);
}
}
@@ -0,0 +1,351 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_service/constant/colors.dart';
import 'package:siro_service/controller/mainController/main_controller.dart';
import 'package:siro_service/views/widgets/my_scafold.dart';
class PassengersPage extends StatelessWidget {
PassengersPage({super.key});
final MainController mainController = Get.find<MainController>();
@override
Widget build(BuildContext context) {
return GetBuilder<MainController>(
builder: (controller) {
final data = _extractPassengerData(controller);
return MyScaffold(
title: data != null
? '${data['first_name'] ?? ''} ${data['last_name'] ?? ''}'
: 'Passenger Not Found'.tr,
isleading: true,
body: [
if (data != null)
Container(
color: AppColor.surfaceColor,
child: ListView(
physics: const BouncingScrollPhysics(),
padding: const EdgeInsets.symmetric(vertical: 12),
children: [
_buildPersonalInfoCard(data),
_buildCommunicationCard(data),
_buildLatestRideCard(data),
_buildWalletInfoCard(data),
const SizedBox(height: 40),
],
),
)
else
Container(
color: AppColor.surfaceColor,
child: Center(
child: Padding(
padding: const EdgeInsets.all(24.0),
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(
Icons.person_off_rounded,
size: 64,
color: AppColor.greyColor.withOpacity(0.5),
),
const SizedBox(height: 16),
Text(
'No passenger data available.'.tr,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w600,
color: AppColor.writeColor,
),
textAlign: TextAlign.center,
),
],
),
),
),
),
],
);
},
);
}
/// Helper method to extract the passenger data safely.
Map? _extractPassengerData(MainController controller) {
final passengerData = controller.passengerData;
if (passengerData == null ||
passengerData['message'] == null ||
passengerData['message'].isEmpty) {
return null;
}
return passengerData['message'][0];
}
Widget _buildPersonalInfoCard(Map data) {
return _buildCard(
title: 'Personal Information'.tr,
icon: Icons.person_outline_rounded,
iconColor: AppColor.primaryColor,
children: [
_buildInfoRow('Phone'.tr, data['phone']),
_buildInfoRow('Email'.tr, data['email']),
_buildInfoRow('Gender'.tr, data['gender']),
_buildInfoRow('Birthdate'.tr, data['birthdate']),
_buildInfoRow('Employment'.tr, data['employmentType']),
_buildInfoRow('Marital Status'.tr, data['maritalStatus'], isLast: true),
],
);
}
Widget _buildCommunicationCard(Map data) {
String phone = data['phone'] ?? '';
String name = data['first_name'] ?? '';
return _buildCard(
title: 'Quick Communication'.tr,
icon: Icons.chat_bubble_outline_rounded,
iconColor: Colors.teal,
children: [
_buildCommunicationRow(
title: 'Call Passenger'.tr,
subtitle: phone,
icon: Icons.phone_rounded,
color: Colors.green,
onTap: () => mainController.makePhoneCall(phone),
),
_buildCommunicationRow(
title: 'WhatsApp: Support'.tr,
subtitle: 'Send technical support message'.tr,
icon: Icons.send_rounded,
color: Colors.blue,
onTap: () => mainController.launchCommunication(
'whatsapp',
phone,
'مرحباً $name، معك الدعم الفني من شركة سيرو. كيف يمكنني مساعدتك اليوم؟',
),
isLast: true,
),
],
);
}
Widget _buildLatestRideCard(Map data) {
return _buildCard(
title: 'Latest Ride'.tr,
icon: Icons.directions_car_outlined,
iconColor: Colors.orange,
children: [
_buildInfoRow('Ride ID'.tr, data['ride_id']),
_buildInfoRow('Date'.tr, data['ride_date']),
_buildInfoRow('Start Time'.tr, data['ride_time']),
_buildInfoRow('End Time'.tr, data['ride_endtime']),
_buildInfoRow(
'Price'.tr, data['price'] != null ? '\$${data['price']}' : null),
_buildInfoRow('Status'.tr, data['ride_status']),
_buildInfoRow('Payment Method'.tr, data['ride_payment_method']),
_buildInfoRow('Car Type'.tr, data['car_type']),
_buildInfoRow(
'Distance'.tr,
data['distance'] != null
? '${(data['distance'] as num).toStringAsFixed(2)} km'
: null,
isLast: true),
],
);
}
Widget _buildWalletInfoCard(Map data) {
return _buildCard(
title: 'Wallet Information'.tr,
icon: Icons.account_balance_wallet_outlined,
iconColor: Colors.green,
children: [
_buildInfoRow(
'Wallet Balance'.tr,
data['passenger_wallet_balance'] != null
? '\$${data['passenger_wallet_balance']}'
: null),
_buildInfoRow(
'Last Payment Amount'.tr,
data['passenger_payment_amount'] != null
? '\$${data['passenger_payment_amount']}'
: null),
_buildInfoRow(
'Last Payment Method'.tr, data['passenger_payment_method'],
isLast: true),
],
);
}
Widget _buildCard({
required String title,
required IconData icon,
required Color iconColor,
required List<Widget> children,
}) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(20),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.03),
blurRadius: 16,
offset: const Offset(0, 6),
),
],
border: Border.all(
color: Colors.grey.shade100,
width: 1,
),
),
child: Padding(
padding: const EdgeInsets.all(20),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Row(
children: [
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
color: iconColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(10),
),
child: Icon(
icon,
color: iconColor,
size: 20,
),
),
const SizedBox(width: 12),
Text(
title,
style: const TextStyle(
fontSize: 18,
fontWeight: FontWeight.w700,
color: AppColor.writeColor,
letterSpacing: 0.2,
),
),
],
),
const SizedBox(height: 16),
...children,
],
),
),
);
}
Widget _buildInfoRow(String label, dynamic value, {bool isLast = false}) {
final displayValue = value?.toString() ?? 'N/A';
return Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 12),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
label,
style: const TextStyle(
fontSize: 15,
fontWeight: FontWeight.w500,
color: AppColor.accentColor,
),
),
const SizedBox(width: 16),
Flexible(
child: Text(
displayValue,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColor.writeColor,
),
textAlign: TextAlign.end,
overflow: TextOverflow.ellipsis,
),
),
],
),
),
if (!isLast)
Divider(
height: 1,
thickness: 1,
color: Colors.grey.shade50,
),
],
);
}
Widget _buildCommunicationRow({
required String title,
required String subtitle,
required IconData icon,
required Color color,
required VoidCallback onTap,
bool isLast = false,
}) {
return Column(
children: [
InkWell(
onTap: onTap,
borderRadius: BorderRadius.circular(12),
child: Padding(
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 4),
child: Row(
children: [
Container(
padding: const EdgeInsets.all(10),
decoration: BoxDecoration(
color: color.withOpacity(0.1),
shape: BoxShape.circle,
),
child: Icon(icon, color: color, size: 20),
),
const SizedBox(width: 14),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
title,
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.w600,
color: AppColor.writeColor,
),
),
const SizedBox(height: 2),
Text(
subtitle,
style: const TextStyle(
fontSize: 13,
color: AppColor.accentColor,
),
),
],
),
),
Icon(
Icons.arrow_forward_ios_rounded,
color: Colors.grey.shade300,
size: 16,
),
],
),
),
),
if (!isLast)
Divider(
height: 1,
thickness: 1,
color: Colors.grey.shade50,
),
],
);
}
}
@@ -0,0 +1,458 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_service/constant/colors.dart';
import 'package:siro_service/views/widgets/elevated_btn.dart';
import 'package:siro_service/views/widgets/my_scafold.dart';
import '../registration_captain_controller.dart';
class RegisterCaptain extends StatelessWidget {
const RegisterCaptain({super.key});
@override
Widget build(BuildContext context) {
final controller = Get.put(RegisterCaptainController());
return MyScaffold(
title: 'Syrian Documents Check'.tr,
isleading: true,
body: [
Obx(() {
if (controller.isLoading.value) {
return const Center(child: CircularProgressIndicator());
}
return Column(
children: [
Padding(
padding: const EdgeInsets.symmetric(vertical: 16.0),
child: StepIndicator(
currentStep: controller.currentPageIndex.value,
totalSteps: 4,
),
),
Expanded(
child: PageView(
controller: controller.pageController,
onPageChanged: (index) {
controller.currentPageIndex.value = index;
},
children: [
_buildSyrianDriverLicenseFront(context, controller),
_buildSyrianDriverLicenseBack(context, controller),
_buildSyrianCarLicenseFront(context, controller),
_buildSyrianCarLicenseBack(controller),
],
),
),
_buildNavigationControls(controller),
],
);
}),
],
);
}
Widget _buildNavigationControls(RegisterCaptainController controller) {
bool isLastPage = controller.currentPageIndex.value == 3;
bool isFirstPage = controller.currentPageIndex.value == 0;
return Padding(
padding: const EdgeInsets.all(16.0),
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
if (!isFirstPage)
MyElevatedButton(
title: 'Back'.tr,
onPressed: controller.previousPage,
kolor: Colors.grey,
),
if (isFirstPage) const Spacer(),
MyElevatedButton(
title: isLastPage ? 'Save and Activate'.tr : 'Next'.tr,
onPressed: isLastPage
? controller.updateAndActivateSyrianDriver
: controller.nextPage,
kolor: isLastPage ? AppColor.greenColor : AppColor.primaryColor,
),
],
),
);
}
Widget _buildTextField({
required String label,
required TextEditingController controller,
IconData? icon,
TextInputType keyboardType = TextInputType.text,
VoidCallback? onTap,
}) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: TextFormField(
controller: controller,
keyboardType: keyboardType,
readOnly: onTap != null,
onTap: onTap,
decoration: InputDecoration(
labelText: label.tr,
prefixIcon:
icon != null ? Icon(icon, color: AppColor.secondaryColor) : null,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
focusedBorder: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
borderSide:
const BorderSide(color: AppColor.secondaryColor, width: 2),
),
),
),
);
}
Widget _buildPageContent({
required RxString imageUrl,
required List<Widget> formFields,
}) {
return SingleChildScrollView(
padding: const EdgeInsets.symmetric(horizontal: 16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Image.network(
imageUrl.value,
fit: BoxFit.fitWidth,
errorBuilder: (context, error, stackTrace) {
return Container(
height: 200,
color: Colors.grey[200],
child: Center(
child: Text(
'Image not available'.tr,
style: const TextStyle(color: Colors.grey),
),
),
);
},
),
),
const SizedBox(height: 20),
const Divider(),
...formFields,
],
),
);
}
Widget _buildColorDropdown(RegisterCaptainController controller) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Obx(
() => DropdownButtonFormField<String>(
value: controller.colorHex.value.isEmpty
? null
: controller.colorHex.value,
isExpanded: true,
decoration: InputDecoration(
labelText: 'Car Color'.tr,
prefixIcon: Icon(Icons.color_lens, color: AppColor.secondaryColor),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
items: RegisterCaptainController.kCarColorOptions.map((opt) {
final hex = opt['hex']!;
final key = opt['key']!;
return DropdownMenuItem<String>(
value: hex,
child: Row(
children: [
Container(
width: 18,
height: 18,
decoration: BoxDecoration(
color: controller.hexToColor(hex),
shape: BoxShape.circle,
border: Border.all(color: Colors.black12),
),
),
const SizedBox(width: 12),
Expanded(child: Text(key.tr)),
],
),
);
}).toList(),
onChanged: (hex) {
if (hex != null) {
controller.updateColorSelection(hex);
}
},
),
),
);
}
Widget _buildGenderDropdown(RegisterCaptainController controller) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Obx(
() => DropdownButtonFormField<String>(
value: controller.selectedGender.value.isEmpty
? null
: controller.selectedGender.value,
isExpanded: true,
decoration: InputDecoration(
labelText: 'Gender'.tr,
prefixIcon: Icon(Icons.wc, color: AppColor.secondaryColor),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
items: ['Male', 'Female'].map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value.tr),
);
}).toList(),
onChanged: (String? newValue) {
if (newValue != null) {
controller.selectedGender.value = newValue;
}
},
),
),
);
}
Widget _buildFuelDropdown(RegisterCaptainController controller) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 8.0),
child: Obx(
() => DropdownButtonFormField<String>(
value: RegisterCaptainController.kFuelOptions
.contains(controller.selectedFuel.value)
? controller.selectedFuel.value
: null,
isExpanded: true,
decoration: InputDecoration(
labelText: 'Fuel Type'.tr,
prefixIcon:
Icon(Icons.local_gas_station, color: AppColor.secondaryColor),
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
),
items: RegisterCaptainController.kFuelOptions.map((String value) {
return DropdownMenuItem<String>(
value: value,
child: Text(value.tr),
);
}).toList(),
onChanged: (String? newValue) {
if (newValue != null) {
controller.selectedFuel.value = newValue;
}
},
),
),
);
}
// --- PAGE 1: Driver License Front ---
Widget _buildSyrianDriverLicenseFront(
BuildContext context, RegisterCaptainController controller) {
return _buildPageContent(
imageUrl: controller.docUrls['driver_license_front']!,
formFields: [
Row(
children: [
Expanded(
child: _buildTextField(
label: 'First Name',
controller: controller.firstNameController,
keyboardType: TextInputType.name),
),
const SizedBox(width: 8),
Expanded(
child: _buildTextField(
label: 'Last Name',
controller: controller.lastNameController,
keyboardType: TextInputType.name),
),
],
),
_buildTextField(
label: 'Place of Registration',
controller: controller.siteController,
icon: Icons.location_city),
Row(
children: [
Expanded(
child: _buildTextField(
label: 'National Number',
controller: controller.nationalNumberController,
keyboardType: TextInputType.number,
icon: Icons.fingerprint),
),
const SizedBox(width: 8),
Expanded(child: _buildGenderDropdown(controller)),
],
),
// _buildTextField(
// label: 'Birthdate',
// controller: controller.birthdateController,
// icon: Icons.cake,
// onTap: () =>
// controller.selectDate(context, controller.birthdateController)),
],
);
}
// --- PAGE 2: Driver License Back ---
Widget _buildSyrianDriverLicenseBack(
BuildContext context, RegisterCaptainController controller) {
return _buildPageContent(
imageUrl: controller.docUrls['driver_license_back']!,
formFields: [
_buildTextField(
label: 'License Category',
controller: controller.licenseCategoriesController),
_buildTextField(
label: 'Expiry Date',
controller: controller.expiryDateController,
icon: Icons.event_busy,
onTap: () => controller.selectDate(
context, controller.expiryDateController)),
],
);
}
// --- PAGE 3: Car License Front ---
Widget _buildSyrianCarLicenseFront(
BuildContext context, RegisterCaptainController controller) {
return _buildPageContent(
imageUrl: controller.docUrls['car_license_front']!,
formFields: [
_buildTextField(
label: 'Owner Name',
controller: controller.ownerController,
keyboardType: TextInputType.name,
icon: Icons.person_search),
Row(
children: [
Expanded(child: _buildColorDropdown(controller)),
const SizedBox(width: 8),
Expanded(
child: _buildTextField(
label: 'Car Plate',
controller: controller.carPlateController,
),
),
],
),
// _buildTextField(
// label: 'VIN',
// controller: controller.vinController,
// icon: Icons.confirmation_number),
Row(
children: [
Expanded(
child: _buildTextField(
label: 'License Issue Date',
controller: controller.licenseIssueDateController,
onTap: () => controller.selectDate(
context, controller.licenseIssueDateController))),
const SizedBox(width: 8),
Expanded(
child: _buildTextField(
label: 'License Expiry Date',
controller: controller.carLicenseExpiryDateController,
onTap: () => controller.selectDate(
context, controller.carLicenseExpiryDateController))),
],
),
],
);
}
// --- PAGE 4: Car License Back ---
Widget _buildSyrianCarLicenseBack(RegisterCaptainController controller) {
return _buildPageContent(
imageUrl: controller.docUrls['car_license_back']!,
formFields: [
Row(
children: [
Expanded(
child: _buildTextField(
label: 'Make',
controller: controller.makeController,
)),
const SizedBox(width: 8),
Expanded(
child: _buildTextField(
label: 'Model',
controller: controller.modelController,
)),
],
),
Row(
children: [
Expanded(
child: _buildTextField(
label: 'Year',
controller: controller.yearController,
keyboardType: TextInputType.number)),
const SizedBox(width: 8),
Expanded(child: _buildFuelDropdown(controller)),
],
),
],
);
}
}
class StepIndicator extends StatelessWidget {
final int currentStep;
final int totalSteps;
const StepIndicator({
super.key,
required this.currentStep,
required this.totalSteps,
});
@override
Widget build(BuildContext context) {
return Column(
children: [
Text(
'${'Step'.tr} ${currentStep + 1} ${'of'.tr} $totalSteps',
style: const TextStyle(
fontSize: 18, fontWeight: FontWeight.bold, color: Colors.black54),
),
const SizedBox(height: 8),
Row(
mainAxisAlignment: MainAxisAlignment.center,
children: List.generate(totalSteps, (index) {
return Container(
margin: const EdgeInsets.symmetric(horizontal: 4),
width: 30,
height: 8,
decoration: BoxDecoration(
color: index <= currentStep
? AppColor.primaryColor
: Colors.grey.shade300,
borderRadius: BorderRadius.circular(4),
),
);
}),
),
],
);
}
}
@@ -0,0 +1,544 @@
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_service/constant/colors.dart';
import 'package:siro_service/controller/mainController/review_driver_controller.dart';
import 'package:siro_service/views/widgets/elevated_btn.dart';
import 'package:siro_service/views/widgets/my_scafold.dart';
class ReviewDriverPage extends StatelessWidget {
const ReviewDriverPage({super.key});
@override
Widget build(BuildContext context) {
final controller = Get.put(ReviewDriverController());
return MyScaffold(
title: 'Review Driver'.tr,
isleading: true,
body: [
Obx(() {
if (controller.isLoading.value) {
return const Center(child: CircularProgressIndicator());
}
return Column(
children: [
_buildTabBar(controller),
Expanded(child: _buildTabBarView(controller)),
_buildBottomActions(controller, context),
],
);
}),
],
);
}
Widget _buildTabBar(ReviewDriverController c) {
final keys = c.docUrls.keys.toList();
return SizedBox(
height: 64,
child: ListView.builder(
scrollDirection: Axis.horizontal,
padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 6),
itemCount: keys.length,
itemBuilder: (context, index) {
final key = keys[index];
return Obx(() {
final isSelected = c.currentTabIndex.value == index;
return GestureDetector(
onTap: () => c.currentTabIndex.value = index,
child: Container(
margin: const EdgeInsets.symmetric(horizontal: 3),
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 6),
decoration: BoxDecoration(
color: isSelected
? AppColor.primaryColor
: AppColor.primaryLight,
borderRadius: BorderRadius.circular(20),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(
c.tabIcons[key] ?? Icons.image,
size: 16,
color: isSelected ? Colors.white : AppColor.primaryColor,
),
const SizedBox(width: 4),
Text(
(c.tabLabels[key] ?? key).tr,
style: TextStyle(
fontSize: 11,
fontWeight:
isSelected ? FontWeight.bold : FontWeight.normal,
color:
isSelected ? Colors.white : AppColor.primaryColor,
),
),
],
),
),
);
});
},
),
);
}
Widget _buildTabBarView(ReviewDriverController c) {
return Obx(() {
final index = c.currentTabIndex.value;
final keys = c.docUrls.keys.toList();
final key = keys[index];
return SingleChildScrollView(
padding: const EdgeInsets.all(12),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
// Country indicator badge
Container(
margin: const EdgeInsets.only(bottom: 8),
padding: const EdgeInsets.symmetric(horizontal: 12, vertical: 6),
decoration: BoxDecoration(
color: AppColor.primaryLight,
borderRadius: BorderRadius.circular(20),
),
child: Row(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.public, size: 16, color: AppColor.primaryColor),
const SizedBox(width: 6),
Text(
c.country.isNotEmpty
? '${'Country'.tr}: ${c.country.tr}'
: 'Detecting country...'.tr,
style: TextStyle(
fontSize: 13,
fontWeight: FontWeight.w600,
color: AppColor.primaryColor,
),
),
],
),
),
_buildDocumentImage(c.docUrls[key]!.value),
const SizedBox(height: 16),
_buildFormFieldsForTab(c, key),
],
),
);
});
}
Widget _buildDocumentImage(String url) {
if (url.isEmpty) {
return Container(
height: 180,
decoration: BoxDecoration(
color: Colors.grey[100],
borderRadius: BorderRadius.circular(12),
),
child: Center(
child: Column(
mainAxisSize: MainAxisSize.min,
children: [
Icon(Icons.image_not_supported, size: 48, color: Colors.grey[400]),
const SizedBox(height: 8),
Text('No image available'.tr, style: TextStyle(color: Colors.grey[500])),
],
),
),
);
}
return GestureDetector(
onTap: () => _showImageFullscreen(url),
child: ClipRRect(
borderRadius: BorderRadius.circular(12),
child: Image.network(
url,
height: 200,
fit: BoxFit.contain,
width: double.infinity,
errorBuilder: (_, __, ___) => Container(
height: 180,
color: Colors.grey[200],
child: Center(
child: Text('Failed to load image'.tr,
style: TextStyle(color: Colors.grey[500])),
),
),
),
),
);
}
void _showImageFullscreen(String url) {
Get.dialog(
Scaffold(
backgroundColor: Colors.black,
appBar: AppBar(
backgroundColor: Colors.black,
iconTheme: const IconThemeData(color: Colors.white),
),
body: Center(
child: InteractiveViewer(
child: Image.network(url, fit: BoxFit.contain),
),
),
),
);
}
Widget _buildFormFieldsForTab(ReviewDriverController c, String tabKey) {
final fields = c.getFieldsForTab(tabKey);
// Special layouts for specific tabs
if (tabKey == 'criminal_record') {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('${c.country} - ${'Criminal Record'.tr}',
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
const Divider(),
Card(
color: Colors.green[50],
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Icon(Icons.check_circle, color: Colors.green[700], size: 32),
const SizedBox(width: 12),
Expanded(
child: Text(
_getCriminalRecordHint(c.country),
style: TextStyle(color: Colors.green[800]),
),
),
],
),
),
),
...fields.map((f) => _buildFieldFromConfig(c, f)),
],
);
}
if (tabKey == 'profile_picture') {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text('Profile Photo'.tr,
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
const Divider(),
Card(
color: Colors.blue[50],
child: Padding(
padding: const EdgeInsets.all(16),
child: Row(
children: [
Icon(Icons.face, color: Colors.blue[700], size: 32),
const SizedBox(width: 12),
Expanded(
child: Text(
'Verify the profile photo matches the person in the ID '
'and Driver License photos above.'.tr,
style: TextStyle(color: Colors.blue[800]),
),
),
],
),
),
),
const SizedBox(height: 12),
...fields.map((f) => _buildFieldFromConfig(c, f)),
],
);
}
// Dynamic fields for all other tabs
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${c.country} - ${(c.tabLabels[tabKey] ?? tabKey).tr}',
style: const TextStyle(fontSize: 18, fontWeight: FontWeight.bold),
),
const Divider(),
...fields.map((f) => _buildFieldFromConfig(c, f)),
],
);
}
String _getCriminalRecordHint(String country) {
switch (country) {
case 'Syria':
return 'Review the "لا حكم عليه" document. Verify name matches driver.'.tr;
case 'Jordan':
return 'Review the "عدم محكومية" document. Verify name matches driver.'.tr;
case 'Egypt':
return 'Review the "فيش وتشبيه" document. Verify name matches driver.'.tr;
default:
return 'Review the criminal record document. Verify name matches driver.'.tr;
}
}
Widget _buildFieldFromConfig(ReviewDriverController c, List<dynamic> fieldDef) {
if (fieldDef.length < 6) return const SizedBox();
final key = fieldDef[0] as String;
final label = fieldDef[1] as String;
final isDate = fieldDef[2] as bool;
final isGender = fieldDef[3] as bool;
final isColor = fieldDef[4] as bool;
final isFuel = fieldDef[5] as bool;
if (isGender) return _buildGenderDropdown(c, label);
if (isColor) return _buildColorDropdown(c, label);
if (isFuel) return _buildFuelDropdown(c, label);
final controller = _getController(c, key);
if (controller == null) return const SizedBox();
if (isDate) {
return _buildTextField(
label: label,
controller: controller,
icon: Icons.event,
onTap: () => c.selectDate(Get.context!, controller),
);
}
// Determine icon based on common field names
final icon = _getFieldIcon(key, label);
return _buildTextField(
label: label,
controller: controller,
icon: icon,
);
}
IconData? _getFieldIcon(String key, String label) {
final l = label.toLowerCase();
if (l.contains('national')) return Icons.fingerprint;
if (l.contains('phone')) return Icons.phone;
if (l.contains('email')) return Icons.email;
if (l.contains('address')) return Icons.location_on;
if (l.contains('birth')) return Icons.cake;
if (l.contains('license') || l.contains('category')) return Icons.card_membership;
if (l.contains('plate')) return Icons.confirmation_number;
if (l.contains('owner')) return Icons.person_search;
if (l.contains('make') || l.contains('model')) return Icons.directions_car;
if (l.contains('year')) return Icons.calendar_today;
if (l.contains('vin')) return Icons.confirmation_number;
if (l.contains('place')) return Icons.location_city;
if (l.contains('marital')) return Icons.people;
if (l.contains('first name') || l.contains('last name')) return Icons.person;
return null;
}
TextEditingController? _getController(ReviewDriverController c, String key) {
switch (key) {
case 'firstNameController': return c.firstNameController;
case 'lastNameController': return c.lastNameController;
case 'phoneController': return c.phoneController;
case 'emailController': return c.emailController;
case 'siteController': return c.siteController;
case 'nationalNumberController': return c.nationalNumberController;
case 'birthdateController': return c.birthdateController;
case 'addressController': return c.addressController;
case 'licenseCategoriesController': return c.licenseCategoriesController;
case 'licenseTypeController': return c.licenseTypeController;
case 'expiryDateController': return c.expiryDateController;
case 'licenseIssueDateController': return c.licenseIssueDateController;
case 'ownerController': return c.ownerController;
case 'carPlateController': return c.carPlateController;
case 'vinController': return c.vinController;
case 'carLicenseExpiryDateController': return c.carLicenseExpiryDateController;
case 'makeController': return c.makeController;
case 'modelController': return c.modelController;
case 'yearController': return c.yearController;
case 'maritalStatusController': return c.maritalStatusController;
default: return null;
}
}
Widget _buildTextField({
required String label,
required TextEditingController controller,
IconData? icon,
TextInputType keyboardType = TextInputType.text,
VoidCallback? onTap,
String? hint,
int maxLines = 1,
}) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: TextFormField(
controller: controller,
keyboardType: keyboardType,
maxLines: maxLines,
readOnly: onTap != null,
onTap: onTap,
decoration: InputDecoration(
labelText: label.tr,
hintText: hint,
prefixIcon: icon != null
? Icon(icon, color: AppColor.secondaryColor)
: null,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
contentPadding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 14),
),
),
);
}
Widget _buildGenderDropdown(ReviewDriverController c, String label) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: Obx(
() => DropdownButtonFormField<String>(
value: c.selectedGender.value.isEmpty
? null
: c.selectedGender.value,
isExpanded: true,
decoration: InputDecoration(
labelText: label.tr,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
contentPadding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 14),
),
items: ['Male', 'Female'].map((v) {
return DropdownMenuItem(value: v, child: Text(v.tr));
}).toList(),
onChanged: (v) {
if (v != null) c.selectedGender.value = v;
},
),
),
);
}
Widget _buildColorDropdown(ReviewDriverController c, String label) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: Obx(
() => DropdownButtonFormField<String>(
value: c.colorHex.value.isEmpty ? null : c.colorHex.value,
isExpanded: true,
decoration: InputDecoration(
labelText: label.tr,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
contentPadding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 14),
),
items: ReviewDriverController.kCarColorOptions.map((opt) {
return DropdownMenuItem<String>(
value: opt['hex'],
child: Row(
children: [
Container(
width: 20,
height: 20,
decoration: BoxDecoration(
color: c.hexToColor(opt['hex']!),
shape: BoxShape.circle,
border: Border.all(color: Colors.black12),
),
),
const SizedBox(width: 10),
Text(opt['key']!.tr),
],
),
);
}).toList(),
onChanged: (hex) {
if (hex != null) c.updateColorSelection(hex);
},
),
),
);
}
Widget _buildFuelDropdown(ReviewDriverController c, String label) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 6),
child: Obx(
() => DropdownButtonFormField<String>(
value: ReviewDriverController.kFuelOptions
.contains(c.selectedFuel.value)
? c.selectedFuel.value
: null,
isExpanded: true,
decoration: InputDecoration(
labelText: label.tr,
border: OutlineInputBorder(
borderRadius: BorderRadius.circular(10),
),
contentPadding:
const EdgeInsets.symmetric(horizontal: 12, vertical: 14),
),
items: ReviewDriverController.kFuelOptions.map((v) {
return DropdownMenuItem(value: v, child: Text(v.tr));
}).toList(),
onChanged: (v) {
if (v != null) c.selectedFuel.value = v;
},
),
),
);
}
Widget _buildBottomActions(ReviewDriverController c, BuildContext context) {
return Obx(() {
final saving = c.isSaving.value;
return Container(
padding: const EdgeInsets.all(12),
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black.withValues(alpha: 0.05),
blurRadius: 10,
offset: const Offset(0, -4),
),
],
),
child: Row(
children: [
Expanded(
child: MyElevatedButton(
title: 'Save'.tr,
onPressed: () { c.saveChanges(); },
kolor: AppColor.blueColor,
loading: saving,
),
),
const SizedBox(width: 8),
Expanded(
child: MyElevatedButton(
title: 'Activate'.tr,
onPressed: () { c.activateDriver(); },
kolor: AppColor.greenColor,
loading: saving,
),
),
const SizedBox(width: 8),
Expanded(
child: MyElevatedButton(
title: 'Reject'.tr,
onPressed: () { c.showRejectDialog(); },
kolor: AppColor.redColor,
loading: saving,
),
),
],
),
);
});
}
}
@@ -0,0 +1,517 @@
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_service/views/widgets/mycircular.dart';
import 'package:siro_service/constant/links.dart';
import 'package:siro_service/controller/functions/crud.dart';
import 'package:siro_service/views/widgets/my_scafold.dart';
/// --------------------------------------------------------------------------
/// نموذج بيانات الرحلة
/// --------------------------------------------------------------------------
class ActiveRideModel {
final String rideId;
final String status;
final String startLocation;
final String endLocation;
final String driverName;
final String driverPhone;
final String passengerName;
final String passengerPhone;
final String price;
final String carType;
final String date;
ActiveRideModel({
required this.rideId,
required this.status,
required this.startLocation,
required this.endLocation,
required this.driverName,
required this.driverPhone,
required this.passengerName,
required this.passengerPhone,
required this.price,
required this.carType,
required this.date,
});
factory ActiveRideModel.fromJson(Map<String, dynamic> json) {
final ride = json['ride_details'] ?? json;
final driver = json['driver_details'] ?? {};
return ActiveRideModel(
rideId: ride['id'].toString(),
status: ride['status'] ?? '',
startLocation: ride['start_location'] ?? '',
endLocation: ride['end_location'] ?? '',
driverName: driver['fullname'] ?? driver['first_name'] ?? 'Unknown'.tr,
driverPhone: driver['phone'] ?? '',
passengerName: '',
passengerPhone: '',
price: ride['price']?.toString() ?? '0',
carType: ride['carType'] ?? '',
date: ride['date'] ?? '',
);
}
}
/// --------------------------------------------------------------------------
/// الـ Controller
/// --------------------------------------------------------------------------
class RideMonitorServiceController extends GetxController {
final TextEditingController phoneCtrl = TextEditingController();
final TextEditingController rideIdCtrl = TextEditingController();
var isLoading = false.obs;
var hasResult = false.obs;
var hasError = false.obs;
var errorMessage = ''.obs;
var activeRides = <ActiveRideModel>[].obs;
var searchMode = 'phone'.obs; // 'phone' or 'ride_id'
final String monitorApiUrl = "${AppLink.server}/Admin/rides/monitorRide.php";
final String searchApiUrl =
"${AppLink.server}/Admin/rides/admin_get_rides_by_phone.php";
@override
void onClose() {
phoneCtrl.dispose();
rideIdCtrl.dispose();
super.onClose();
}
/// البحث برقم هاتف الراكب أو السائق
Future<void> searchByPhone() async {
final phone = phoneCtrl.text.trim();
if (phone.isEmpty) {
mySnackbarWarning('يرجى إدخال رقم الهاتف'.tr);
return;
}
isLoading.value = true;
hasError.value = false;
hasResult.value = false;
activeRides.clear();
try {
final normalizedPhone = CRUD.normalizePhone(phone);
// محاولة البحث أولاً عن رحلة نشطة
var res = await CRUD().post(
link: monitorApiUrl,
payload: {'phone': normalizedPhone},
);
if (res != 'failure' && res is Map && res['status'] == 'success') {
var data = (res['message'] ?? res['data'] ?? res) as Map;
if (data['ride_details'] != null) {
activeRides
.add(ActiveRideModel.fromJson(Map<String, dynamic>.from(data)));
hasResult.value = true;
isLoading.value = false;
return;
}
}
// إذا لم نجد رحلة نشطة، نبحث عن آخر الرحلات
var res2 = await CRUD().post(
link: searchApiUrl,
payload: {'phone': normalizedPhone},
);
if (res2 != 'failure' && res2 is Map && res2['status'] == 'success') {
var data = (res2['message'] ?? res2['data'] ?? res2) as Map;
final rides = data['rides'] as List?;
if (rides != null && rides.isNotEmpty) {
for (var ride in rides) {
final rideMap = ride as Map;
activeRides.add(ActiveRideModel.fromJson({
'ride_details': ride,
'driver_details': {
'fullname': rideMap['driver_first_name'] ?? '',
'phone': rideMap['d_phone'] ?? '',
}
}));
}
hasResult.value = true;
} else {
hasError.value = true;
errorMessage.value = 'No rides found for this number'.tr;
}
} else {
hasError.value = true;
errorMessage.value = 'User not found'.tr;
}
} catch (e) {
hasError.value = true;
errorMessage.value = '${'Connection error'.tr}: $e';
} finally {
isLoading.value = false;
}
}
/// البحث برقم الرحلة (Ride ID)
Future<void> searchByRideId() async {
final rideId = rideIdCtrl.text.trim();
if (rideId.isEmpty) {
mySnackbarWarning('يرجى إدخال رقم الرحلة'.tr);
return;
}
isLoading.value = true;
hasError.value = false;
hasResult.value = false;
activeRides.clear();
try {
var res = await CRUD().post(
link: "${AppLink.server}/Admin/rides/get_rides_by_status.php",
payload: {'status': 'All'},
);
if (res != 'failure' && res is Map && res['status'] == 'success') {
final rawRides = res['message'];
final rides = (rawRides is List ? rawRides : []) as List;
final found = rides.where((r) {
final rm = r as Map;
return rm['id'].toString() == rideId;
}).toList();
if (found.isNotEmpty) {
for (var ride in found) {
final rideMap = ride as Map;
activeRides.add(ActiveRideModel.fromJson({
'ride_details': ride,
'driver_details': {
'fullname': rideMap['driver_full_name'] ?? '',
'phone': rideMap['d_phone'] ?? '',
}
}));
}
hasResult.value = true;
} else {
hasError.value = true;
errorMessage.value = 'No ride found for this number'.tr;
}
} else {
hasError.value = true;
errorMessage.value = 'Failed to fetch rides'.tr;
}
} catch (e) {
hasError.value = true;
errorMessage.value = '${'Connection error'.tr}: $e';
} finally {
isLoading.value = false;
}
}
}
/// --------------------------------------------------------------------------
/// الشاشة الرئيسية
/// --------------------------------------------------------------------------
class RideMonitorServicePage extends StatelessWidget {
const RideMonitorServicePage({super.key});
@override
Widget build(BuildContext context) {
final controller = Get.put(RideMonitorServiceController());
return MyScaffold(
title: 'Ride monitoring'.tr,
isleading: true,
body: [
Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// 🎯 اختيار طريقة البحث
Obx(() => Row(
children: [
Expanded(
child: _buildModeButton(
'Phone number'.tr,
Icons.phone_rounded,
controller.searchMode.value == 'phone',
() => controller.searchMode.value = 'phone',
),
),
const SizedBox(width: 12),
Expanded(
child: _buildModeButton(
'Trip number'.tr,
Icons.confirmation_number_rounded,
controller.searchMode.value == 'ride_id',
() => controller.searchMode.value = 'ride_id',
),
),
],
)),
const SizedBox(height: 20),
// 🔍 حقل البحث
Obx(() => controller.searchMode.value == 'phone'
? _buildPhoneSearch(controller)
: _buildRideIdSearch(controller)),
const SizedBox(height: 24),
// 📋 النتائج
Obx(() {
if (controller.isLoading.value) {
return const Center(
child: Padding(
padding: EdgeInsets.all(40),
child: CircularProgressIndicator(),
),
);
}
if (controller.hasError.value) {
return Container(
width: double.infinity,
padding: const EdgeInsets.all(20),
decoration: BoxDecoration(
color: Colors.red.shade50,
borderRadius: BorderRadius.circular(12),
border: Border.all(color: Colors.red.shade200),
),
child: Column(
children: [
Icon(Icons.error_outline_rounded,
color: Colors.red, size: 40),
const SizedBox(height: 8),
Text(
controller.errorMessage.value,
textAlign: TextAlign.center,
style: TextStyle(color: Colors.red.shade700),
),
],
),
);
}
if (controller.hasResult.value) {
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'${'Search results'.tr} (${controller.activeRides.length})',
style: const TextStyle(
fontSize: 16,
fontWeight: FontWeight.bold,
),
),
const SizedBox(height: 12),
...controller.activeRides.map(
(ride) => _buildRideCard(ride),
),
],
);
}
return const SizedBox();
}),
],
),
),
],
);
}
Widget _buildModeButton(
String title, IconData icon, bool isActive, VoidCallback onTap) {
return GestureDetector(
onTap: onTap,
child: Container(
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 8),
decoration: BoxDecoration(
color: isActive ? const Color(0xFF4318FF) : Colors.grey.shade200,
borderRadius: BorderRadius.circular(12),
),
child: Row(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Icon(icon,
color: isActive ? Colors.white : Colors.grey.shade600,
size: 18),
const SizedBox(width: 6),
Text(
title,
style: TextStyle(
color: isActive ? Colors.white : Colors.grey.shade600,
fontWeight: FontWeight.bold,
fontSize: 13,
),
),
],
),
),
);
}
Widget _buildPhoneSearch(RideMonitorServiceController controller) {
return Column(
children: [
TextField(
controller: controller.phoneCtrl,
keyboardType: TextInputType.phone,
decoration: InputDecoration(
labelText: 'Phone number'.tr,
hintText: '0992952235 or 079XXXXXXX'.tr,
prefixIcon: const Icon(Icons.phone_rounded),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
),
),
const SizedBox(height: 12),
SizedBox(
width: double.infinity,
height: 48,
child: ElevatedButton.icon(
onPressed: () => controller.searchByPhone(),
icon: const Icon(Icons.search_rounded),
label: Text('Search for trips'.tr,
style: const TextStyle(fontWeight: FontWeight.bold)),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF4318FF),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
),
),
),
],
);
}
Widget _buildRideIdSearch(RideMonitorServiceController controller) {
return Column(
children: [
TextField(
controller: controller.rideIdCtrl,
keyboardType: TextInputType.number,
decoration: InputDecoration(
labelText: 'Trip number'.tr,
hintText: 'Example: 12345'.tr,
prefixIcon: const Icon(Icons.confirmation_number_rounded),
border: OutlineInputBorder(borderRadius: BorderRadius.circular(12)),
),
),
const SizedBox(height: 12),
SizedBox(
width: double.infinity,
height: 48,
child: ElevatedButton.icon(
onPressed: () => controller.searchByRideId(),
icon: const Icon(Icons.search_rounded),
label: Text('Search by trip number'.tr,
style: const TextStyle(fontWeight: FontWeight.bold)),
style: ElevatedButton.styleFrom(
backgroundColor: const Color(0xFF4318FF),
foregroundColor: Colors.white,
shape: RoundedRectangleBorder(
borderRadius: BorderRadius.circular(12)),
),
),
),
],
);
}
Widget _buildRideCard(ActiveRideModel ride) {
Color statusColor;
switch (ride.status) {
case 'Begin':
statusColor = const Color(0xFF10B981);
break;
case 'Apply':
case 'Applied':
statusColor = const Color(0xFF3B82F6);
break;
case 'Finished':
statusColor = const Color(0xFF14B8A6);
break;
default:
statusColor = Colors.grey;
}
return Container(
margin: const EdgeInsets.only(bottom: 12),
decoration: BoxDecoration(
color: Colors.white,
borderRadius: BorderRadius.circular(16),
boxShadow: [
BoxShadow(
color: Colors.black.withOpacity(0.05),
blurRadius: 10,
offset: const Offset(0, 4),
),
],
border: Border.all(color: Colors.grey.withOpacity(0.1)),
),
child: Padding(
padding: const EdgeInsets.all(16),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// Header
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'${'Trip'.tr} #${ride.rideId}',
style: const TextStyle(
fontSize: 15, fontWeight: FontWeight.bold),
),
Container(
padding:
const EdgeInsets.symmetric(horizontal: 8, vertical: 4),
decoration: BoxDecoration(
color: statusColor.withOpacity(0.1),
borderRadius: BorderRadius.circular(8),
),
child: Text(
ride.status,
style: TextStyle(
color: statusColor,
fontSize: 12,
fontWeight: FontWeight.bold),
),
),
],
),
const SizedBox(height: 8),
Text(ride.date,
style: TextStyle(color: Colors.grey[500], fontSize: 12)),
const Divider(height: 20),
// Info rows
_buildInfoRow(Icons.person_rounded, '${'Driver'.tr}: ${ride.driverName}'),
if (ride.driverPhone.isNotEmpty)
_buildInfoRow(
Icons.phone_rounded, '${'Driver phone'.tr}: ${ride.driverPhone}'),
_buildInfoRow(Icons.payments_rounded, '${'Price'.tr}: ${ride.price}'),
if (ride.carType.isNotEmpty)
_buildInfoRow(
Icons.directions_car_rounded, '${'Car type'.tr}: ${ride.carType}'),
],
),
),
);
}
Widget _buildInfoRow(IconData icon, String text) {
return Padding(
padding: const EdgeInsets.symmetric(vertical: 4),
child: Row(
children: [
Icon(icon, size: 16, color: Colors.grey[600]),
const SizedBox(width: 8),
Expanded(
child: Text(text, style: const TextStyle(fontSize: 13)),
),
],
),
);
}
}
@@ -0,0 +1,224 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:siro_service/views/widgets/mycircular.dart';
import 'package:siro_service/constant/colors.dart';
import 'package:siro_service/constant/style.dart';
import 'package:siro_service/views/widgets/elevated_btn.dart';
import 'package:siro_service/views/widgets/my_scafold.dart';
import '../main_controller.dart';
class WelcomeCall extends StatelessWidget {
const WelcomeCall({super.key});
@override
Widget build(BuildContext context) {
final controller = Get.put(MainController());
return MyScaffold(
title: 'Welcome Drivers'.tr,
isleading: true,
body: [
GetBuilder<MainController>(builder: (mainController) {
final drivers = mainController.newDriverRegister;
if (drivers.isEmpty) {
return Padding(
padding: const EdgeInsets.all(32.0),
child: Center(
child: Text(
'No new drivers found.'.tr,
style: const TextStyle(fontSize: 18),
),
),
);
}
return CupertinoScrollbar(
child: ListView.builder(
padding: const EdgeInsets.only(bottom: 20),
itemCount: drivers.length,
itemBuilder: (context, index) {
final driver = drivers[index];
return DriverCard(driver: driver);
},
),
);
}),
],
);
}
}
class DriverCard extends StatelessWidget {
final Map<String, dynamic> driver;
const DriverCard({super.key, required this.driver});
Widget buildActionButton({
required IconData icon,
required Color color,
required VoidCallback onPressed,
}) {
return CupertinoButton(
padding: const EdgeInsets.symmetric(horizontal: 6.0),
onPressed: onPressed,
child: Icon(icon, color: color, size: 28),
);
}
@override
Widget build(BuildContext context) {
final controller = Get.find<MainController>();
return CupertinoCard(
margin: const EdgeInsets.all(16.0),
child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
// حالة التلوين حسب isCall
Container(
decoration: AppStyle.boxDecoration1.copyWith(
color: driver['isCall'].toString() == '1'
? AppColor.greenColor
: AppColor.accentColor,
),
padding: const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
child: Text(
'Driver Information'.tr,
style: CupertinoTheme.of(context).textTheme.navTitleTextStyle,
),
),
const SizedBox(height: 12),
InfoText(
'Name'.tr, driver['first_name'] + ' ' + driver['last_name']),
InfoText('Phone'.tr, driver['phone']),
InfoText('Email'.tr, driver['email']),
InfoText('License Type'.tr, driver['license_type']),
InfoText('License Categories'.tr, driver['license_categories']),
InfoText('National Number'.tr, driver['national_number']),
InfoText('Occupation'.tr, driver['occupation']),
const SizedBox(height: 12),
Text('Notes:'.tr,
style: CupertinoTheme.of(context).textTheme.navTitleTextStyle),
const SizedBox(height: 8),
CupertinoTextField(
controller: controller.notesController,
placeholder: driver['notes'] ?? 'Enter notes here...'.tr,
maxLines: 3,
padding: const EdgeInsets.all(12.0),
decoration: BoxDecoration(
border: Border.all(color: CupertinoColors.systemGrey),
borderRadius: BorderRadius.circular(8.0),
),
),
const SizedBox(height: 16),
Row(
children: [
Expanded(
child: MyElevatedButton(
title: 'Call Driver'.tr,
onPressed: () {
controller.makePhoneCall(driver['phone'].toString());
},
),
),
const SizedBox(width: 16),
Expanded(
child: CupertinoButton(
onPressed: () async {
await controller.addWelcomeCall(driver['id'].toString());
},
child: Text('Save Changes'.tr),
),
),
Expanded(
child: CupertinoButton(
onPressed: () async {
final phone = driver['phone'];
if (phone == null || phone.toString().isEmpty) {
mySnackbarError('لا يوجد رقم هاتف لهذا السائق'.tr);
return;
}
String message = "مرحباً،\n\n"
"يعطيك العافية أستاذ. نرحب بك في شركة *سيرو* للنقل الذكي.\n"
"نود تعريفك بالتطبيق ومميزاته لتتمكن من الاستفادة الكاملة وبدء العمل معنا بسهولة.\n\n"
"لأي استفسار أو مساعدة، يمكنك التواصل معنا على الأرقام التالية:\n\n"
"+963 952 475 742\n"
"+963 952 475 740\n"
"+963 952 475 734\n\n"
"فريق سيرو يتمنى لك تجربة موفقة ويوم سعيد.";
Get.find<MainController>().launchCommunication(
'whatsapp',
phone,
message,
);
},
child: Text('send'.tr),
),
),
],
),
],
),
),
);
}
}
class InfoText extends StatelessWidget {
final String label;
final dynamic value;
const InfoText(this.label, this.value, {super.key});
@override
Widget build(BuildContext context) {
final display = value?.toString() ?? 'N/A';
return Padding(
padding: const EdgeInsets.symmetric(vertical: 2.0),
child: Text(
'$label: $display',
style: CupertinoTheme.of(context).textTheme.textStyle,
),
);
}
}
class CupertinoCard extends StatelessWidget {
final Widget child;
final EdgeInsetsGeometry margin;
const CupertinoCard(
{super.key, required this.child, this.margin = EdgeInsets.zero});
@override
Widget build(BuildContext context) {
return Container(
margin: margin,
decoration: BoxDecoration(
color: CupertinoColors.systemBackground,
borderRadius: BorderRadius.circular(12.0),
boxShadow: [
BoxShadow(
color: CupertinoColors.systemGrey.withOpacity(0.2),
spreadRadius: 1,
blurRadius: 5,
offset: const Offset(0, 3),
),
],
),
child: child,
);
}
}