Update: 2026-06-27 04:21:17

This commit is contained in:
Hamza-Ayed
2026-06-27 04:21:17 +03:00
parent b4f063aaac
commit 8993aa0a6b
6 changed files with 122 additions and 43 deletions

View File

@@ -583,13 +583,14 @@ $pwdHashed = password_hash($rawSecret, PASSWORD_DEFAULT);
if (isset($con) && $con instanceof PDO && $con->inTransaction()) { if (isset($con) && $con instanceof PDO && $con->inTransaction()) {
$con->rollBack(); $con->rollBack();
} }
error_log("register_driver_and_car ERROR: " . $e->getMessage()); $msg = $e->getMessage();
jsonError("Server error"); error_log("register_driver_and_car ERROR: " . $msg);
jsonError("Server error: $msg", 400);
} catch (PDOException $e) { } catch (PDOException $e) {
if (isset($con) && $con instanceof PDO && $con->inTransaction()) { if (isset($con) && $con instanceof PDO && $con->inTransaction()) {
$con->rollBack(); $con->rollBack();
} }
error_log("register_driver_and_car PDO: " . $e->getMessage()); error_log("register_driver_and_car PDO: " . $e->getMessage());
jsonError("Database error."); jsonError("Database error: " . $e->getMessage());
} }
?> ?>

View File

@@ -43,7 +43,13 @@ class KazanController extends GetxController {
if (decoded['status'] == "success") { if (decoded['status'] == "success") {
var message = decoded['message']; var message = decoded['message'];
if (message is List && message.isNotEmpty) { 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; kazanData['country'] = selectedCountry.value;
} else { } else {
kazanData.value = {'country': selectedCountry.value}; kazanData.value = {'country': selectedCountry.value};

View File

@@ -102,6 +102,13 @@ class LoginDriverController extends GetxController {
// } // }
isPhoneVerified() async { 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( var res = await CRUD().post(
link: AppLink.isPhoneVerified, link: AppLink.isPhoneVerified,
payload: {'phone_number': box.read(BoxName.phoneDriver)}); payload: {'phone_number': box.read(BoxName.phoneDriver)});

View File

@@ -371,9 +371,15 @@ class RegistrationController extends GetxController {
final _jwt = box.read(BoxName.jwt); final _jwt = box.read(BoxName.jwt);
final String _token = _jwt != null ? r(_jwt).split(AppInformation.addd)[0] : ''; final String _token = _jwt != null ? r(_jwt).split(AppInformation.addd)[0] : '';
String timestamp = DateTime.now().millisecondsSinceEpoch.toString();
String nonce = timestamp;
final headers = <String, String>{ final headers = <String, String>{
'Authorization': 'Bearer $_token', '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); request.headers.addAll(headers);
@@ -472,7 +478,7 @@ class RegistrationController extends GetxController {
final req = http.MultipartRequest('POST', syrianUploadUri); final req = http.MultipartRequest('POST', syrianUploadUri);
req.headers.addAll({ req.headers.addAll({
'Authorization': authHeader, 'Authorization': authHeader,
'X-HMAC-Auth': hmacHeader, 'X-Device-FP': box.read(BoxName.deviceFingerprint)?.toString() ?? '',
}); });
req.fields['driver_id'] = driverId; req.fields['driver_id'] = driverId;
@@ -603,11 +609,14 @@ class RegistrationController extends GetxController {
_addField(fields, 'expiry_date', driverLicenseExpiryController.text); _addField(fields, 'expiry_date', driverLicenseExpiryController.text);
_addField(fields, 'password', 'generated_password_or_token'); _addField(fields, 'password', 'generated_password_or_token');
_addField(fields, 'status', 'yet'); _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, 'gender', 'Male'); // يفضل ربطها بـ Dropdown أيضاً
_addField(fields, 'country', box.read(BoxName.countryCode)?.toString() ?? 'Jordan');
// --- Car Data --- // --- Car Data ---
_addField(fields, 'vin', 'yet'); _addField(fields, 'vin', carVinController.text.isNotEmpty ? carVinController.text : 'yet');
_addField(fields, 'car_plate', carPlateController.text); _addField(fields, 'car_plate', carPlateController.text);
_addField(fields, 'make', carMakeController.text); _addField(fields, 'make', carMakeController.text);
_addField(fields, 'model', carModelController.text); _addField(fields, 'model', carModelController.text);
@@ -615,8 +624,9 @@ class RegistrationController extends GetxController {
_addField( _addField(
fields, fields,
'expiration_date', 'expiration_date',
driverLicenseExpiryController carRegistrationExpiryController.text.isNotEmpty
.text); // تأكد من أن هذا تاريخ انتهاء السيارة وليس الرخصة ? carRegistrationExpiryController.text
: driverLicenseExpiryController.text);
_addField( _addField(
fields, fields,
'color', 'color',
@@ -668,8 +678,8 @@ class RegistrationController extends GetxController {
_addField(fields, 'id_front', idFrontUrl); _addField(fields, 'id_front', idFrontUrl);
_addField(fields, 'id_back', idBackUrl); _addField(fields, 'id_back', idBackUrl);
_addField(fields, 'driver_license', driverLicenseUrl); _addField(fields, 'driver_license', driverLicenseUrl);
if (isSyria) // Always add the key to avoid PHP undefined index error
_addField(fields, 'driver_license_back', driverLicenseBackUrl); fields['driver_license_back'] = driverLicenseBackUrl ?? '';
_addField(fields, 'profile_picture', profilePicUrl); _addField(fields, 'profile_picture', profilePicUrl);
_addField(fields, 'criminal_record', criminalRecordUrl); _addField(fields, 'criminal_record', criminalRecordUrl);
_addField(fields, 'car_license_front', carFrontUrl); _addField(fields, 'car_license_front', carFrontUrl);
@@ -677,6 +687,12 @@ class RegistrationController extends GetxController {
req.fields.addAll(fields); req.fields.addAll(fields);
Log.print('--- 🚀 Registration Request Payload ---');
req.fields.forEach((key, value) {
Log.print('[$key]: $value');
});
Log.print('---------------------------------------');
// 3) الإرسال // 3) الإرسال
final streamed = final streamed =
await client.send(req).timeout(const Duration(seconds: 60)); await client.send(req).timeout(const Duration(seconds: 60));

View File

@@ -400,7 +400,8 @@ class _MyAppState extends State<MyApp> with WidgetsBindingObserver {
if (savedTrip != null && savedTrip.isNotEmpty) { if (savedTrip != null && savedTrip.isNotEmpty) {
if (Get.currentRoute == '/') { 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; return;
} }
await storage.delete(key: 'pending_driver_list'); 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 { Future<void> _processAcceptOrder(List<dynamic> data) async {
if (_isProcessingAccept) { if (_isProcessingAccept) {
print("⏳ _processAcceptOrder: Already accepting order, skipping duplicate request."); print(
"⏳ _processAcceptOrder: Already accepting order, skipping duplicate request.");
return; return;
} }
_isProcessingAccept = true; _isProcessingAccept = true;

View File

@@ -450,36 +450,83 @@ class RegistrationView extends StatelessWidget {
child: SizedBox( child: SizedBox(
height: 150, height: 150,
width: double.infinity, width: double.infinity,
child: (img != null && img.isNotEmpty) child: Stack(
? Image.network( children: [
img, // Card template background
fit: BoxFit.cover, Container(
errorBuilder: (context, error, stackTrace) { width: double.infinity,
return Center( height: double.infinity,
child: Column( decoration: BoxDecoration(
mainAxisAlignment: MainAxisAlignment.center, color: Colors.grey[100],
children: [ borderRadius: BorderRadius.circular(4),
Icon(Icons.broken_image, size: 40, color: Colors.red), border: Border.all(
const SizedBox(height: 8), color: Colors.grey[300]!,
Text('Image not available', width: 1.5,
style: TextStyle(color: Colors.red[700])), strokeAlign: BorderSide.strokeAlignInside,
], ),
),
);
},
)
: 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: 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)),
],
),
),
],
),
), ),
), ),
); );