Initial commit for service
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
@@ -71,6 +72,27 @@ class MainController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
void updateDriverField(String key, dynamic value) async {
|
||||
// Update locally
|
||||
driverData['message'][0][key] = value;
|
||||
Log.print('driverData: ${driverData['message'][0]['driverID']}');
|
||||
update();
|
||||
|
||||
var res = await CRUD().post(link: AppLink.updateDriver, payload: {
|
||||
'driverID': driverData['message'][0]['driverID'].toString(),
|
||||
key: value.toString(),
|
||||
});
|
||||
if (res == 'failure') {
|
||||
Get.snackbar('Error', 'Failed to update driver data',
|
||||
backgroundColor: AppColor.redColor);
|
||||
} else {
|
||||
Get.snackbar('Success', 'Driver data updated successfully',
|
||||
backgroundColor: AppColor.greenColor);
|
||||
}
|
||||
// Optionally fetch driver again
|
||||
// await getDriverData();
|
||||
}
|
||||
|
||||
Future<void> makePhoneCall(String phoneNumber) async {
|
||||
final Uri launchUri = Uri(
|
||||
scheme: 'tel',
|
||||
@@ -79,25 +101,29 @@ class MainController extends GetxController {
|
||||
await launchUrl(launchUri);
|
||||
}
|
||||
|
||||
void launchCommunication(
|
||||
Future<void> launchCommunication(
|
||||
String method, String contactInfo, String message) async {
|
||||
String url;
|
||||
// رقّم فقط (بدون + أو مسافات)
|
||||
final phone = contactInfo.replaceAll(RegExp(r'[^0-9]'), '');
|
||||
final encodedMsg = Uri.encodeComponent(message);
|
||||
|
||||
Uri? uri;
|
||||
|
||||
if (Platform.isIOS) {
|
||||
switch (method) {
|
||||
case 'phone':
|
||||
url = 'tel:$contactInfo';
|
||||
uri = Uri.parse('tel:$phone');
|
||||
break;
|
||||
case 'sms':
|
||||
url = 'sms:$contactInfo?body=${Uri.encodeComponent(message)}';
|
||||
uri = Uri.parse('sms:$phone?body=$encodedMsg');
|
||||
break;
|
||||
case 'whatsapp':
|
||||
url =
|
||||
'https://api.whatsapp.com/send?phone=$contactInfo&text=${Uri.encodeComponent(message)}';
|
||||
uri = Uri.parse(
|
||||
'https://api.whatsapp.com/send?phone=$phone&text=$encodedMsg');
|
||||
break;
|
||||
case 'email':
|
||||
url =
|
||||
'mailto:$contactInfo?subject=Subject&body=${Uri.encodeComponent(message)}';
|
||||
uri =
|
||||
Uri.parse('mailto:$contactInfo?subject=Subject&body=$encodedMsg');
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
@@ -105,27 +131,32 @@ class MainController extends GetxController {
|
||||
} else if (Platform.isAndroid) {
|
||||
switch (method) {
|
||||
case 'phone':
|
||||
url = 'tel:$contactInfo';
|
||||
uri = Uri.parse('tel:$phone');
|
||||
break;
|
||||
case 'sms':
|
||||
url = 'sms:$contactInfo?body=${Uri.encodeComponent(message)}';
|
||||
uri = Uri.parse('sms:$phone?body=$encodedMsg');
|
||||
break;
|
||||
case 'whatsapp':
|
||||
// Check if WhatsApp is installed
|
||||
final bool whatsappInstalled =
|
||||
await canLaunchUrl(Uri.parse('whatsapp://'));
|
||||
if (whatsappInstalled) {
|
||||
url =
|
||||
'whatsapp://send?phone=$contactInfo&text=${Uri.encodeComponent(message)}';
|
||||
} else {
|
||||
// Provide an alternative action, such as opening the WhatsApp Web API
|
||||
url =
|
||||
'https://api.whatsapp.com/send?phone=$contactInfo&text=${Uri.encodeComponent(message)}';
|
||||
{
|
||||
final waDeepLink =
|
||||
Uri.parse('whatsapp://send?phone=$phone&text=$encodedMsg');
|
||||
if (await canLaunchUrl(waDeepLink)) {
|
||||
await launchUrl(waDeepLink, mode: LaunchMode.externalApplication);
|
||||
return;
|
||||
} else {
|
||||
final webUri = Uri.parse(
|
||||
'https://api.whatsapp.com/send?phone=$phone&text=$encodedMsg');
|
||||
if (await canLaunchUrl(webUri)) {
|
||||
await launchUrl(webUri, mode: LaunchMode.externalApplication);
|
||||
return;
|
||||
}
|
||||
// لو ما في متصفح أساسًا
|
||||
throw 'No handler for WhatsApp links';
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'email':
|
||||
url =
|
||||
'mailto:$contactInfo?subject=Subject&body=${Uri.encodeComponent(message)}';
|
||||
uri =
|
||||
Uri.parse('mailto:$contactInfo?subject=Subject&body=$encodedMsg');
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
@@ -134,9 +165,14 @@ class MainController extends GetxController {
|
||||
return;
|
||||
}
|
||||
|
||||
if (await canLaunchUrl(Uri.parse(url))) {
|
||||
await launchUrl(Uri.parse(url));
|
||||
} else {}
|
||||
if (uri != null) {
|
||||
final ok = await canLaunchUrl(uri);
|
||||
if (ok) {
|
||||
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||
} else {
|
||||
// ممكن تضيف Snackbar/Toast هنا
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List driverNotCompleteRegistration = [];
|
||||
@@ -153,6 +189,48 @@ class MainController extends GetxController {
|
||||
}
|
||||
}
|
||||
|
||||
deleteDriverNotCompleteRegistration(String phone) async {
|
||||
var res = await CRUD()
|
||||
.get(link: AppLink.deleteDriverNotCompleteRegistration, payload: {
|
||||
'phone': phone,
|
||||
});
|
||||
if (res != 'failure') {
|
||||
Get.snackbar(res, '', backgroundColor: AppColor.greenColor);
|
||||
// await getDriverWantCompleteRegistration();
|
||||
update();
|
||||
} else {
|
||||
Get.snackbar(res, '');
|
||||
}
|
||||
}
|
||||
|
||||
List driverWantCompleteRegistration = [];
|
||||
getDriverWantCompleteRegistration() async {
|
||||
var res =
|
||||
await CRUD().get(link: AppLink.getDriversWaitingActive, payload: {});
|
||||
if (res != 'failure') {
|
||||
var d = jsonDecode(res)['message'];
|
||||
driverWantCompleteRegistration = d;
|
||||
filteredDrivers = driverWantCompleteRegistration;
|
||||
update();
|
||||
} else {
|
||||
Get.snackbar(res, '');
|
||||
}
|
||||
}
|
||||
|
||||
List driversPhoneNotComplete = [];
|
||||
getDriversPhoneNotComplete() async {
|
||||
var res =
|
||||
await CRUD().get(link: AppLink.getDriversPhoneNotComplete, payload: {});
|
||||
if (res != 'failure') {
|
||||
var d = jsonDecode(res)['message'];
|
||||
driverWantCompleteRegistration = d;
|
||||
filteredDrivers = driverWantCompleteRegistration;
|
||||
update();
|
||||
} else {
|
||||
Get.snackbar(res, '');
|
||||
}
|
||||
}
|
||||
|
||||
List newDriverRegister = [];
|
||||
getNewDriverRegister() async {
|
||||
var res = await CRUD().get(link: AppLink.getNewDriverRegister, payload: {});
|
||||
@@ -364,6 +442,7 @@ class MainController extends GetxController {
|
||||
payload: {"phone": phone, "editor": editor, "note": note});
|
||||
if (res != 'failure') {
|
||||
Get.snackbar(res, '', backgroundColor: AppColor.greenColor);
|
||||
getDriversPhoneNotComplete();
|
||||
notesController.clear();
|
||||
} else {
|
||||
Get.snackbar(res, '', backgroundColor: AppColor.redColor);
|
||||
@@ -387,6 +466,22 @@ class MainController extends GetxController {
|
||||
if (formKey.currentState!.validate()) {
|
||||
await getDriverByPhone();
|
||||
Get.back();
|
||||
if (driverData.isEmpty) {
|
||||
Get.snackbar('Error', 'Driver not found', backgroundColor: Colors.red);
|
||||
return;
|
||||
}
|
||||
Get.to(() => DriverPage());
|
||||
}
|
||||
}
|
||||
|
||||
searchDriverByNational() async {
|
||||
if (formKey.currentState!.validate()) {
|
||||
await getDriverByNational();
|
||||
Get.back();
|
||||
if (driverData.isEmpty) {
|
||||
Get.snackbar('Error', 'Driver not found', backgroundColor: Colors.red);
|
||||
return;
|
||||
}
|
||||
Get.to(() => DriverPage());
|
||||
}
|
||||
}
|
||||
@@ -394,7 +489,7 @@ class MainController extends GetxController {
|
||||
getPassengersByPhone() async {
|
||||
var res = await CRUD().get(
|
||||
link: AppLink.getPassengersByPhone,
|
||||
payload: {"phone": '+2${passengerPhoneController.text}'});
|
||||
payload: {"phone": passengerPhoneController.text});
|
||||
|
||||
if (res != 'failure') {
|
||||
var d = jsonDecode(res);
|
||||
@@ -406,12 +501,28 @@ class MainController extends GetxController {
|
||||
getDriverByPhone() async {
|
||||
var res = await CRUD().get(
|
||||
link: AppLink.getDriverByPhone,
|
||||
payload: {"phone": '+2${driverPhoneController.text}'});
|
||||
payload: {"phone": driverPhoneController.text});
|
||||
|
||||
if (res != 'failure') {
|
||||
var d = jsonDecode(res);
|
||||
driverData = d;
|
||||
update();
|
||||
} else {
|
||||
Get.snackbar('Error', 'Driver not found', backgroundColor: Colors.red);
|
||||
}
|
||||
}
|
||||
|
||||
getDriverByNational() async {
|
||||
var res = await CRUD().get(
|
||||
link: AppLink.getDriverByNational,
|
||||
payload: {"national_number": driverPhoneController.text});
|
||||
|
||||
if (res != 'failure') {
|
||||
var d = jsonDecode(res);
|
||||
driverData = d;
|
||||
update();
|
||||
} else {
|
||||
Get.snackbar('Error', 'Driver not found', backgroundColor: Colors.red);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,7 +92,7 @@ class AddCarForm extends StatelessWidget {
|
||||
AppLink.uploadEgypt, carData['id'], 'car_front');
|
||||
},
|
||||
child: Image.network(
|
||||
'https://sefer.click/sefer/card_image/car_front-${carData['id']}.jpg',
|
||||
'${AppLink.server}/card_image/car_front-${carData['id']}.jpg',
|
||||
height: 200,
|
||||
width: double.maxFinite,
|
||||
fit: BoxFit.fill,
|
||||
@@ -100,7 +100,7 @@ class AddCarForm extends StatelessWidget {
|
||||
StackTrace? stackTrace) {
|
||||
// If the image fails to load, use the _copy version
|
||||
return Image.network(
|
||||
'https://sefer.click/sefer/card_image/car_front-${carData['id']}_copy.jpg',
|
||||
'${AppLink.server}/card_image/car_front-${carData['id']}_copy.jpg',
|
||||
height: 200,
|
||||
width: double.maxFinite,
|
||||
fit: BoxFit.fill,
|
||||
@@ -114,7 +114,7 @@ class AddCarForm extends StatelessWidget {
|
||||
AppLink.uploadEgypt, carData['id'], 'car_back');
|
||||
},
|
||||
child: Image.network(
|
||||
'https://sefer.click/sefer/card_image/car_back-${carData['id']}.jpg',
|
||||
'${AppLink.server}/card_image/car_back-${carData['id']}.jpg',
|
||||
height: 200,
|
||||
width: double.maxFinite,
|
||||
fit: BoxFit.fill,
|
||||
@@ -122,7 +122,7 @@ class AddCarForm extends StatelessWidget {
|
||||
StackTrace? stackTrace) {
|
||||
// If the image fails to load, use the _copy version
|
||||
return Image.network(
|
||||
'https://sefer.click/sefer/card_image/car_back-${carData['id']}_copy.jpg',
|
||||
'${AppLink.server}/card_image/car_back-${carData['id']}_copy.jpg',
|
||||
height: 200,
|
||||
width: double.maxFinite,
|
||||
fit: BoxFit.fill,
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:service/views/widgets/my_scafold.dart';
|
||||
|
||||
import '../main_controller.dart';
|
||||
|
||||
class DriverPage extends StatelessWidget {
|
||||
DriverPage({super.key});
|
||||
MainController mainController = MainController();
|
||||
|
||||
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']}'),
|
||||
@@ -21,16 +23,11 @@ class DriverPage extends StatelessWidget {
|
||||
child: CupertinoScrollbar(
|
||||
child: ListView(
|
||||
children: [
|
||||
_buildDriverInfoSection(
|
||||
mainController.driverData['message'][0]),
|
||||
_buildStatisticsSection(
|
||||
mainController.driverData['message'][0]),
|
||||
_buildCarInfoSection(mainController.driverData['message'][0]),
|
||||
_buildLicenseInfoSection(
|
||||
mainController.driverData['message'][0]),
|
||||
_buildBankInfoSection(
|
||||
mainController.driverData['message'][0]),
|
||||
// buildCarInfo(mainController.driverData['message'][0]),
|
||||
_buildDriverInfoSection(data),
|
||||
_buildStatisticsSection(data),
|
||||
_buildCarInfoSection(data),
|
||||
_buildLicenseInfoSection(data),
|
||||
_buildBankInfoSection(data),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -40,32 +37,83 @@ class DriverPage extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildDriverInfoSection(Map<String, dynamic> data) {
|
||||
// ============================================================
|
||||
// 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 $label"),
|
||||
trailing: GestureDetector(
|
||||
child: const Text(
|
||||
"Save",
|
||||
style: 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: [
|
||||
_buildInfoRow('Name'.tr, data['name_arabic'].toString()),
|
||||
_buildInfoRow('Name (English)'.tr, data['name_english'].toString()),
|
||||
_buildInfoRow('Phone'.tr, data['phone'].toString()),
|
||||
_buildInfoRow('Email'.tr, data['email'].toString()),
|
||||
_buildInfoRow('Gender'.tr, data['gender'].toString()),
|
||||
_buildInfoRow('Birthdate'.tr, data['birthdate'].toString()),
|
||||
_buildInfoRow('National Number'.tr, data['national_number'].toString()),
|
||||
_buildInfoRow('Religion'.tr, data['religion'].toString()),
|
||||
_buildInfoRow('Occupation'.tr, data['occupation'].toString()),
|
||||
_buildInfoRow('Education'.tr, data['education'].toString()),
|
||||
_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<String, dynamic> 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']}'),
|
||||
_buildInfoRow('Wallet Balance'.tr, '\$${data['totalDriverWallet']}'),
|
||||
_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(
|
||||
@@ -76,53 +124,55 @@ class DriverPage extends StatelessWidget {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCarInfoSection(Map<String, dynamic> data) {
|
||||
return CupertinoListSection.insetGrouped(
|
||||
header: Text('Vehicle Information'.tr),
|
||||
children: [
|
||||
_buildInfoRow('VIN'.tr, data['vin'].toString()),
|
||||
_buildInfoRow('Plate Number'.tr, data['car_plate'].toString()),
|
||||
_buildInfoRow('Make'.tr, data['make'].toString()),
|
||||
_buildInfoRow('Model'.tr, data['model'].toString()),
|
||||
_buildInfoRow('Year'.tr, data['year'].toString()),
|
||||
_buildInfoRow('Color'.tr, data['color'].toString()),
|
||||
_buildInfoRow('Fuel Type'.tr, data['fuel'].toString()),
|
||||
_buildInfoRow('Displacement'.tr, data['displacement'].toString()),
|
||||
_buildInfoRow(
|
||||
'Registration Date'.tr, data['registration_date'].toString()),
|
||||
_buildInfoRow('Expiration Date'.tr, data['expiration_date'].toString()),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLicenseInfoSection(Map<String, dynamic> data) {
|
||||
return CupertinoListSection.insetGrouped(
|
||||
header: Text('License Information'.tr),
|
||||
children: [
|
||||
_buildInfoRow('License Type'.tr, data['license_type'].toString()),
|
||||
_buildInfoRow('Card ID'.tr, data['card_id'].toString()),
|
||||
_buildInfoRow('Issue Date'.tr, data['issue_date'].toString()),
|
||||
_buildInfoRow('Expiry Date'.tr, data['expiry_date'].toString()),
|
||||
_buildInfoRow('Categories'.tr, data['license_categories'].toString()),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildBankInfoSection(Map<String, dynamic> data) {
|
||||
return CupertinoListSection.insetGrouped(
|
||||
header: Text('Bank Information'.tr),
|
||||
children: [
|
||||
_buildInfoRow('Account Number'.tr, data['accountBank'].toString()),
|
||||
_buildInfoRow('Bank Code'.tr, data['bankCode'].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)),
|
||||
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),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,9 +2,10 @@ import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:service/constant/colors.dart';
|
||||
import 'package:service/controller/functions/encrypt_decrypt.dart';
|
||||
import 'package:service/controller/mainController/main_controller.dart';
|
||||
import 'package:service/views/widgets/my_scafold.dart';
|
||||
import 'package:service/main.dart';
|
||||
import 'package:service/constant/box_name.dart';
|
||||
|
||||
import 'registration_captain_page.dart';
|
||||
|
||||
@@ -14,201 +15,204 @@ class DriversCantRegister extends StatelessWidget {
|
||||
@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 Cant Register'.tr,
|
||||
title: 'Drivers Want Register'.tr,
|
||||
isleading: true,
|
||||
body: [
|
||||
GetBuilder<MainController>(builder: (mainController) {
|
||||
return Column(
|
||||
children: [
|
||||
// Search
|
||||
Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
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: ListView.builder(
|
||||
itemCount: mainController.filteredDrivers.length,
|
||||
itemBuilder: (context, index) {
|
||||
final driver = mainController.filteredDrivers[index];
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(8.0),
|
||||
child: Container(
|
||||
color: driver['note'] == null
|
||||
? AppColor.greenColor
|
||||
: AppColor.greyColor,
|
||||
child: CupertinoFormSection(
|
||||
header: Text(
|
||||
'Driver ID: ${driver['driverId']}',
|
||||
),
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () => mainController
|
||||
.makePhoneCall(driver['phone_number']),
|
||||
child: Container(
|
||||
height: 40,
|
||||
color: driver['note'] != null
|
||||
? AppColor.greenColor
|
||||
: AppColor.greyColor,
|
||||
child: Row(
|
||||
mainAxisAlignment:
|
||||
MainAxisAlignment.spaceAround,
|
||||
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: [
|
||||
Text((driver['phone_number'])),
|
||||
IconButton(
|
||||
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"
|
||||
"نلاحظ أنك لم تكمل عملية التسجيل في خدمة Tripz درايفر. نود تذكيرك بأن إكمال التسجيل يتيح لك فرصة الانضمام إلى فريق Tripz والاستفادة من خدماتنا المتنوعة.\n\n"
|
||||
"إذا كنت بحاجة إلى أي مساعدة أو لديك أي استفسارات، لا تتردد في الاتصال بنا. نحن هنا لمساعدتك.\n\n"
|
||||
"للاتصال بنا، يرجى الاتصال على الرقم التالي: +20 101 880 5430\n\n"
|
||||
"مع تحيات فريق Tripz.";
|
||||
"يظهر لدينا في نظام تطبيق *انطلق* أنك لم تكمل عملية التسجيل بعد.\n"
|
||||
"ندعوك لإكمال التسجيل للاستفادة من مزايا التطبيق والبدء بالعمل معنا.\n\n"
|
||||
"إذا احتجت لأي مساعدة، تواصل معنا على خدمة العملاء:\n"
|
||||
"+963 952 475 742\n\n"
|
||||
"+963 952 475 740\n\n"
|
||||
"فريق انطلق يتمنى لك يوماً سعيداً.";
|
||||
|
||||
mainController.launchCommunication(
|
||||
'whatsapp',
|
||||
'${driver['phone_number']}',
|
||||
message);
|
||||
'whatsapp',
|
||||
driver['phone_number'],
|
||||
message,
|
||||
);
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.send_time_extension_sharp,
|
||||
color: AppColor.secondaryColor,
|
||||
),
|
||||
),
|
||||
IconButton(
|
||||
|
||||
// Edit button → go to registration form
|
||||
buildActionButton(
|
||||
icon: CupertinoIcons
|
||||
.pencil_ellipsis_rectangle,
|
||||
color: AppColor.gold,
|
||||
onPressed: () {
|
||||
Get.to(() => RegisterCaptain(),
|
||||
arguments: {
|
||||
"phone_number":
|
||||
driver['phone_number']
|
||||
.toString(),
|
||||
'driverId': driver['driverId']
|
||||
.toString(),
|
||||
'email':
|
||||
driver['email'].toString(),
|
||||
});
|
||||
Get.to(
|
||||
() => RegisterCaptain(),
|
||||
arguments: {
|
||||
"phone": driver['phone_number'],
|
||||
"driverId": driver['driverId'],
|
||||
},
|
||||
);
|
||||
},
|
||||
),
|
||||
buildActionButton(
|
||||
icon: CupertinoIcons
|
||||
.pencil_ellipsis_rectangle,
|
||||
color: AppColor.redColor,
|
||||
onPressed: () {
|
||||
mainController
|
||||
.deleteDriverNotCompleteRegistration(
|
||||
driver['phone_number']);
|
||||
},
|
||||
icon: const Icon(
|
||||
Icons.save,
|
||||
color: AppColor.gold,
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
// CupertinoFormRow(
|
||||
// prefix: Text('Phone Number'.tr),
|
||||
// child: CupertinoTextFormFieldRow(
|
||||
// initialValue: driver['phone_number'],
|
||||
// readOnly: true,
|
||||
// placeholder: 'Phone Number'.tr,
|
||||
// ),
|
||||
// ),
|
||||
),
|
||||
),
|
||||
CupertinoFormRow(
|
||||
prefix: Text('Created At'.tr),
|
||||
child: CupertinoTextFormFieldRow(
|
||||
initialValue: driver['created_at'],
|
||||
readOnly: true,
|
||||
placeholder: 'Created At',
|
||||
),
|
||||
),
|
||||
CupertinoFormRow(
|
||||
prefix: Text('Status'.tr),
|
||||
child: GestureDetector(
|
||||
onTap: () {
|
||||
showCupertinoModalPopup<void>(
|
||||
context: Get.context!,
|
||||
builder: (BuildContext context) =>
|
||||
Container(
|
||||
height: 216,
|
||||
padding: const EdgeInsets.only(top: 6.0),
|
||||
margin: EdgeInsets.only(
|
||||
bottom: MediaQuery.of(context)
|
||||
.viewInsets
|
||||
.bottom,
|
||||
),
|
||||
color: CupertinoColors.systemBackground
|
||||
.resolveFrom(context),
|
||||
child: SafeArea(
|
||||
top: false,
|
||||
child: CupertinoPicker(
|
||||
magnification: 1.22,
|
||||
squeeze: 1.2,
|
||||
useMagnifier: true,
|
||||
itemExtent: 32.0,
|
||||
scrollController:
|
||||
FixedExtentScrollController(
|
||||
initialItem: mainController
|
||||
.selectedStatus
|
||||
.indexOf(mainController
|
||||
.selectedStatus),
|
||||
),
|
||||
onSelectedItemChanged:
|
||||
(int selectedItem) {
|
||||
mainController.setSelectedStatus(
|
||||
mainController
|
||||
.statusOptions[selectedItem]
|
||||
.tr);
|
||||
},
|
||||
children: List<Widget>.generate(
|
||||
mainController.statusOptions
|
||||
.length, (int index) {
|
||||
return Center(
|
||||
child: Text(mainController
|
||||
.statusOptions[index].tr),
|
||||
);
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
child: CupertinoFormRow(
|
||||
child: Text(
|
||||
mainController.selectedStatus.tr,
|
||||
style: TextStyle(
|
||||
color: CupertinoColors.label
|
||||
.resolveFrom(Get.context!)),
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
CupertinoFormRow(
|
||||
prefix: Text('Notes'.tr),
|
||||
child: CupertinoTextFormFieldRow(
|
||||
cursorColor: AppColor.blueColor,
|
||||
controller: mainController.notesController,
|
||||
placeholder:
|
||||
driver['note'] ?? "Additional comments".tr,
|
||||
),
|
||||
),
|
||||
CupertinoButton(
|
||||
child: Text('Save Notes'.tr),
|
||||
onPressed: () {
|
||||
// Save the notes for the driver
|
||||
String notes =
|
||||
mainController.notesController.text == ''
|
||||
? mainController.selectedStatus
|
||||
.toString()
|
||||
: mainController.notesController.text;
|
||||
|
||||
mainController
|
||||
.saveNoteForDriverNotCompleteRegistration(
|
||||
driver['phone_number'],
|
||||
'girls name',
|
||||
notes);
|
||||
print(
|
||||
'Notes for driver ${driver['id']}: $notes');
|
||||
},
|
||||
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),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
485
lib/controller/mainController/pages/new_driver.dart
Normal file
485
lib/controller/mainController/pages/new_driver.dart
Normal file
@@ -0,0 +1,485 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:service/constant/colors.dart';
|
||||
// Importa tu nuevo controlador de servicio
|
||||
import 'package:service/controller/mainController/ragister_service_controller.dart';
|
||||
import 'package:service/views/widgets/elevated_btn.dart';
|
||||
import 'package: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,
|
||||
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),
|
||||
),
|
||||
);
|
||||
}),
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -28,12 +28,12 @@ class PassengersCantRegister extends StatelessWidget {
|
||||
header: Text('Passenger ID: ${passenger['id']}'),
|
||||
children: [
|
||||
InkWell(
|
||||
onTap: () => mainController
|
||||
.makePhoneCall(passenger['phone_number']),
|
||||
onTap: () =>
|
||||
mainController.makePhoneCall(passenger['phone']),
|
||||
child: CupertinoFormRow(
|
||||
prefix: Text('Phone Number'.tr),
|
||||
prefix: Text(' phone'.tr),
|
||||
child: CupertinoTextFormFieldRow(
|
||||
initialValue: ((passenger['phone_number'])),
|
||||
initialValue: ((passenger['phone'])),
|
||||
readOnly: true,
|
||||
placeholder: 'Phone Number'.tr,
|
||||
),
|
||||
|
||||
@@ -9,92 +9,109 @@ class PassengersPage extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GetBuilder<MainController>(builder: (mainController) {
|
||||
Map data = mainController.passengerData['message'][0];
|
||||
return MyScaffold(
|
||||
title: (data['first_name']?.toString() ?? '') +
|
||||
' ' +
|
||||
(data['last_name']?.toString() ?? ''),
|
||||
isleading: true,
|
||||
body: [
|
||||
ListView(
|
||||
children: [
|
||||
_buildPersonalInfoCard(data),
|
||||
_buildLatestRideCard(data),
|
||||
_buildWalletInfoCard(data),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
});
|
||||
return GetBuilder<MainController>(
|
||||
builder: (mainController) {
|
||||
final data = _extractPassengerData(mainController);
|
||||
|
||||
return MyScaffold(
|
||||
title: data != null
|
||||
? '${data['first_name'] ?? ''} ${data['last_name'] ?? ''}'
|
||||
: 'Passenger Not Found'.tr,
|
||||
isleading: true,
|
||||
body: [
|
||||
if (data != null)
|
||||
ListView(
|
||||
children: [
|
||||
_buildPersonalInfoCard(data),
|
||||
_buildLatestRideCard(data),
|
||||
_buildWalletInfoCard(data),
|
||||
],
|
||||
)
|
||||
else
|
||||
const Center(
|
||||
child: Padding(
|
||||
padding: EdgeInsets.all(24.0),
|
||||
child: Text(
|
||||
'No passenger data available.',
|
||||
style: TextStyle(fontSize: 18, fontWeight: FontWeight.w500),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
/// 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 Card(
|
||||
margin: const EdgeInsets.all(16),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Personal Information'.tr,
|
||||
style:
|
||||
const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 16),
|
||||
_buildInfoRow('Phone'.tr, data['phone']?.toString() ?? 'N/A'),
|
||||
_buildInfoRow('Email'.tr, data['email']?.toString() ?? 'N/A'),
|
||||
_buildInfoRow('Gender'.tr, data['gender']?.toString() ?? 'N/A'),
|
||||
_buildInfoRow(
|
||||
'Birthdate'.tr, data['birthdate']?.toString() ?? 'N/A'),
|
||||
_buildInfoRow(
|
||||
'Education'.tr, data['education']?.toString() ?? 'N/A'),
|
||||
_buildInfoRow(
|
||||
'Employment'.tr, data['employmentType']?.toString() ?? 'N/A'),
|
||||
_buildInfoRow('Marital Status'.tr,
|
||||
data['maritalStatus']?.toString() ?? 'N/A'),
|
||||
],
|
||||
),
|
||||
),
|
||||
return _buildCard(
|
||||
title: 'Personal Information'.tr,
|
||||
children: [
|
||||
_buildInfoRow('Phone'.tr, data['phone']),
|
||||
_buildInfoRow('Email'.tr, data['email']),
|
||||
_buildInfoRow('Gender'.tr, data['gender']),
|
||||
_buildInfoRow('Birthdate'.tr, data['birthdate']),
|
||||
// _buildInfoRow('Education'.tr, data['education']),
|
||||
_buildInfoRow('Employment'.tr, data['employmentType']),
|
||||
_buildInfoRow('Marital Status'.tr, data['maritalStatus']),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLatestRideCard(Map data) {
|
||||
return Card(
|
||||
margin: const EdgeInsets.all(16),
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Latest Ride'.tr,
|
||||
style:
|
||||
const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 16),
|
||||
_buildInfoRow('Ride ID'.tr, data['ride_id']?.toString() ?? 'N/A'),
|
||||
_buildInfoRow('Date'.tr, data['ride_date']?.toString() ?? 'N/A'),
|
||||
_buildInfoRow(
|
||||
'Start Time'.tr, data['ride_time']?.toString() ?? 'N/A'),
|
||||
_buildInfoRow(
|
||||
'End Time'.tr, data['ride_endtime']?.toString() ?? 'N/A'),
|
||||
_buildInfoRow(
|
||||
'Price'.tr, '\$${data['price']?.toString() ?? 'N/A'}'),
|
||||
_buildInfoRow(
|
||||
'Status'.tr, data['ride_status']?.toString() ?? 'N/A'),
|
||||
_buildInfoRow('Payment Method'.tr,
|
||||
data['ride_payment_method']?.toString() ?? 'N/A'),
|
||||
_buildInfoRow('Car Type'.tr, data['car_type']?.toString() ?? 'N/A'),
|
||||
_buildInfoRow(
|
||||
'Distance'.tr,
|
||||
data['distance'] != null
|
||||
? '${data['distance'].toStringAsFixed(2)} km'
|
||||
: 'N/A'),
|
||||
],
|
||||
),
|
||||
),
|
||||
return _buildCard(
|
||||
title: 'Latest Ride'.tr,
|
||||
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),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildWalletInfoCard(Map data) {
|
||||
return _buildCard(
|
||||
title: 'Wallet Information'.tr,
|
||||
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']),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildCard({required String title, required List<Widget> children}) {
|
||||
return Card(
|
||||
margin: const EdgeInsets.all(16),
|
||||
child: Padding(
|
||||
@@ -102,30 +119,26 @@ class PassengersPage extends StatelessWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text('Wallet Information'.tr,
|
||||
Text(title,
|
||||
style:
|
||||
const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 16),
|
||||
_buildInfoRow('Wallet Balance'.tr,
|
||||
'\$${data['passenger_wallet_balance']?.toString() ?? 'N/A'}'),
|
||||
_buildInfoRow('Last Payment Amount'.tr,
|
||||
'\$${data['passenger_payment_amount']?.toString() ?? 'N/A'}'),
|
||||
_buildInfoRow('Last Payment Method'.tr,
|
||||
data['passenger_payment_method']?.toString() ?? 'N/A'),
|
||||
...children,
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildInfoRow(String label, String value) {
|
||||
Widget _buildInfoRow(String label, dynamic value) {
|
||||
final displayValue = value?.toString() ?? 'N/A';
|
||||
return Padding(
|
||||
padding: const EdgeInsets.symmetric(vertical: 4),
|
||||
child: Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(label, style: const TextStyle(fontWeight: FontWeight.bold)),
|
||||
Text(value),
|
||||
Flexible(child: Text(displayValue, overflow: TextOverflow.ellipsis)),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,4 +1,5 @@
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:service/constant/colors.dart';
|
||||
import 'package:service/constant/style.dart';
|
||||
@@ -12,21 +13,35 @@ class WelcomeCall extends StatelessWidget {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
Get.put(MainController());
|
||||
final controller = Get.put(MainController());
|
||||
|
||||
return MyScaffold(
|
||||
title: 'Welcome Drivers'.tr,
|
||||
isleading: true,
|
||||
body: [
|
||||
GetBuilder<MainController>(builder: (mainController) {
|
||||
return Expanded(
|
||||
child: CupertinoScrollbar(
|
||||
child: ListView.builder(
|
||||
itemCount: mainController.newDriverRegister.length,
|
||||
itemBuilder: (context, index) {
|
||||
final driver = mainController.newDriverRegister[index];
|
||||
return DriverCard(driver: driver);
|
||||
},
|
||||
final drivers = mainController.newDriverRegister;
|
||||
|
||||
if (drivers.isEmpty) {
|
||||
return const Padding(
|
||||
padding: EdgeInsets.all(32.0),
|
||||
child: Center(
|
||||
child: Text(
|
||||
'No new drivers found.',
|
||||
style: 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);
|
||||
},
|
||||
),
|
||||
);
|
||||
}),
|
||||
@@ -39,9 +54,22 @@ 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(
|
||||
@@ -49,38 +77,37 @@ class DriverCard extends StatelessWidget {
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
// حالة التلوين حسب isCall
|
||||
Container(
|
||||
decoration: AppStyle.boxDecoration1.copyWith(
|
||||
color: driver['isCall'].toString() == '1'
|
||||
? AppColor.greenColor
|
||||
: AppColor.accentColor),
|
||||
child: Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 10, vertical: 2),
|
||||
child: Text(
|
||||
'Driver Information'.tr,
|
||||
style: CupertinoTheme.of(context).textTheme.navTitleTextStyle,
|
||||
),
|
||||
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: 16),
|
||||
InfoText('Name'.tr, driver['name_arabic'].toString()),
|
||||
InfoText('Phone'.tr, driver['phone'].toString()),
|
||||
InfoText('Email'.tr, driver['email'].toString()),
|
||||
InfoText('License Type'.tr, driver['license_type'].toString()),
|
||||
const SizedBox(height: 12),
|
||||
|
||||
InfoText(
|
||||
'License Categories'.tr, driver['license_categories'] ?? ''),
|
||||
InfoText(
|
||||
'National Number'.tr, driver['national_number'].toString()),
|
||||
InfoText('Occupation'.tr, driver['occupation'].toString()),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Notes:'.tr,
|
||||
style: CupertinoTheme.of(context).textTheme.navTitleTextStyle,
|
||||
),
|
||||
'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: Get.find<MainController>().notesController,
|
||||
controller: controller.notesController,
|
||||
placeholder: driver['notes'] ?? 'Enter notes here...'.tr,
|
||||
maxLines: 3,
|
||||
padding: const EdgeInsets.all(12.0),
|
||||
@@ -89,25 +116,55 @@ class DriverCard extends StatelessWidget {
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
),
|
||||
),
|
||||
|
||||
const SizedBox(height: 16),
|
||||
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
SizedBox(
|
||||
width: Get.width * .4,
|
||||
child: MyElevatedButton(
|
||||
title: 'Call Driver'.tr,
|
||||
onPressed: () {
|
||||
Get.find<MainController>()
|
||||
.makePhoneCall(driver['phone'].toString());
|
||||
})),
|
||||
CupertinoButton(
|
||||
onPressed: () async {
|
||||
await Get.find<MainController>().addWelcomeCall(
|
||||
driver['id'].toString(),
|
||||
);
|
||||
},
|
||||
child: Text('Save Changes'.tr),
|
||||
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) {
|
||||
Get.snackbar("خطأ", "لا يوجد رقم هاتف لهذا السائق");
|
||||
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),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -120,16 +177,17 @@ class DriverCard extends StatelessWidget {
|
||||
|
||||
class InfoText extends StatelessWidget {
|
||||
final String label;
|
||||
final String value;
|
||||
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.only(bottom: 4.0),
|
||||
padding: const EdgeInsets.symmetric(vertical: 2.0),
|
||||
child: Text(
|
||||
'$label: $value',
|
||||
'$label: $display',
|
||||
style: CupertinoTheme.of(context).textTheme.textStyle,
|
||||
),
|
||||
);
|
||||
|
||||
352
lib/controller/mainController/ragister_service_controller.dart
Normal file
352
lib/controller/mainController/ragister_service_controller.dart
Normal file
@@ -0,0 +1,352 @@
|
||||
import 'dart:convert';
|
||||
import 'package:crypto/crypto.dart';
|
||||
import 'package:flutter/cupertino.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:service/constant/box_name.dart';
|
||||
import 'package:service/constant/links.dart';
|
||||
import 'package:service/controller/functions/crud.dart';
|
||||
import 'package:service/main.dart';
|
||||
import 'package:service/print.dart';
|
||||
import 'package:service/views/widgets/my_dialog.dart';
|
||||
|
||||
import 'pages/registration_captain_page.dart';
|
||||
|
||||
class RegisterCaptainServiceController extends GetxController {
|
||||
// --- UI State Management ---
|
||||
var isLoading = true.obs;
|
||||
late PageController pageController;
|
||||
var currentPageIndex = 0.obs;
|
||||
var driverId = ''.obs;
|
||||
var phone = ''.obs;
|
||||
var serverData = <String, dynamic>{}.obs;
|
||||
// En tu archivo registration_captain_controller.dart
|
||||
final phoneController = TextEditingController();
|
||||
// --- Dynamic Document Image URLs ---
|
||||
final Map<String, RxString> docUrls = {
|
||||
'driver_license_front': ''.obs,
|
||||
'driver_license_back': ''.obs,
|
||||
'car_license_front': ''.obs,
|
||||
'car_license_back': ''.obs,
|
||||
};
|
||||
|
||||
// --- Text Field Controllers ---
|
||||
late TextEditingController firstNameController;
|
||||
late TextEditingController lastNameController;
|
||||
late TextEditingController siteController;
|
||||
late TextEditingController nationalNumberController;
|
||||
late TextEditingController birthdateController;
|
||||
late TextEditingController licenseCategoriesController;
|
||||
late TextEditingController expiryDateController;
|
||||
late TextEditingController ownerController;
|
||||
late TextEditingController colorController;
|
||||
late TextEditingController carPlateController;
|
||||
late TextEditingController vinController;
|
||||
late TextEditingController licenseIssueDateController;
|
||||
late TextEditingController carLicenseExpiryDateController;
|
||||
late TextEditingController makeController;
|
||||
late TextEditingController modelController;
|
||||
late TextEditingController yearController;
|
||||
|
||||
// --- Reactive State for Dropdowns ---
|
||||
var selectedGender = ''.obs;
|
||||
var colorHex = ''.obs;
|
||||
var selectedFuel = ''.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
pageController = PageController();
|
||||
|
||||
// final arguments = Get.arguments;
|
||||
// driverId.value = arguments?['driverId']; // Fallback for testing
|
||||
// phone.value = arguments?['phone']; // Fallback for testing
|
||||
|
||||
_initializeTextControllers();
|
||||
_initializeDocUrls();
|
||||
fetchDataFromServer();
|
||||
}
|
||||
|
||||
void _initializeDocUrls() {
|
||||
const baseUrl =
|
||||
"https://syria.intaleq.xyz/intaleq/auth/syria/driversDocs/syria.intaleq.xyz-";
|
||||
docUrls['driver_license_front']!.value =
|
||||
"$baseUrl${driverId.value}-driver_license_front.jpg";
|
||||
docUrls['driver_license_back']!.value =
|
||||
"$baseUrl${driverId.value}-driver_license_back.jpg";
|
||||
docUrls['car_license_front']!.value =
|
||||
"$baseUrl${driverId.value}-car_license_front.jpg";
|
||||
docUrls['car_license_back']!.value =
|
||||
"$baseUrl${driverId.value}-car_license_back.jpg";
|
||||
}
|
||||
|
||||
void _initializeTextControllers() {
|
||||
firstNameController = TextEditingController();
|
||||
lastNameController = TextEditingController();
|
||||
siteController = TextEditingController();
|
||||
nationalNumberController = TextEditingController();
|
||||
birthdateController = TextEditingController();
|
||||
licenseCategoriesController = TextEditingController();
|
||||
expiryDateController = TextEditingController();
|
||||
ownerController = TextEditingController();
|
||||
colorController = TextEditingController();
|
||||
carPlateController = TextEditingController();
|
||||
vinController = TextEditingController();
|
||||
licenseIssueDateController = TextEditingController();
|
||||
carLicenseExpiryDateController = TextEditingController();
|
||||
makeController = TextEditingController();
|
||||
modelController = TextEditingController();
|
||||
yearController = TextEditingController();
|
||||
}
|
||||
|
||||
Future<void> fetchDataFromServer() async {
|
||||
isLoading.value = true;
|
||||
try {
|
||||
var responseString = await CRUD().post(
|
||||
link: AppLink.getDriverDetailsForActivate,
|
||||
payload: {'driverId': driverId.value});
|
||||
|
||||
if (responseString != 'failure') {
|
||||
var decodedResponse = jsonDecode(responseString);
|
||||
if (decodedResponse['status'] == 'success' &&
|
||||
(decodedResponse['message'] as List).isNotEmpty) {
|
||||
var rawData = decodedResponse['message'][0] as Map<String, dynamic>;
|
||||
// Sanitize data: ensure all values are strings to prevent type errors
|
||||
serverData.value = rawData
|
||||
.map((key, value) => MapEntry(key, value?.toString() ?? ''));
|
||||
_populateControllersWithServerData();
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
Get.snackbar('Error', 'An unexpected error occurred: $e');
|
||||
} finally {
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
void _populateControllersWithServerData() {
|
||||
firstNameController.text = serverData['first_name'] ?? '';
|
||||
lastNameController.text = serverData['last_name'] ?? '';
|
||||
siteController.text = serverData['site'] ?? '';
|
||||
nationalNumberController.text = serverData['national_number'] ?? '';
|
||||
birthdateController.text = serverData['birthdate'] ?? '';
|
||||
selectedGender.value = serverData['gender'] ?? '';
|
||||
licenseCategoriesController.text = serverData['license_categories'] ?? '';
|
||||
expiryDateController.text = serverData['expiry_date'] ?? '';
|
||||
ownerController.text = serverData['owner'] ?? '';
|
||||
|
||||
final serverColorName = serverData['color'] ?? '';
|
||||
final colorOption = kCarColorOptions.firstWhere(
|
||||
(opt) => opt['key'] == serverColorName,
|
||||
orElse: () => {'key': serverColorName, 'hex': '#000000'}); // Fallback
|
||||
colorController.text = colorOption['key']!;
|
||||
colorHex.value = colorOption['hex']!;
|
||||
|
||||
carPlateController.text = serverData['car_plate'] ?? '';
|
||||
vinController.text = serverData['vin'] ?? '';
|
||||
licenseIssueDateController.text = serverData['issue_date'] ?? '';
|
||||
carLicenseExpiryDateController.text = serverData['expiration_date'] ?? '';
|
||||
makeController.text = serverData['make'] ?? '';
|
||||
modelController.text = serverData['model'] ?? '';
|
||||
selectedFuel.value = serverData['fuel'] ?? '';
|
||||
yearController.text = serverData['year'] ?? '';
|
||||
}
|
||||
|
||||
Future<void> selectDate(
|
||||
BuildContext context, TextEditingController controller) async {
|
||||
DateTime initialDate = DateTime.now();
|
||||
try {
|
||||
if (controller.text.isNotEmpty)
|
||||
initialDate = DateFormat('yyyy-MM-dd').parse(controller.text);
|
||||
} catch (e) {/* Use default if parsing fails */}
|
||||
|
||||
DateTime? pickedDate = initialDate;
|
||||
await showCupertinoModalPopup<void>(
|
||||
context: context,
|
||||
builder: (BuildContext context) => Container(
|
||||
height: 280,
|
||||
color: CupertinoColors.systemBackground.resolveFrom(context),
|
||||
child: Column(
|
||||
children: [
|
||||
SizedBox(
|
||||
height: 200,
|
||||
child: CupertinoDatePicker(
|
||||
initialDateTime: initialDate,
|
||||
minimumDate: DateTime(1950),
|
||||
maximumDate: DateTime(2050),
|
||||
mode: CupertinoDatePickerMode.date,
|
||||
onDateTimeChanged: (DateTime newDate) {
|
||||
pickedDate = newDate;
|
||||
},
|
||||
),
|
||||
),
|
||||
CupertinoButton(
|
||||
child: Text('Done'.tr),
|
||||
onPressed: () {
|
||||
if (pickedDate != null)
|
||||
controller.text =
|
||||
DateFormat('yyyy-MM-dd').format(pickedDate!);
|
||||
Navigator.of(context).pop();
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void nextPage() {
|
||||
if (currentPageIndex.value < 3)
|
||||
pageController.nextPage(
|
||||
duration: const Duration(milliseconds: 400), curve: Curves.easeInOut);
|
||||
}
|
||||
|
||||
void previousPage() {
|
||||
if (currentPageIndex.value > 0)
|
||||
pageController.previousPage(
|
||||
duration: const Duration(milliseconds: 400), curve: Curves.easeInOut);
|
||||
}
|
||||
|
||||
void updateAndActivateSyrianDriver() {
|
||||
isLoading.value = true;
|
||||
var bytes = utf8.encode(phoneController.text);
|
||||
|
||||
// 2. Genera el hash MD5 completo
|
||||
var digest = md5.convert(bytes);
|
||||
|
||||
// 3. Convierte el hash a un string (tendrá 32 caracteres)
|
||||
String fullMd5Hash = digest.toString();
|
||||
|
||||
// 4. Trunca el string a los primeros 20 caracteres
|
||||
driverId.value = fullMd5Hash.substring(0, 20);
|
||||
Map<String, dynamic> dataToSend = {
|
||||
// Driver fields from all pages
|
||||
'id': driverId.value,
|
||||
'phone': phoneController.text,
|
||||
'first_name': firstNameController.text,
|
||||
'last_name': lastNameController.text,
|
||||
'site': siteController.text,
|
||||
'national_number': nationalNumberController.text,
|
||||
'gender': selectedGender.value,
|
||||
'birthdate': birthdateController.text,
|
||||
'license_categories': licenseCategoriesController.text,
|
||||
'expiry_date': expiryDateController.text,
|
||||
'license_issue_date': licenseIssueDateController.text,
|
||||
'password': md5.convert(utf8.encode('123456')).toString(),
|
||||
'status': 'actives',
|
||||
// Car fields from all pages
|
||||
'owner': ownerController.text,
|
||||
'color': colorController.text,
|
||||
'color_hex': colorHex.value,
|
||||
'car_plate': carPlateController.text,
|
||||
'maritalStatus': box.read(BoxName.employeename) ?? 'unknown',
|
||||
// 'vin': vinController.text,
|
||||
'expiration_date': carLicenseExpiryDateController.text,
|
||||
'make': makeController.text,
|
||||
'model': modelController.text,
|
||||
'fuel': selectedFuel.value,
|
||||
'year': yearController.text,
|
||||
};
|
||||
|
||||
print("--- Submitting Data to Server ---");
|
||||
print(dataToSend);
|
||||
|
||||
CRUD()
|
||||
.post(
|
||||
link:
|
||||
'${AppLink.server}/serviceapp/registerDriverAndCarService.php',
|
||||
payload: dataToSend)
|
||||
.then((response) {
|
||||
isLoading.value = false;
|
||||
var decodedResponse = (response);
|
||||
Log.print('decodedResponse: ${decodedResponse}');
|
||||
if (decodedResponse != 'failure') {
|
||||
MyDialog().getDialog('Success'.tr, '',
|
||||
Text('Driver has been activated successfully!'.tr), () {
|
||||
Get.back();
|
||||
Get.back();
|
||||
// fetchDataFromServer();
|
||||
Get.back();
|
||||
// Get.off(() => RegisterCaptain());
|
||||
});
|
||||
} else {
|
||||
Get.snackbar(
|
||||
'Error'.tr,
|
||||
'Failed to update driver: ${decodedResponse['message']}'.tr,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
}
|
||||
}).catchError((error) {
|
||||
isLoading.value = false;
|
||||
Get.snackbar(
|
||||
'Error'.tr,
|
||||
'An error occurred: $error'.tr,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white,
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
static const List<Map<String, String>> kCarColorOptions = [
|
||||
{'key': 'white', 'hex': '#FFFFFF'},
|
||||
{'key': 'black', 'hex': '#000000'},
|
||||
{'key': 'silver', 'hex': '#C0C0C0'},
|
||||
{'key': 'gray', 'hex': '#808080'},
|
||||
{'key': 'gunmetal', 'hex': '#2A3439'},
|
||||
{'key': 'red', 'hex': '#C62828'},
|
||||
{'key': 'blue', 'hex': '#1565C0'},
|
||||
{'key': 'navy', 'hex': '#0D47A1'},
|
||||
{'key': 'green', 'hex': '#2E7D32'},
|
||||
{'key': 'darkGreen', 'hex': '#1B5E20'},
|
||||
{'key': 'beige', 'hex': '#D7CCC8'},
|
||||
{'key': 'brown', 'hex': '#5D4037'},
|
||||
{'key': 'maroon', 'hex': '#800000'},
|
||||
{'key': 'burgundy', 'hex': '#800020'},
|
||||
{'key': 'yellow', 'hex': '#F9A825'},
|
||||
{'key': 'orange', 'hex': '#EF6C00'},
|
||||
{'key': 'gold', 'hex': '#D4AF37'},
|
||||
{'key': 'bronze', 'hex': '#CD7F32'},
|
||||
{'key': 'champagne', 'hex': '#EFE1C6'},
|
||||
{'key': 'purple', 'hex': '#6A1B9A'},
|
||||
];
|
||||
|
||||
static const List<String> kFuelOptions = [
|
||||
'بنزين',
|
||||
'ديزل',
|
||||
'هايبرد',
|
||||
'كهربائي'
|
||||
];
|
||||
|
||||
Color hexToColor(String code) =>
|
||||
Color(int.parse(code.substring(1, 7), radix: 16) + 0xFF000000);
|
||||
|
||||
void updateColorSelection(String hex) {
|
||||
colorHex.value = hex;
|
||||
colorController.text =
|
||||
kCarColorOptions.firstWhere((o) => o['hex'] == hex)['key']!;
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
pageController.dispose();
|
||||
firstNameController.dispose();
|
||||
lastNameController.dispose();
|
||||
siteController.dispose();
|
||||
nationalNumberController.dispose();
|
||||
birthdateController.dispose();
|
||||
licenseCategoriesController.dispose();
|
||||
expiryDateController.dispose();
|
||||
ownerController.dispose();
|
||||
colorController.dispose();
|
||||
carPlateController.dispose();
|
||||
vinController.dispose();
|
||||
licenseIssueDateController.dispose();
|
||||
carLicenseExpiryDateController.dispose();
|
||||
makeController.dispose();
|
||||
modelController.dispose();
|
||||
yearController.dispose();
|
||||
super.onClose();
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user