first commit
This commit is contained in:
567
siro_service/lib/controller/mainController/main_controller.dart
Normal file
567
siro_service/lib/controller/mainController/main_controller.dart
Normal file
@@ -0,0 +1,567 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:siro_service/constant/colors.dart';
|
||||
import 'package:siro_service/constant/links.dart';
|
||||
import 'package:siro_service/controller/functions/crud.dart';
|
||||
import 'package:siro_service/controller/mainController/pages/driver_page.dart';
|
||||
import 'package:siro_service/main.dart';
|
||||
import 'package:siro_service/views/widgets/my_dialog.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
|
||||
import '../../print.dart';
|
||||
import 'pages/passengers_page.dart';
|
||||
|
||||
class MainController extends GetxController {
|
||||
final formKey = GlobalKey<FormState>();
|
||||
bool isLoading = false;
|
||||
final passengerPhoneController = TextEditingController();
|
||||
final driverPhoneController = TextEditingController();
|
||||
final notesController = TextEditingController();
|
||||
final carplateController = TextEditingController();
|
||||
TextEditingController colorController = TextEditingController();
|
||||
TextEditingController makeController = TextEditingController();
|
||||
TextEditingController modelController = TextEditingController();
|
||||
TextEditingController expirationDateController = TextEditingController();
|
||||
TextEditingController yearController = TextEditingController();
|
||||
TextEditingController ownerController = TextEditingController();
|
||||
TextEditingController carOwnerWorkController = TextEditingController();
|
||||
TextEditingController driverNameController = TextEditingController();
|
||||
TextEditingController nationalIdController = TextEditingController();
|
||||
TextEditingController birthDateController = TextEditingController();
|
||||
TextEditingController licenseTypeController = TextEditingController();
|
||||
TextEditingController phoneController = TextEditingController();
|
||||
TextEditingController phoneCarController = TextEditingController();
|
||||
TextEditingController carNumberController = TextEditingController();
|
||||
TextEditingController manufactureYearController = TextEditingController();
|
||||
TextEditingController carModelController = TextEditingController();
|
||||
TextEditingController carTypeController = TextEditingController();
|
||||
TextEditingController siteCarController = TextEditingController();
|
||||
TextEditingController siteDriverController = TextEditingController();
|
||||
TextEditingController registrationDateController = TextEditingController();
|
||||
Map passengerData = {};
|
||||
Map driverData = {};
|
||||
List filteredDrivers = [];
|
||||
var color = ''.obs;
|
||||
var colorHex = ''.obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
// refreshDashboardStats(); // Removed to save data consumption at start
|
||||
}
|
||||
|
||||
Future<void> refreshDashboardStats() async {
|
||||
isLoading = true;
|
||||
update();
|
||||
try {
|
||||
await Future.wait<void>([
|
||||
getDriverWantCompleteRegistration(),
|
||||
getDriverNotCompleteRegistration(),
|
||||
getNewDriverRegister(),
|
||||
]);
|
||||
} catch (e) {
|
||||
Log.print('Error refreshing stats: $e');
|
||||
}
|
||||
isLoading = false;
|
||||
update();
|
||||
}
|
||||
|
||||
Future<void> searchPassengerByPhone() async {
|
||||
if (formKey.currentState == null || formKey.currentState!.validate()) {
|
||||
isLoading = true;
|
||||
update();
|
||||
await getPassengersByPhone();
|
||||
isLoading = false;
|
||||
update();
|
||||
Get.back();
|
||||
if (passengerData.isEmpty) {
|
||||
Get.snackbar('Error'.tr, 'Passenger not found'.tr,
|
||||
backgroundColor: Colors.red, colorText: Colors.white);
|
||||
return;
|
||||
}
|
||||
Get.to(() => PassengersPage());
|
||||
}
|
||||
}
|
||||
|
||||
void searchDrivers(String query) {
|
||||
if (query.isEmpty) {
|
||||
filteredDrivers = driverNotCompleteRegistration;
|
||||
update();
|
||||
} else {
|
||||
filteredDrivers = driverNotCompleteRegistration
|
||||
.where((driver) => driver['phone_number']
|
||||
.toString()
|
||||
.toLowerCase()
|
||||
.contains(query.toLowerCase()))
|
||||
.toList();
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
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',
|
||||
path: phoneNumber,
|
||||
);
|
||||
await launchUrl(launchUri);
|
||||
}
|
||||
|
||||
Future<void> launchCommunication(
|
||||
String method, String contactInfo, String message) async {
|
||||
// رقّم فقط (بدون + أو مسافات)
|
||||
final phone = contactInfo.replaceAll(RegExp(r'[^0-9]'), '');
|
||||
final encodedMsg = Uri.encodeComponent(message);
|
||||
|
||||
Uri? uri;
|
||||
|
||||
if (Platform.isIOS) {
|
||||
switch (method) {
|
||||
case 'phone':
|
||||
uri = Uri.parse('tel:$phone');
|
||||
break;
|
||||
case 'sms':
|
||||
uri = Uri.parse('sms:$phone?body=$encodedMsg');
|
||||
break;
|
||||
case 'whatsapp':
|
||||
uri = Uri.parse(
|
||||
'https://api.whatsapp.com/send?phone=$phone&text=$encodedMsg');
|
||||
break;
|
||||
case 'email':
|
||||
uri =
|
||||
Uri.parse('mailto:$contactInfo?subject=Subject&body=$encodedMsg');
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
} else if (Platform.isAndroid) {
|
||||
switch (method) {
|
||||
case 'phone':
|
||||
uri = Uri.parse('tel:$phone');
|
||||
break;
|
||||
case 'sms':
|
||||
uri = Uri.parse('sms:$phone?body=$encodedMsg');
|
||||
break;
|
||||
case 'whatsapp':
|
||||
{
|
||||
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';
|
||||
}
|
||||
}
|
||||
case 'email':
|
||||
uri =
|
||||
Uri.parse('mailto:$contactInfo?subject=Subject&body=$encodedMsg');
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
final ok = await canLaunchUrl(uri);
|
||||
if (ok) {
|
||||
await launchUrl(uri, mode: LaunchMode.externalApplication);
|
||||
} else {
|
||||
// ممكن تضيف Snackbar/Toast هنا
|
||||
}
|
||||
}
|
||||
|
||||
List driverNotCompleteRegistration = [];
|
||||
Future<void> getDriverNotCompleteRegistration() async {
|
||||
var res = await CRUD()
|
||||
.get(link: AppLink.getDriverNotCompleteRegistration, payload: {});
|
||||
if (res != 'failure') {
|
||||
var d = res['message'];
|
||||
driverNotCompleteRegistration = d;
|
||||
filteredDrivers = driverNotCompleteRegistration;
|
||||
update();
|
||||
} else {
|
||||
driverNotCompleteRegistration = [];
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> 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 = [];
|
||||
Future<void> getDriverWantCompleteRegistration() async {
|
||||
var res =
|
||||
await CRUD().get(link: AppLink.getDriversWaitingActive, payload: {});
|
||||
if (res != 'failure') {
|
||||
var d = res['message'];
|
||||
driverWantCompleteRegistration = d;
|
||||
filteredDrivers = driverWantCompleteRegistration;
|
||||
update();
|
||||
} else {
|
||||
driverWantCompleteRegistration = [];
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
List driversPhoneNotComplete = [];
|
||||
Future<void> getDriversPhoneNotComplete() async {
|
||||
var res =
|
||||
await CRUD().get(link: AppLink.getDriversPhoneNotComplete, payload: {});
|
||||
if (res != 'failure') {
|
||||
var d = res['message'];
|
||||
driverWantCompleteRegistration = d;
|
||||
filteredDrivers = driverWantCompleteRegistration;
|
||||
update();
|
||||
} else {
|
||||
Get.snackbar(res, '');
|
||||
}
|
||||
}
|
||||
|
||||
List newDriverRegister = [];
|
||||
Future<void> getNewDriverRegister() async {
|
||||
var res = await CRUD().get(link: AppLink.getNewDriverRegister, payload: {});
|
||||
if (res != 'failure') {
|
||||
var d = res['message'];
|
||||
newDriverRegister = d;
|
||||
update();
|
||||
} else {
|
||||
newDriverRegister = [];
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> addWelcomeCall(String driveId) async {
|
||||
var res = await CRUD().post(link: AppLink.addWelcomeDriverNote, payload: {
|
||||
"driverId": driveId,
|
||||
"notes": notesController.text,
|
||||
});
|
||||
if (res != 'failure') {
|
||||
Get.snackbar('Success'.tr, '', backgroundColor: AppColor.greenColor);
|
||||
}
|
||||
}
|
||||
|
||||
String selectedStatus = "I'm not ready yet".tr;
|
||||
List passengerNotCompleteRegistration = [];
|
||||
Future<void> getPassengerNotCompleteRegistration() async {
|
||||
var res = await CRUD()
|
||||
.get(link: AppLink.getPassengersNotCompleteRegistration, payload: {});
|
||||
if (res != 'failure') {
|
||||
var d = res['message'];
|
||||
passengerNotCompleteRegistration = d;
|
||||
update();
|
||||
} else {
|
||||
Get.snackbar(res, '');
|
||||
}
|
||||
}
|
||||
|
||||
void setSelectedStatus(String status) {
|
||||
selectedStatus = status;
|
||||
update();
|
||||
}
|
||||
|
||||
final List<String> statusOptions = [
|
||||
"I'm not ready yet".tr,
|
||||
"I don't have a suitable vehicle".tr,
|
||||
"I'll register when the app is fully launched".tr,
|
||||
"I need more help understanding the app".tr,
|
||||
"My documents have expired".tr,
|
||||
];
|
||||
|
||||
List carPlateNotEdit = [];
|
||||
|
||||
getCarPlateNotEdit() async {
|
||||
var res = await CRUD().get(link: AppLink.getCarPlateNotEdit, payload: {});
|
||||
if (res != 'failure') {
|
||||
var d = res['message'];
|
||||
carPlateNotEdit = d;
|
||||
update();
|
||||
} else {
|
||||
MyDialog().getDialog('No Car found yet'.tr, 'thanks'.tr, const SizedBox(),
|
||||
() {
|
||||
Get.back();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
List driverWithoutCar = [];
|
||||
getdriverWithoutCar() async {
|
||||
var res = await CRUD().get(link: AppLink.getdriverWithoutCar, payload: {});
|
||||
if (res != 'failure') {
|
||||
var d = res['message'];
|
||||
driverWithoutCar = d;
|
||||
update();
|
||||
} else {
|
||||
MyDialog().getDialog('No Car found yet'.tr, 'thanks'.tr, const SizedBox(),
|
||||
() {
|
||||
Get.back();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> addRegistrationCarEgyptHandling({
|
||||
required String driverId,
|
||||
required String carPlate,
|
||||
required String color,
|
||||
required String colorHex,
|
||||
required String year,
|
||||
required String make,
|
||||
required String model,
|
||||
required String expirationDate,
|
||||
required String owner,
|
||||
}) async {
|
||||
try {
|
||||
isLoading = true;
|
||||
update();
|
||||
|
||||
var payload = {
|
||||
'driverID': driverId,
|
||||
'vin': 'vin',
|
||||
'car_plate': carPlate,
|
||||
'make': make,
|
||||
'model': model,
|
||||
'year': year,
|
||||
'expiration_date': expirationDate,
|
||||
'color': color,
|
||||
'owner': owner,
|
||||
'color_hex': colorHex,
|
||||
'address': 'addressCar',
|
||||
'displacement': 'displacement',
|
||||
'fuel': 'fuel',
|
||||
'registration_date': '2024-09-06',
|
||||
};
|
||||
|
||||
Log.print('Payload: $payload');
|
||||
|
||||
var res =
|
||||
await CRUD().post(link: AppLink.addCartoDriver, payload: payload);
|
||||
|
||||
isLoading = false;
|
||||
update();
|
||||
|
||||
var status = res;
|
||||
Log.print('res: $res');
|
||||
Log.print('status: $status');
|
||||
|
||||
if (status != 'failure' && status['status'] == 'success') {
|
||||
await Future.wait([
|
||||
CRUD().post(
|
||||
link:
|
||||
'${AppLink.seferAlexandriaServer}/ride/RegisrationCar/add.php',
|
||||
payload: payload),
|
||||
CRUD().post(
|
||||
link: '${AppLink.seferGizaServer}/ride/RegisrationCar/add.php',
|
||||
payload: payload),
|
||||
]);
|
||||
|
||||
Get.snackbar('Success', 'Registration successful',
|
||||
backgroundColor: AppColor.greenColor);
|
||||
Get.back();
|
||||
} else {
|
||||
Log.print('Error: Unexpected status: ${status['status']}');
|
||||
Get.snackbar('Error', 'Registration failed',
|
||||
backgroundColor: Colors.red);
|
||||
}
|
||||
} catch (e) {
|
||||
Log.print('Error: $e');
|
||||
Get.snackbar('Error', 'An error occurred during registration',
|
||||
backgroundColor: Colors.red);
|
||||
}
|
||||
}
|
||||
|
||||
editCarPlateNotEdit(
|
||||
String driverId,
|
||||
String carPlate,
|
||||
String color,
|
||||
String colorHex,
|
||||
String year,
|
||||
String make,
|
||||
String model,
|
||||
String expirationDate,
|
||||
String owner,
|
||||
) async {
|
||||
var res = await CRUD().post(link: AppLink.editCarPlate, payload: {
|
||||
"driverId": driverId,
|
||||
"carPlate": carPlate,
|
||||
"color": color,
|
||||
"color_hex": colorHex,
|
||||
"make": make,
|
||||
"year": year,
|
||||
"model": model,
|
||||
"expiration_date": expirationDate.toString(),
|
||||
"owner": owner,
|
||||
"employee": storage.read(key: 'name').toString(),
|
||||
});
|
||||
Log.print('res: ${res}');
|
||||
if (res != 'failure') {
|
||||
// Get.snackbar(res, '', backgroundColor: AppColor.greenColor);
|
||||
Get.back();
|
||||
carplateController.clear();
|
||||
yearController.clear();
|
||||
makeController.clear();
|
||||
modelController.clear();
|
||||
ownerController.clear();
|
||||
|
||||
await getCarPlateNotEdit();
|
||||
update();
|
||||
} else {
|
||||
Get.snackbar(res, '', backgroundColor: AppColor.redColor);
|
||||
}
|
||||
}
|
||||
|
||||
// editCarPlateNotEdit(String driverId, carPlate) async {
|
||||
// var res = await CRUD().post(link: AppLink.editCarPlate, payload: {
|
||||
// "driverId": driverId,
|
||||
// "carPlate": carPlate,
|
||||
// });
|
||||
// if (res != 'failure') {
|
||||
// Get.snackbar(res, '', backgroundColor: AppColor.greenColor);
|
||||
// carplateController.clear();
|
||||
// await getCarPlateNotEdit();
|
||||
// update();
|
||||
// } else {
|
||||
// Get.snackbar(res, '', backgroundColor: AppColor.redColor);
|
||||
// }
|
||||
// }
|
||||
|
||||
saveNoteForDriverNotCompleteRegistration(String phone, editor, note) async {
|
||||
var res = await CRUD().post(
|
||||
link: AppLink.addNotesDriver,
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
saveNoteForPassengerNotCompleteRegistration(
|
||||
String phone, editor, note) async {
|
||||
var res = await CRUD().post(
|
||||
link: AppLink.addNotesPassenger,
|
||||
payload: {"phone": phone, "editor": editor, "note": note});
|
||||
if (res != 'failure') {
|
||||
Get.snackbar(res, '', backgroundColor: AppColor.greenColor);
|
||||
notesController.clear();
|
||||
} else {
|
||||
Get.snackbar(res, '', backgroundColor: AppColor.redColor);
|
||||
}
|
||||
}
|
||||
|
||||
searchDriverByPhone() async {
|
||||
if (formKey.currentState == null || formKey.currentState!.validate()) {
|
||||
isLoading = true;
|
||||
update();
|
||||
await getDriverByPhone();
|
||||
isLoading = false;
|
||||
update();
|
||||
Get.back();
|
||||
if (driverData.isEmpty) {
|
||||
Get.snackbar('Error'.tr, 'Driver not found'.tr,
|
||||
backgroundColor: Colors.red, colorText: Colors.white);
|
||||
return;
|
||||
}
|
||||
Get.to(() => DriverPage());
|
||||
}
|
||||
}
|
||||
|
||||
searchDriverByNational() async {
|
||||
if (formKey.currentState == null || formKey.currentState!.validate()) {
|
||||
isLoading = true;
|
||||
update();
|
||||
await getDriverByNational();
|
||||
isLoading = false;
|
||||
update();
|
||||
Get.back();
|
||||
if (driverData.isEmpty) {
|
||||
Get.snackbar('Error'.tr, 'Driver not found'.tr,
|
||||
backgroundColor: Colors.red, colorText: Colors.white);
|
||||
return;
|
||||
}
|
||||
Get.to(() => DriverPage());
|
||||
}
|
||||
}
|
||||
|
||||
getPassengersByPhone() async {
|
||||
var res = await CRUD().get(
|
||||
link: AppLink.getPassengersByPhone,
|
||||
payload: {"phone": passengerPhoneController.text});
|
||||
|
||||
if (res != 'failure') {
|
||||
var d = res;
|
||||
passengerData = d;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
getDriverByPhone() async {
|
||||
var res = await CRUD().get(
|
||||
link: AppLink.getDriverByPhone,
|
||||
payload: {"phone": driverPhoneController.text});
|
||||
|
||||
if (res != 'failure') {
|
||||
var d = 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 = res;
|
||||
driverData = d;
|
||||
update();
|
||||
} else {
|
||||
Get.snackbar('Error', 'Driver not found', backgroundColor: Colors.red);
|
||||
}
|
||||
}
|
||||
}
|
||||
378
siro_service/lib/controller/mainController/pages/add_car.dart
Normal file
378
siro_service/lib/controller/mainController/pages/add_car.dart
Normal file
@@ -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'),
|
||||
subtitle: Text(
|
||||
'Phone: ${(driver['phone']) ?? 'N/A'}'),
|
||||
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),
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
: const Center(
|
||||
child: Text('No drivers available.'),
|
||||
);
|
||||
})
|
||||
],
|
||||
isleading: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.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'),
|
||||
subtitle: Text(
|
||||
'Phone: ${(driver['phone']) ?? 'N/A'}'),
|
||||
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),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
)
|
||||
: const Center(
|
||||
child: Text('No drivers available.'),
|
||||
);
|
||||
}),
|
||||
],
|
||||
)
|
||||
],
|
||||
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 {
|
||||
Get.snackbar('error', '', backgroundColor: AppColor.redColor);
|
||||
}
|
||||
}
|
||||
|
||||
@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: []);
|
||||
}
|
||||
}
|
||||
13071
siro_service/lib/controller/mainController/pages/contact_page.dart
Normal file
13071
siro_service/lib/controller/mainController/pages/contact_page.dart
Normal file
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 $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: [
|
||||
_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,223 @@
|
||||
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 'registration_captain_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 registration form
|
||||
buildActionButton(
|
||||
icon: CupertinoIcons
|
||||
.pencil_ellipsis_rectangle,
|
||||
color: AppColor.gold,
|
||||
onPressed: () {
|
||||
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']);
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
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),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
334
siro_service/lib/controller/mainController/pages/edit_car.dart
Normal file
334
siro_service/lib/controller/mainController/pages/edit_car.dart
Normal file
@@ -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'),
|
||||
subtitle: Text(
|
||||
'Phone: ${(driver['phone']) ?? 'N/A'}'),
|
||||
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),
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
: const Center(
|
||||
child: Text('No drivers available.'),
|
||||
);
|
||||
})
|
||||
],
|
||||
isleading: true,
|
||||
);
|
||||
}
|
||||
}
|
||||
485
siro_service/lib/controller/mainController/pages/new_driver.dart
Normal file
485
siro_service/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: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,
|
||||
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: ${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',
|
||||
),
|
||||
),
|
||||
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,146 @@
|
||||
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';
|
||||
|
||||
class PassengersPage extends StatelessWidget {
|
||||
PassengersPage({super.key});
|
||||
final MainController mainController = MainController();
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
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 _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 _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(
|
||||
padding: const EdgeInsets.all(16),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Text(title,
|
||||
style:
|
||||
const TextStyle(fontSize: 18, fontWeight: FontWeight.bold)),
|
||||
const SizedBox(height: 16),
|
||||
...children,
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
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)),
|
||||
Flexible(child: Text(displayValue, overflow: TextOverflow.ellipsis)),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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,223 @@
|
||||
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/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 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);
|
||||
},
|
||||
),
|
||||
);
|
||||
}),
|
||||
],
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
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),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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:siro_service/constant/box_name.dart';
|
||||
import 'package:siro_service/constant/links.dart';
|
||||
import 'package:siro_service/controller/functions/crud.dart';
|
||||
import 'package:siro_service/main.dart';
|
||||
import 'package:siro_service/print.dart';
|
||||
import 'package:siro_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 = 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' && decodedResponse['status'] == 'success') {
|
||||
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 is Map ? decodedResponse['message'] : 'failure'}'.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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,338 @@
|
||||
import 'dart:convert';
|
||||
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/box_name.dart';
|
||||
import 'package:siro_service/constant/links.dart';
|
||||
import 'package:siro_service/controller/functions/crud.dart';
|
||||
import 'package:siro_service/main.dart';
|
||||
import 'package:siro_service/print.dart';
|
||||
import 'package:siro_service/views/widgets/my_dialog.dart';
|
||||
|
||||
import 'pages/registration_captain_page.dart';
|
||||
|
||||
class RegisterCaptainController 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 = 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;
|
||||
|
||||
Map<String, dynamic> dataToSend = {
|
||||
// Driver fields from all pages
|
||||
'driverId': driverId.value,
|
||||
'phone': phone.value,
|
||||
'first_name': firstNameController.text,
|
||||
'last_name': lastNameController.text,
|
||||
'site': siteController.text,
|
||||
'national_number': nationalNumberController.text,
|
||||
'gender': selectedGender.value,
|
||||
'birthdate': '2000-01-01', //birthdateController.text,
|
||||
'license_categories': licenseCategoriesController.text,
|
||||
'expiry_date': expiryDateController.text,
|
||||
'license_issue_date': licenseIssueDateController.text,
|
||||
|
||||
// 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.updateDriverToActive, payload: dataToSend)
|
||||
.then((response) {
|
||||
isLoading.value = false;
|
||||
var decodedResponse = (response);
|
||||
Log.print('decodedResponse: ${decodedResponse}');
|
||||
if (decodedResponse != 'failure' && decodedResponse['status'] == 'success') {
|
||||
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 is Map ? decodedResponse['message'] : 'failure'}'.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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user