25-8-9-1
This commit is contained in:
@@ -79,12 +79,14 @@ class LoginDriverController extends GetxController {
|
||||
var res = await CRUD().get(
|
||||
link: AppLink.getTesterApp,
|
||||
payload: {'appPlatform': AppInformation.appName});
|
||||
Log.print('res: ${res}');
|
||||
if (res != 'failure') {
|
||||
var d = jsonDecode(res);
|
||||
isTest = d['message'][0]['isTest'];
|
||||
Log.print('isTest: ${isTest}');
|
||||
box.write(BoxName.isTest, isTest);
|
||||
|
||||
Log.print('isTest: ${box.read(BoxName.isTest)}');
|
||||
// Log.print('isTest: ${box.read(BoxName.isTest)}');
|
||||
update();
|
||||
} else {
|
||||
isTest = 0;
|
||||
@@ -164,22 +166,22 @@ class LoginDriverController extends GetxController {
|
||||
|
||||
getJWT() async {
|
||||
dev = Platform.isAndroid ? 'android' : 'ios';
|
||||
Log.print(
|
||||
'box.read(BoxName.firstTimeLoadKey): ${box.read(BoxName.firstTimeLoadKey)}');
|
||||
// Log.print(
|
||||
// 'box.read(BoxName.firstTimeLoadKey): ${box.read(BoxName.firstTimeLoadKey)}');
|
||||
if (box.read(BoxName.firstTimeLoadKey).toString() != 'false') {
|
||||
var payload = {
|
||||
'id': box.read(BoxName.driverID) ?? AK.newId,
|
||||
'password': AK.passnpassenger,
|
||||
'aud': '${AK.allowed}$dev',
|
||||
};
|
||||
Log.print('payload: ${payload}');
|
||||
// Log.print('payload: ${payload}');
|
||||
|
||||
var response0 = await http.post(
|
||||
Uri.parse(AppLink.loginFirstTimeDriver),
|
||||
body: payload,
|
||||
);
|
||||
Log.print('response0: ${response0.body}');
|
||||
Log.print('request: ${response0.request}');
|
||||
// Log.print('response0: ${response0.body}');
|
||||
// Log.print('request: ${response0.request}');
|
||||
if (response0.statusCode == 200) {
|
||||
final decodedResponse1 = jsonDecode(response0.body);
|
||||
// Log.print('decodedResponse1: ${decodedResponse1}');
|
||||
@@ -207,13 +209,13 @@ class LoginDriverController extends GetxController {
|
||||
'password': box.read(BoxName.emailDriver),
|
||||
'aud': '${AK.allowed}$dev',
|
||||
};
|
||||
print(payload);
|
||||
// print(payload);
|
||||
var response1 = await http.post(
|
||||
Uri.parse(AppLink.loginJwtDriver),
|
||||
body: payload,
|
||||
);
|
||||
print(response1.request);
|
||||
print(response1.body);
|
||||
// print(response1.request);
|
||||
// print(response1.body);
|
||||
|
||||
if (response1.statusCode == 200) {
|
||||
final decodedResponse1 = jsonDecode(response1.body);
|
||||
@@ -265,7 +267,7 @@ class LoginDriverController extends GetxController {
|
||||
isloading = true;
|
||||
update();
|
||||
await SecurityHelper.performSecurityChecks();
|
||||
Log.print('(BoxName.emailDriver): ${box.read(BoxName.emailDriver)}');
|
||||
// Log.print('(BoxName.emailDriver): ${box.read(BoxName.emailDriver)}');
|
||||
|
||||
var res = await CRUD().get(link: AppLink.loginFromGoogleCaptin, payload: {
|
||||
'email': email ?? 'yet',
|
||||
@@ -329,8 +331,8 @@ class LoginDriverController extends GetxController {
|
||||
String fingerPrint = await DeviceHelper.getDeviceFingerprint();
|
||||
await storage.write(
|
||||
key: BoxName.fingerPrint, value: fingerPrint.toString());
|
||||
print(jsonDecode(token)['data'][0]['token'].toString());
|
||||
print(box.read(BoxName.tokenDriver).toString());
|
||||
// print(jsonDecode(token)['data'][0]['token'].toString());
|
||||
// print(box.read(BoxName.tokenDriver).toString());
|
||||
if (email == '962798583052@intaleqapp.com') {
|
||||
} else {
|
||||
if (token != 'failure') {
|
||||
@@ -379,7 +381,7 @@ class LoginDriverController extends GetxController {
|
||||
isloading = true;
|
||||
update();
|
||||
await SecurityHelper.performSecurityChecks();
|
||||
Log.print('(BoxName.emailDriver): ${box.read(BoxName.emailDriver)}');
|
||||
// Log.print('(BoxName.emailDriver): ${box.read(BoxName.emailDriver)}');
|
||||
|
||||
var res = await CRUD().get(link: AppLink.loginFromGoogleCaptin, payload: {
|
||||
'email': email ?? 'yet',
|
||||
|
||||
365
lib/controller/auth/syria/registration_controller.dart
Normal file
365
lib/controller/auth/syria/registration_controller.dart
Normal file
@@ -0,0 +1,365 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:image_picker/image_picker.dart';
|
||||
import 'package:image_cropper/image_cropper.dart';
|
||||
import 'package:image/image.dart' as img;
|
||||
import '../../../constant/box_name.dart';
|
||||
import 'package:path_provider/path_provider.dart';
|
||||
// --- Final Submission ---
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:get/get.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:http_parser/http_parser.dart';
|
||||
import 'package:mime/mime.dart';
|
||||
import 'package:path/path.dart' as p;
|
||||
import '../../../constant/colors.dart';
|
||||
import '../../../constant/info.dart';
|
||||
import '../../../main.dart';
|
||||
import '../../functions/crud.dart';
|
||||
import '../../functions/encrypt_decrypt.dart';
|
||||
|
||||
// You can create a simple enum to manage image types
|
||||
enum ImageType {
|
||||
driverLicenseFront,
|
||||
driverLicenseBack,
|
||||
carLicenseFront,
|
||||
carLicenseBack,
|
||||
}
|
||||
|
||||
class RegistrationController extends GetxController {
|
||||
// Page Controller for managing steps
|
||||
late PageController pageController;
|
||||
var currentPage =
|
||||
0.obs; // Use .obs for reactive updates on the step indicator
|
||||
|
||||
// Loading state
|
||||
var isLoading = false.obs;
|
||||
String? colorHex; // سيُملى من الدروب داون
|
||||
// Form Keys for validation
|
||||
final driverInfoFormKey = GlobalKey<FormState>();
|
||||
final carInfoFormKey = GlobalKey<FormState>();
|
||||
|
||||
// STEP 1: Driver Information Controllers
|
||||
final firstNameController = TextEditingController();
|
||||
final lastNameController = TextEditingController();
|
||||
final nationalIdController = TextEditingController();
|
||||
final phoneController = TextEditingController(); // You can pre-fill this
|
||||
final driverLicenseExpiryController = TextEditingController();
|
||||
DateTime? driverLicenseExpiryDate;
|
||||
|
||||
// STEP 2: Car Information Controllers
|
||||
final carPlateController = TextEditingController();
|
||||
final carMakeController = TextEditingController();
|
||||
final carModelController = TextEditingController();
|
||||
final carYearController = TextEditingController();
|
||||
final carColorController = TextEditingController();
|
||||
final carVinController = TextEditingController(); // Chassis number
|
||||
final carRegistrationExpiryController = TextEditingController();
|
||||
DateTime? carRegistrationExpiryDate;
|
||||
|
||||
// STEP 3: Document Uploads
|
||||
File? driverLicenseFrontImage;
|
||||
File? driverLicenseBackImage;
|
||||
File? carLicenseFrontImage;
|
||||
File? carLicenseBackImage;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
pageController = PageController();
|
||||
// Pre-fill phone number if it exists in storage
|
||||
// phoneController.text = box.read(BoxName.phoneDriver) ?? '';
|
||||
}
|
||||
|
||||
@override
|
||||
void onClose() {
|
||||
pageController.dispose();
|
||||
// Dispose all other text controllers
|
||||
super.onClose();
|
||||
}
|
||||
|
||||
// --- Page Navigation ---
|
||||
void goToNextStep() {
|
||||
bool isValid = false;
|
||||
if (currentPage.value == 0) {
|
||||
// Validate Step 1
|
||||
isValid = driverInfoFormKey.currentState!.validate();
|
||||
if (isValid) {
|
||||
// Optional: Check if license is expired
|
||||
if (driverLicenseExpiryDate != null &&
|
||||
driverLicenseExpiryDate!.isBefore(DateTime.now())) {
|
||||
Get.snackbar('Expired License', 'Your driver’s license has expired.',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white);
|
||||
return; // Stop progression
|
||||
}
|
||||
}
|
||||
} else if (currentPage.value == 1) {
|
||||
// Validate Step 2
|
||||
isValid = carInfoFormKey.currentState!.validate();
|
||||
}
|
||||
|
||||
if (isValid) {
|
||||
pageController.nextPage(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeIn,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void goToPreviousStep() {
|
||||
pageController.previousPage(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
curve: Curves.easeIn,
|
||||
);
|
||||
}
|
||||
|
||||
// --- Image Picking ---
|
||||
Future<void> pickImage(ImageType type) async {
|
||||
try {
|
||||
final picker = ImagePicker();
|
||||
final picked = await picker.pickImage(
|
||||
source: ImageSource.camera,
|
||||
imageQuality: 95, // جودة أولية من الكاميرا
|
||||
maxWidth: 3000, // نسمح بصورة كبيرة ثم نصغّر نحن
|
||||
);
|
||||
if (picked == null) return;
|
||||
|
||||
// قصّ الصورة
|
||||
final cropped = await ImageCropper().cropImage(
|
||||
sourcePath: picked.path,
|
||||
uiSettings: [
|
||||
AndroidUiSettings(
|
||||
toolbarTitle: 'Cropper'.tr,
|
||||
toolbarColor: AppColor.accentColor,
|
||||
toolbarWidgetColor: AppColor.redColor,
|
||||
initAspectRatio: CropAspectRatioPreset.original,
|
||||
lockAspectRatio: false,
|
||||
),
|
||||
IOSUiSettings(title: 'Cropper'.tr),
|
||||
],
|
||||
);
|
||||
if (cropped == null) return; // المستخدم ألغى
|
||||
|
||||
// قراءة bytes + التصحيح حسب EXIF ثم التصغير
|
||||
final rawBytes = await File(cropped.path).readAsBytes();
|
||||
final decoded = img.decodeImage(rawBytes);
|
||||
if (decoded == null) throw Exception('Decode image failed');
|
||||
|
||||
// تصحيح اتجاه الصورة (EXIF)
|
||||
final fixed = img.bakeOrientation(decoded);
|
||||
|
||||
// تصغير لعرض 800px (عدّل عند الحاجة)
|
||||
final resized = img.copyResize(fixed, width: 800);
|
||||
|
||||
// حفظ مؤقت بصيغة JPG
|
||||
final tmpDir = await getTemporaryDirectory();
|
||||
final outPath =
|
||||
'${tmpDir.path}/doc_${DateTime.now().millisecondsSinceEpoch}.jpg';
|
||||
final outFile = File(outPath);
|
||||
await outFile.writeAsBytes(img.encodeJpg(resized, quality: 85));
|
||||
|
||||
// عيّن الملف في المتغير الصحيح حسب النوع
|
||||
if (outFile != null) {
|
||||
switch (type) {
|
||||
case ImageType.driverLicenseFront:
|
||||
driverLicenseFrontImage = File(outFile.path);
|
||||
break;
|
||||
case ImageType.driverLicenseBack:
|
||||
driverLicenseBackImage = File(outFile.path);
|
||||
break;
|
||||
case ImageType.carLicenseFront:
|
||||
carLicenseFrontImage = File(outFile.path);
|
||||
break;
|
||||
case ImageType.carLicenseBack:
|
||||
carLicenseBackImage = File(outFile.path);
|
||||
break;
|
||||
}
|
||||
update(); // Use update() to refresh the GetBuilder UI
|
||||
}
|
||||
|
||||
update(); // لتحديث الـ UI
|
||||
|
||||
// // الإرسال للذكاء الاصطناعي
|
||||
// await sendToAI(type, imageFile: outFile);
|
||||
} catch (e) {
|
||||
Get.snackbar('Error'.tr, '${'An unexpected error occurred:'.tr} $e');
|
||||
}
|
||||
}
|
||||
|
||||
// ثابت: 20 لون سيارة شائع
|
||||
static const List<Map<String, String>> kCarColorOptions = [
|
||||
{'key': 'color.white', 'hex': '#FFFFFF'},
|
||||
{'key': 'color.black', 'hex': '#000000'},
|
||||
{'key': 'color.silver', 'hex': '#C0C0C0'},
|
||||
{'key': 'color.gray', 'hex': '#808080'},
|
||||
{'key': 'color.gunmetal', 'hex': '#2A3439'},
|
||||
{'key': 'color.red', 'hex': '#C62828'},
|
||||
{'key': 'color.blue', 'hex': '#1565C0'},
|
||||
{'key': 'color.navy', 'hex': '#0D47A1'},
|
||||
{'key': 'color.green', 'hex': '#2E7D32'},
|
||||
{'key': 'color.darkGreen', 'hex': '#1B5E20'},
|
||||
{'key': 'color.beige', 'hex': '#D7CCC8'},
|
||||
{'key': 'color.brown', 'hex': '#5D4037'},
|
||||
{'key': 'color.maroon', 'hex': '#800000'},
|
||||
{'key': 'color.burgundy', 'hex': '#800020'},
|
||||
{'key': 'color.yellow', 'hex': '#F9A825'},
|
||||
{'key': 'color.orange', 'hex': '#EF6C00'},
|
||||
{'key': 'color.gold', 'hex': '#D4AF37'},
|
||||
{'key': 'color.bronze', 'hex': '#CD7F32'},
|
||||
{'key': 'color.champagne', 'hex': '#EFE1C6'},
|
||||
{'key': 'color.purple', 'hex': '#6A1B9A'},
|
||||
];
|
||||
|
||||
Color hexToColor(String hex) {
|
||||
var v = hex.replaceAll('#', '');
|
||||
if (v.length == 6) v = 'FF$v';
|
||||
return Color(int.parse(v, radix: 16));
|
||||
}
|
||||
|
||||
Future<void> submitRegistration() async {
|
||||
// 1) تحقق من الصور
|
||||
if (driverLicenseFrontImage == null ||
|
||||
driverLicenseBackImage == null ||
|
||||
carLicenseFrontImage == null ||
|
||||
carLicenseBackImage == null) {
|
||||
Get.snackbar(
|
||||
'Missing Documents'.tr, 'Please upload all 4 required documents.'.tr,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.orange,
|
||||
colorText: Colors.white);
|
||||
return;
|
||||
}
|
||||
|
||||
isLoading.value = true;
|
||||
|
||||
final uri = Uri.parse(
|
||||
'https://intaleq.xyz/intaleq/auth/syria/driver/register_driver_and_car.php',
|
||||
);
|
||||
|
||||
final client = http.Client();
|
||||
try {
|
||||
final req = http.MultipartRequest('POST', uri);
|
||||
|
||||
// مهم: لا تضع Content-Type يدويًا، الـ MultipartRequest يتكفّل فيه ببناء boundary.
|
||||
final headers = {
|
||||
'Authorization':
|
||||
'Bearer ${r(box.read(BoxName.jwt)).split(AppInformation.addd)[0]}',
|
||||
'X-HMAC-Auth': '${box.read(BoxName.hmac)}',
|
||||
};
|
||||
// 2) الحقول النصية
|
||||
final fields = <String, String>{};
|
||||
|
||||
// --- Driver Data ---
|
||||
_addField(fields, 'id', box.read(BoxName.driverID)?.toString());
|
||||
_addField(fields, 'first_name', firstNameController.text);
|
||||
_addField(fields, 'last_name', lastNameController.text);
|
||||
_addField(fields, 'phone', box.read(BoxName.phoneDriver) ?? '');
|
||||
_addField(fields, 'national_number', nationalIdController.text);
|
||||
_addField(fields, 'expiry_date', driverLicenseExpiryController.text);
|
||||
_addField(
|
||||
fields, 'password', 'generate_your_password_here'); // عدّل حسب منطقك
|
||||
_addField(fields, 'status', 'yet');
|
||||
_addField(fields, 'email',
|
||||
'Not specified'); // سكربت السيرفر سيحوّلها null ويبني ايميل افتراضي
|
||||
_addField(fields, 'gender', 'Male');
|
||||
|
||||
// --- Car Data (مطابقة لما يتوقّعه السكربت) ---
|
||||
_addField(fields, 'vin', 'carVinController.text);');
|
||||
_addField(fields, 'car_plate', carPlateController.text);
|
||||
_addField(fields, 'make', carMakeController.text);
|
||||
_addField(fields, 'model', carModelController.text);
|
||||
_addField(fields, 'year', carYearController.text);
|
||||
_addField(fields, 'expiration_date', 'carRegistrationExpiryController');
|
||||
_addField(fields, 'color', carColorController.text);
|
||||
_addField(fields, 'fuel', 'Gasoline'); // أو حسب اختيارك
|
||||
_addField(fields, 'color_hex', colorHex); // مهم
|
||||
// لو عندك حقول إضافية مطلوبة بالسكربت (مالك المركبة / الكود اللوني / الوقود) مرّرها:
|
||||
_addField(fields, 'owner',
|
||||
firstNameController.text + ' ' + lastNameController.text);
|
||||
// if (colorHex != null) _addField(fields, 'color_hex', colorHex);
|
||||
// if (fuelType != null) _addField(fields, 'fuel', fuelType);
|
||||
req.headers.addAll(headers);
|
||||
req.fields.addAll(fields);
|
||||
|
||||
// 3) الملفات (4 صور) — مفاتيحها مطابقة للسكربت
|
||||
Future<void> addFile(String field, File file) async {
|
||||
final mime = lookupMimeType(file.path) ?? 'image/jpeg';
|
||||
final parts = mime.split('/');
|
||||
final mediaType = MediaType(parts.first, parts.last);
|
||||
req.files.add(
|
||||
await http.MultipartFile.fromPath(
|
||||
field,
|
||||
file.path,
|
||||
filename: p.basename(file.path),
|
||||
contentType: mediaType,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
await addFile('driver_license_front', driverLicenseFrontImage!);
|
||||
await addFile('driver_license_back', driverLicenseBackImage!);
|
||||
await addFile('car_license_front', carLicenseFrontImage!);
|
||||
await addFile('car_license_back', carLicenseBackImage!);
|
||||
|
||||
// (اختياري) هيدر للقبول بـ JSON
|
||||
|
||||
// 4) الإرسال
|
||||
final streamed =
|
||||
await client.send(req).timeout(const Duration(seconds: 60));
|
||||
final resp = await http.Response.fromStream(streamed);
|
||||
|
||||
// 5) فحص النتيجة
|
||||
Map<String, dynamic>? json;
|
||||
try {
|
||||
json = jsonDecode(resp.body) as Map<String, dynamic>;
|
||||
} catch (_) {}
|
||||
|
||||
if (resp.statusCode == 200 &&
|
||||
json != null &&
|
||||
json['status'] == 'success') {
|
||||
// ممكن يرجّع driverID, carRegID, documents
|
||||
final driverID =
|
||||
(json['data']?['driverID'] ?? json['driverID'])?.toString();
|
||||
if (driverID != null && driverID.isNotEmpty) {
|
||||
box.write(BoxName.driverID, driverID);
|
||||
}
|
||||
|
||||
Get.snackbar('Success'.tr, 'Registration completed successfully!'.tr,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.green,
|
||||
colorText: Colors.white);
|
||||
|
||||
// TODO: انتقل للصفحة التالية أو حدّث الحالة…
|
||||
} else {
|
||||
final msg =
|
||||
(json?['message'] ?? 'Registration failed. Please try again.')
|
||||
.toString();
|
||||
Get.snackbar('Error'.tr, msg,
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white);
|
||||
}
|
||||
} catch (e) {
|
||||
Get.snackbar('Error'.tr, '${'An unexpected error occurred:'.tr} $e',
|
||||
snackPosition: SnackPosition.BOTTOM,
|
||||
backgroundColor: Colors.red,
|
||||
colorText: Colors.white);
|
||||
} finally {
|
||||
client.close();
|
||||
isLoading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Helpers
|
||||
void _addField(Map<String, String> fields, String key, String? value) {
|
||||
if (value != null && value.toString().trim().isNotEmpty) {
|
||||
fields[key] = value.toString().trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user