Update: 2026-06-27 04:21:17
This commit is contained in:
@@ -583,13 +583,14 @@ $pwdHashed = password_hash($rawSecret, PASSWORD_DEFAULT);
|
||||
if (isset($con) && $con instanceof PDO && $con->inTransaction()) {
|
||||
$con->rollBack();
|
||||
}
|
||||
error_log("register_driver_and_car ERROR: " . $e->getMessage());
|
||||
jsonError("Server error");
|
||||
$msg = $e->getMessage();
|
||||
error_log("register_driver_and_car ERROR: " . $msg);
|
||||
jsonError("Server error: $msg", 400);
|
||||
} catch (PDOException $e) {
|
||||
if (isset($con) && $con instanceof PDO && $con->inTransaction()) {
|
||||
$con->rollBack();
|
||||
}
|
||||
error_log("register_driver_and_car PDO: " . $e->getMessage());
|
||||
jsonError("Database error.");
|
||||
jsonError("Database error: " . $e->getMessage());
|
||||
}
|
||||
?>
|
||||
@@ -43,7 +43,13 @@ class KazanController extends GetxController {
|
||||
if (decoded['status'] == "success") {
|
||||
var message = decoded['message'];
|
||||
if (message is List && message.isNotEmpty) {
|
||||
kazanData.value = Map<String, dynamic>.from(message[0]);
|
||||
final match = message.cast<Map<String, dynamic>>().firstWhere(
|
||||
(row) =>
|
||||
(row['country'] ?? '').toString().toLowerCase() ==
|
||||
countryParam.toLowerCase(),
|
||||
orElse: () => message[0] as Map<String, dynamic>,
|
||||
);
|
||||
kazanData.value = Map<String, dynamic>.from(match);
|
||||
kazanData['country'] = selectedCountry.value;
|
||||
} else {
|
||||
kazanData.value = {'country': selectedCountry.value};
|
||||
|
||||
@@ -102,6 +102,13 @@ class LoginDriverController extends GetxController {
|
||||
// }
|
||||
|
||||
isPhoneVerified() async {
|
||||
// If the phone is already verified locally and we have a driver ID, skip the API check
|
||||
// This prevents asking for OTP again if the user restarts the app during registration.
|
||||
if (box.read(BoxName.phoneVerified) == '1' && box.read(BoxName.driverID) != null) {
|
||||
Get.offAll(() => RegistrationView());
|
||||
return;
|
||||
}
|
||||
|
||||
var res = await CRUD().post(
|
||||
link: AppLink.isPhoneVerified,
|
||||
payload: {'phone_number': box.read(BoxName.phoneDriver)});
|
||||
|
||||
@@ -371,9 +371,15 @@ class RegistrationController extends GetxController {
|
||||
|
||||
final _jwt = box.read(BoxName.jwt);
|
||||
final String _token = _jwt != null ? r(_jwt).split(AppInformation.addd)[0] : '';
|
||||
|
||||
String timestamp = DateTime.now().millisecondsSinceEpoch.toString();
|
||||
String nonce = timestamp;
|
||||
|
||||
final headers = <String, String>{
|
||||
'Authorization': 'Bearer $_token',
|
||||
'X-HMAC-Auth': '${box.read(BoxName.hmac)}',
|
||||
'X-Device-FP': box.read(BoxName.deviceFingerprint)?.toString() ?? '',
|
||||
'X-Timestamp': timestamp,
|
||||
'X-Nonce': nonce,
|
||||
};
|
||||
request.headers.addAll(headers);
|
||||
|
||||
@@ -472,7 +478,7 @@ class RegistrationController extends GetxController {
|
||||
final req = http.MultipartRequest('POST', syrianUploadUri);
|
||||
req.headers.addAll({
|
||||
'Authorization': authHeader,
|
||||
'X-HMAC-Auth': hmacHeader,
|
||||
'X-Device-FP': box.read(BoxName.deviceFingerprint)?.toString() ?? '',
|
||||
});
|
||||
|
||||
req.fields['driver_id'] = driverId;
|
||||
@@ -603,11 +609,14 @@ class RegistrationController extends GetxController {
|
||||
_addField(fields, 'expiry_date', driverLicenseExpiryController.text);
|
||||
_addField(fields, 'password', 'generated_password_or_token');
|
||||
_addField(fields, 'status', 'yet');
|
||||
_addField(fields, 'email', 'Not specified');
|
||||
|
||||
String generatedEmail = 'driver_${box.read(BoxName.driverID) ?? DateTime.now().millisecondsSinceEpoch}@siromove.com';
|
||||
_addField(fields, 'email', generatedEmail);
|
||||
_addField(fields, 'gender', 'Male'); // يفضل ربطها بـ Dropdown أيضاً
|
||||
_addField(fields, 'country', box.read(BoxName.countryCode)?.toString() ?? 'Jordan');
|
||||
|
||||
// --- Car Data ---
|
||||
_addField(fields, 'vin', 'yet');
|
||||
_addField(fields, 'vin', carVinController.text.isNotEmpty ? carVinController.text : 'yet');
|
||||
_addField(fields, 'car_plate', carPlateController.text);
|
||||
_addField(fields, 'make', carMakeController.text);
|
||||
_addField(fields, 'model', carModelController.text);
|
||||
@@ -615,8 +624,9 @@ class RegistrationController extends GetxController {
|
||||
_addField(
|
||||
fields,
|
||||
'expiration_date',
|
||||
driverLicenseExpiryController
|
||||
.text); // تأكد من أن هذا تاريخ انتهاء السيارة وليس الرخصة
|
||||
carRegistrationExpiryController.text.isNotEmpty
|
||||
? carRegistrationExpiryController.text
|
||||
: driverLicenseExpiryController.text);
|
||||
_addField(
|
||||
fields,
|
||||
'color',
|
||||
@@ -668,8 +678,8 @@ class RegistrationController extends GetxController {
|
||||
_addField(fields, 'id_front', idFrontUrl);
|
||||
_addField(fields, 'id_back', idBackUrl);
|
||||
_addField(fields, 'driver_license', driverLicenseUrl);
|
||||
if (isSyria)
|
||||
_addField(fields, 'driver_license_back', driverLicenseBackUrl);
|
||||
// Always add the key to avoid PHP undefined index error
|
||||
fields['driver_license_back'] = driverLicenseBackUrl ?? '';
|
||||
_addField(fields, 'profile_picture', profilePicUrl);
|
||||
_addField(fields, 'criminal_record', criminalRecordUrl);
|
||||
_addField(fields, 'car_license_front', carFrontUrl);
|
||||
@@ -677,6 +687,12 @@ class RegistrationController extends GetxController {
|
||||
|
||||
req.fields.addAll(fields);
|
||||
|
||||
Log.print('--- 🚀 Registration Request Payload ---');
|
||||
req.fields.forEach((key, value) {
|
||||
Log.print('[$key]: $value');
|
||||
});
|
||||
Log.print('---------------------------------------');
|
||||
|
||||
// 3) الإرسال
|
||||
final streamed =
|
||||
await client.send(req).timeout(const Duration(seconds: 60));
|
||||
|
||||
@@ -400,7 +400,8 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
|
||||
|
||||
if (savedTrip != null && savedTrip.isNotEmpty) {
|
||||
if (Get.currentRoute == '/') {
|
||||
print('⏳ App is still on Splash screen. Postponing notification trip navigation to HomeCaptainController.');
|
||||
print(
|
||||
'⏳ App is still on Splash screen. Postponing notification trip navigation to HomeCaptainController.');
|
||||
return;
|
||||
}
|
||||
await storage.delete(key: 'pending_driver_list');
|
||||
@@ -425,7 +426,8 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
|
||||
// ==============================================================================
|
||||
Future<void> _processAcceptOrder(List<dynamic> data) async {
|
||||
if (_isProcessingAccept) {
|
||||
print("⏳ _processAcceptOrder: Already accepting order, skipping duplicate request.");
|
||||
print(
|
||||
"⏳ _processAcceptOrder: Already accepting order, skipping duplicate request.");
|
||||
return;
|
||||
}
|
||||
_isProcessingAccept = true;
|
||||
|
||||
@@ -450,36 +450,83 @@ class RegistrationView extends StatelessWidget {
|
||||
child: SizedBox(
|
||||
height: 150,
|
||||
width: double.infinity,
|
||||
child: (img != null && img.isNotEmpty)
|
||||
? Image.network(
|
||||
img,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.broken_image, size: 40, color: Colors.red),
|
||||
const SizedBox(height: 8),
|
||||
Text('Image not available',
|
||||
style: TextStyle(color: Colors.red[700])),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
)
|
||||
: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.camera_alt_outlined,
|
||||
size: 40, color: Colors.grey[600]),
|
||||
const SizedBox(height: 8),
|
||||
Text(title, style: TextStyle(color: Colors.grey[700])),
|
||||
Text('Tap to upload'.tr,
|
||||
style:
|
||||
const TextStyle(color: Colors.grey, fontSize: 12)),
|
||||
],
|
||||
child: Stack(
|
||||
children: [
|
||||
// Card template background
|
||||
Container(
|
||||
width: double.infinity,
|
||||
height: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.grey[100],
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
border: Border.all(
|
||||
color: Colors.grey[300]!,
|
||||
width: 1.5,
|
||||
strokeAlign: BorderSide.strokeAlignInside,
|
||||
),
|
||||
),
|
||||
child: Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.credit_card, size: 48, color: Colors.grey[300]),
|
||||
const SizedBox(height: 8),
|
||||
Text(
|
||||
title,
|
||||
style: TextStyle(
|
||||
color: Colors.grey[400],
|
||||
fontSize: 13,
|
||||
fontWeight: FontWeight.w500,
|
||||
),
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
// Uploaded image with 50% opacity
|
||||
if (img != null && img.isNotEmpty)
|
||||
Positioned.fill(
|
||||
child: Opacity(
|
||||
opacity: 0.5,
|
||||
child: ClipRRect(
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
child: Image.network(
|
||||
img,
|
||||
fit: BoxFit.cover,
|
||||
errorBuilder: (context, error, stackTrace) {
|
||||
return Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.broken_image, size: 40, color: Colors.red),
|
||||
const SizedBox(height: 8),
|
||||
Text('Image not available',
|
||||
style: TextStyle(color: Colors.red[700])),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
// Empty state with camera icon
|
||||
if (img == null || img.isEmpty)
|
||||
Center(
|
||||
child: Column(
|
||||
mainAxisAlignment: MainAxisAlignment.center,
|
||||
children: [
|
||||
Icon(Icons.camera_alt_outlined,
|
||||
size: 40, color: Colors.grey[600]),
|
||||
const SizedBox(height: 8),
|
||||
Text('Tap to upload'.tr,
|
||||
style: const TextStyle(color: Colors.grey, fontSize: 12)),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user