diff --git a/backend/Admin/Staff/add_super_admin.php b/backend/Admin/Staff/add_super_admin.php
deleted file mode 100644
index f8d4ed53..00000000
--- a/backend/Admin/Staff/add_super_admin.php
+++ /dev/null
@@ -1,63 +0,0 @@
- 'Access denied. Admin key required.']));
-// }
-
-$con = Database::get('main');
-
-$name = $_GET['name'] ?? filterRequest('name') ?: 'Super Admin';
-$email = $_GET['email'] ?? filterRequest('email') ?: '';
-$phone = $_GET['phone'] ?? filterRequest('phone') ?: '';
-$fingerprint = $_GET['fingerprint'] ?? filterRequest('fingerprint') ?: '';
-$password = $_GET['password'] ?? filterRequest('password') ?: bin2hex(random_bytes(8));
-
-try {
- $hashedPass = password_hash($password, PASSWORD_DEFAULT);
- $encName = $encryptionHelper->encryptData($name);
- $encPhone = $phone ? $encryptionHelper->encryptData($phone) : '';
- $encEmail = $email ? $encryptionHelper->encryptData($email) : '';
- $encFp = $fingerprint ? $encryptionHelper->encryptData($fingerprint) : '';
- $fpHash = $fingerprint ? hash('sha256', $fingerprint) : '';
- $uniqueId = bin2hex(random_bytes(16));
-
- $check = $con->prepare("SELECT id FROM adminUser WHERE role = 'super_admin' LIMIT 1");
- $check->execute();
- if ($check->fetch()) {
- echo "
⚠️ Super Admin already exists.
";
- exit;
- }
-
- $sql = "INSERT INTO adminUser (id, fingerprint, fingerprint_hash, name, phone, email, password, role, created_at)
- VALUES (:id, :fp, :fp_hash, :name, :phone, :email, :pass, 'super_admin', NOW())";
- $stmt = $con->prepare($sql);
- $stmt->execute([
- ':id' => $uniqueId,
- ':fp' => $encFp,
- ':fp_hash' => $fpHash,
- ':name' => $encName,
- ':phone' => $encPhone,
- ':email' => $encEmail,
- ':pass' => $hashedPass,
- ]);
-
- if ($stmt->rowCount() > 0) {
- echo "✅ Super Admin created successfully!
";
- echo "ID: $uniqueId
";
- echo "Name: $name
";
- echo "Password: $password
";
- echo "⚠️ Save this password. Delete this file after use.
";
- } else {
- echo "❌ Failed to create Super Admin.
";
- }
-} catch (Exception $e) {
- echo "❌ Error: " . htmlspecialchars($e->getMessage()) . "
";
-}
diff --git a/backend/Admin/adminUser/add_invoice.php b/backend/Admin/adminUser/add_invoice.php
index 3e140a11..fd582d3c 100644
--- a/backend/Admin/adminUser/add_invoice.php
+++ b/backend/Admin/adminUser/add_invoice.php
@@ -7,7 +7,6 @@ error_reporting(E_ALL);
require_once __DIR__ . '/../../connect.php';
-$driverID = filterRequest("driverID");
$invoiceNumber = filterRequest("invoiceNumber");
$amount = filterRequest("amount");
$date = filterRequest("date");
@@ -17,7 +16,7 @@ $linkImage = null;
$uploadDate = date("Y-m-d H:i:s");
// ✅ طباعة بيانات الإدخال للتأكد
-error_log("[add_invoice.php] 📥 Data received | driverID: $driverID, invoiceNumber: $invoiceNumber, amount: $amount, date: $date");
+error_log("[add_invoice.php] 📥 Data received | invoiceNumber: $invoiceNumber, amount: $amount, date: $date");
// التحقق من وجود ملف الصورة
if (isset($_FILES['image']) && $_FILES['image']['error'] === UPLOAD_ERR_OK) {
@@ -43,7 +42,7 @@ if (isset($_FILES['image']) && $_FILES['image']['error'] === UPLOAD_ERR_OK) {
exit;
}
- $new_filename = $invoiceNumber . "_" . $driverID . '.' . $image_extension;
+ $new_filename = $invoiceNumber . '.' . $image_extension;
$target_dir = "invoice_images/";
$target_file = $target_dir . $new_filename;
@@ -66,9 +65,9 @@ if (isset($_FILES['image']) && $_FILES['image']['error'] === UPLOAD_ERR_OK) {
}
try {
- $stmt = $con->prepare("INSERT INTO invoice_records (driverID, invoice_number,name, amount, date, image_link, created_at)
- VALUES (?, ?, ?,?, ?, ?, ?)");
- $stmt->execute([$driverID, $invoiceNumber,$name, $amount, $date, $linkImage, $uploadDate]);
+ $stmt = $con->prepare("INSERT INTO invoice_records (invoice_number, name, amount, date, image_link, created_at)
+ VALUES (?, ?, ?, ?, ?, ?)");
+ $stmt->execute([$invoiceNumber, $name, $amount, $date, $linkImage, $uploadDate]);
echo json_encode([
'status' => 'success',
diff --git a/backend/Admin/auth/migrate_db.php b/backend/Admin/auth/migrate_db.php
deleted file mode 100644
index f3bb1766..00000000
--- a/backend/Admin/auth/migrate_db.php
+++ /dev/null
@@ -1,28 +0,0 @@
-query("SHOW COLUMNS FROM adminUser LIKE 'status'");
- if ($check->rowCount() == 0) {
- $sql = "ALTER TABLE adminUser
- ADD COLUMN status ENUM('pending', 'approved', 'suspended', 'rejected') NOT NULL DEFAULT 'pending' AFTER role,
- ADD COLUMN phone VARCHAR(50) DEFAULT NULL AFTER name,
- ADD COLUMN email VARCHAR(255) DEFAULT NULL AFTER phone,
- ADD COLUMN approved_by VARCHAR(64) DEFAULT NULL AFTER status,
- ADD COLUMN approved_at DATETIME DEFAULT NULL AFTER approved_by";
-
- $con->exec($sql);
-
- // Update existing admins to approved and super_admin
- $con->exec("UPDATE adminUser SET status = 'approved', role = 'super_admin' WHERE id IS NOT NULL");
-
- echo json_encode(["status" => "success", "message" => "Migration completed successfully."]);
- } else {
- echo json_encode(["status" => "success", "message" => "Columns already exist."]);
- }
-} catch (Exception $e) {
- echo json_encode(["status" => "error", "message" => "An internal error occurred"]);
-}
diff --git a/backend/Admin/auth/migration_cryptography.php b/backend/Admin/auth/migration_cryptography.php
deleted file mode 100644
index 2c4d5b8a..00000000
--- a/backend/Admin/auth/migration_cryptography.php
+++ /dev/null
@@ -1,128 +0,0 @@
- [
- 'phone', 'email', 'gender', 'birthdate', 'site',
- 'first_name', 'last_name', 'accountBank', 'education',
- 'employmentType', 'maritalStatus', 'national_number',
- 'name_arabic', 'address'
- ],
- 'passengers' => [
- 'phone', 'email', 'gender', 'birthdate',
- 'first_name', 'last_name', 'token'
- ],
- 'CarRegistration' => [
- 'vin', 'car_plate', 'owner', 'address'
- ],
- 'carPlateEdit' => [
- 'carPlate', 'owner'
- ],
- 'phone_verification' => [
- 'phone_number'
- ],
- 'phone_verification_passenger' => [
- 'phone_number'
- ],
- 'driverToken' => [
- 'token'
- ],
- 'passengerToken' => [
- 'token'
- ],
- 'mishwari' => [
- 'phone', 'gender', 'name', 'name_english', 'car_plate', 'token', 'education', 'national_number', 'age'
- ],
- 'rate_app' => [
- 'email', 'phone'
- ],
- 'admins' => [
- 'name', 'phone', 'email', 'fp'
- ],
- 'driver_assurance' => [
- 'assured', 'health_insurance_provider'
- ],
- 'blacklist_drivers' => [
- 'phone'
- ],
- 'blacklist_passengers' => [
- 'phone'
- ],
- 'feedBack' => [
- 'feedBack'
- ]
-];
-
-$totalUpdated = 0;
-
-foreach ($tables as $table => $columns) {
- echo "Processing table: $table ...\n";
- ob_flush(); flush();
-
- try {
- $sql = "SELECT `id`, `" . implode("`, `", $columns) . "` FROM `$table`";
- $stmt = $con->query($sql);
- if (!$stmt) {
- echo "Skipped $table (Not found or missing columns).\n";
- continue;
- }
- $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
- } catch (Exception $e) {
- echo "An internal error occurred" . "\n";
- continue;
- }
-
- $tableUpdatedCount = 0;
-
- foreach ($rows as $row) {
- $id = $row['id'];
- $needsUpdate = false;
- $updateValues = [];
- $params = [':id' => $id];
-
- foreach ($columns as $col) {
- $value = $row[$col];
-
- // تحقق إذا كان الحقل يحتوي على قيمة وإذا لم يكن مشفر بالنظام الجديد
- if (!empty($value) && strpos($value, 'GCM:') !== 0) {
- // محاولة فك التشفير القديم (CBC)
- try {
- $decrypted = $encryptionHelper->decryptData($value);
- if ($decrypted !== false && $decrypted !== '') {
- // إعادة التشفير (سيستخدم GCM الآن)
- $newEncrypted = $encryptionHelper->encryptData($decrypted);
- $updateValues[] = "`$col` = :$col";
- $params[":$col"] = $newEncrypted;
- $needsUpdate = true;
- }
- } catch (Exception $e) {
- error_log("Failed to migrate $col for ID $id in $table: " . $e->getMessage());
- }
- }
- }
-
- if ($needsUpdate) {
- $setClause = implode(", ", $updateValues);
- $updateSql = "UPDATE `$table` SET $setClause WHERE `id` = :id";
- $updateStmt = $con->prepare($updateSql);
- $updateStmt->execute($params);
- $tableUpdatedCount++;
- }
- }
-
- echo "Finished $table. Updated rows: $tableUpdatedCount\n";
- $totalUpdated += $tableUpdatedCount;
- ob_flush(); flush();
-}
-
-echo "Migration completed! Total rows updated: $totalUpdated\n";
-?>
diff --git a/backend/core/Services/SiroGeminiService.php b/backend/core/Services/SiroGeminiService.php
index b90245ae..0c7384c8 100644
--- a/backend/core/Services/SiroGeminiService.php
+++ b/backend/core/Services/SiroGeminiService.php
@@ -30,7 +30,7 @@ class SiroGeminiService {
float $siroBasePrice,
string $regionName,
string $countryCode,
- string $model = 'gemini-1.5-flash'
+ string $model = 'gemini-flash-lite-latest'
): ?array {
if (!$this->apiKey) {
error_log("[SiroGeminiService] API Key is missing.");
diff --git a/backend/diagnose_fingerprint.php b/backend/diagnose_fingerprint.php
deleted file mode 100644
index 5592a904..00000000
--- a/backend/diagnose_fingerprint.php
+++ /dev/null
@@ -1,38 +0,0 @@
-getMessage() . "\n";
- exit;
-}
-
-$targetId = 'e494c5750f95e1c26654';
-echo "Target Passenger ID: " . $targetId . "\n\n";
-
-try {
- $stmt = $con->prepare("SELECT * FROM tokens WHERE passengerID = ? LIMIT 1");
- $stmt->execute([$targetId]);
- $row = $stmt->fetch(PDO::FETCH_ASSOC);
- if ($row) {
- echo "✅ Token row found:\n";
- echo " - Passenger ID: " . $row['passengerID'] . "\n";
- echo " - Token (encrypted/raw): " . $row['token'] . "\n";
- echo " - Fingerprint (stored): " . $row['fingerprint'] . "\n";
-
- $decryptedToken = $encryptionHelper->decryptData($row['token']);
- echo " - Decrypted Token: " . $decryptedToken . "\n";
-
- $fpPepper = getenv('FP_PEPPER') ?: '';
- echo " - FP_PEPPER: " . ($fpPepper ? "Set" : "Not Set") . "\n";
- } else {
- echo "❌ No token row found for passenger!\n";
- }
-} catch (Exception $e) {
- echo "❌ Error: " . $e->getMessage() . "\n";
-}
diff --git a/backend/diagnose_login.php b/backend/diagnose_login.php
deleted file mode 100644
index e7fa425a..00000000
--- a/backend/diagnose_login.php
+++ /dev/null
@@ -1,94 +0,0 @@
-getMessage() . "\n";
- exit;
-}
-
-$targetId = 'e494c5750f95e1c26654';
-echo "Target Passenger ID: " . $targetId . "\n\n";
-
-// 1. Check passengers table
-try {
- $stmt = $con->prepare("SELECT * FROM passengers WHERE id = ?");
- $stmt->execute([$targetId]);
- $p = $stmt->fetch(PDO::FETCH_ASSOC);
- if ($p) {
- echo "✅ Passenger found in database:\n";
- echo " - Phone (encrypted): " . $p['phone'] . "\n";
- echo " - Phone (decrypted): " . $encryptionHelper->decryptData($p['phone']) . "\n";
- echo " - First Name (decrypted): " . $encryptionHelper->decryptData($p['first_name']) . "\n";
- echo " - Last Name (decrypted): " . $encryptionHelper->decryptData($p['last_name']) . "\n";
- } else {
- echo "❌ Passenger NOT found in database!\n";
- }
-} catch (Exception $e) {
- echo "❌ Error querying passengers table: " . $e->getMessage() . "\n";
-}
-
-// 2. Check phone_verification_passenger table
-try {
- $decryptedPhone = '962798583052';
- $encryptedPhone = $encryptionHelper->encryptData($decryptedPhone);
- echo "\nSearching phone_verification_passenger for: $decryptedPhone ($encryptedPhone)\n";
-
- $stmt = $con->prepare("SELECT * FROM phone_verification_passenger WHERE phone_number = ?");
- $stmt->execute([$encryptedPhone]);
- $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
- if (!empty($rows)) {
- echo "✅ Phone verification rows found (" . count($rows) . "):\n";
- foreach ($rows as $row) {
- echo " - ID: " . $row['id'] . " | Verified: " . $row['verified'] . " | Expiration: " . $row['expiration_time'] . " | Created At: " . $row['created_at'] . "\n";
- }
- } else {
- echo "❌ No phone verification rows found for this phone number!\n";
-
- // Let's get any recent rows
- $stmt2 = $con->prepare("SELECT * FROM phone_verification_passenger ORDER BY id DESC LIMIT 5");
- $stmt2->execute();
- $all = $stmt2->fetchAll(PDO::FETCH_ASSOC);
- echo " Recent rows in table:\n";
- foreach ($all as $row) {
- $dec = $encryptionHelper->decryptData($row['phone_number']);
- echo " - ID: " . $row['id'] . " | Phone (decrypted): $dec | Verified: " . $row['verified'] . "\n";
- }
- }
-} catch (Exception $e) {
- echo "❌ Error querying phone_verification_passenger: " . $e->getMessage() . "\n";
-}
-
-// 3. Test the exact SQL query from loginFromGooglePassenger.php
-echo "\n--- 🧪 Testing loginFromGooglePassenger query (WITH verified = 1 constraint) ---\n";
-try {
- $sqlOld = "SELECT p.`id` FROM passengers p
- LEFT JOIN phone_verification_passenger ON phone_verification_passenger.phone_number = p.phone
- WHERE p.id = :id AND phone_verification_passenger.verified = '1'";
-
- $stmt = $con->prepare($sqlOld);
- $stmt->execute([':id' => $targetId]);
- $count = $stmt->rowCount();
- echo "Old query row count: $count\n";
-} catch (Exception $e) {
- echo "Old query error: " . $e->getMessage() . "\n";
-}
-
-echo "\n--- 🧪 Testing loginFromGooglePassenger query (WITHOUT verified = 1 constraint) ---\n";
-try {
- $sqlNew = "SELECT p.`id` FROM passengers p
- LEFT JOIN phone_verification_passenger ON phone_verification_passenger.phone_number = p.phone
- WHERE p.id = :id";
-
- $stmt = $con->prepare($sqlNew);
- $stmt->execute([':id' => $targetId]);
- $count = $stmt->rowCount();
- echo "New query row count: $count\n";
-} catch (Exception $e) {
- echo "New query error: " . $e->getMessage() . "\n";
-}
diff --git a/backend/migrate_driver_passwords.php b/backend/migrate_driver_passwords.php
deleted file mode 100644
index 2c4f2528..00000000
--- a/backend/migrate_driver_passwords.php
+++ /dev/null
@@ -1,128 +0,0 @@
- false,
- PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
- PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
- PDO::MYSQL_ATTR_INIT_COMMAND => "SET NAMES UTF8",
- ];
- $pdo = new PDO($dsn, $dbUser, $dbPass, $options);
-
- // نجلب الحقول التي نحتاجها لبناء السر
- $sql = "SELECT id, phone, birthdate, national_number FROM driver";
- $stmt = $pdo->query($sql);
-
- $update = $pdo->prepare("UPDATE driver SET password = :pwd WHERE id = :id");
-
- $count = 0;
- $skipped = 0;
- $startTime = microtime(true);
-
- while ($row = $stmt->fetch()) {
- $id = $row['id'];
- $encPhone = $row['phone'];
- $encBirth = $row['birthdate'] ?? null;
- $encNat = $row['national_number'] ?? null;
-
- // نفك التشفير – قد يرجع null لو الحقل فاضي
- $phone = $encPhone ? $encryptionHelper->decryptData($encPhone) : null;
- $birth = $encBirth ? $encryptionHelper->decryptData($encBirth) : null;
- $nat = $encNat ? $encryptionHelper->decryptData($encNat) : null;
-
- if (empty($id) || empty($phone)) {
- // لو ناقصين، نتجاوز السطر مع تسجيل في اللوج
- error_log("[MIGRATE] Skip driver id={$id}: missing phone or id.");
- $skipped++;
- continue;
- }
-
- // في الوضع المثالي عندك nat + birthdate لكل السائقين
- // لو حاب تجبرهم يكونوا موجودين:
- /*
- if (empty($nat) || empty($birth)) {
- error_log("[MIGRATE] Skip driver id={$id}: missing nat or birthdate.");
- $skipped++;
- continue;
- }
- */
-
- // phone مفروض يكون أصلاً مطبّع (9639...) من سكربت التسجيل
- $normalizedPhone = trim($phone);
-
- // نبني baseString: الأساس id + phone
- $parts = [$id, $normalizedPhone];
-
- // نضيف رقم وطني أو سنة الميلاد (حسب الموجود)
- if (!empty($nat)) {
- $parts[] = trim($nat);
- } elseif (!empty($birth)) {
- // birthdate متوقعة بصيغة YYYY-01-01 -> نأخذ السنة فقط
- $year = substr($birth, 0, 4);
- if (preg_match('/^\d{4}$/', $year)) {
- $parts[] = $year;
- }
- }
-
- $baseString = implode('|', $parts);
-
- // اشتقاق السر النهائي (HEX string، بدون باينري)
- $hmacHex = hash_hmac('sha256', $baseString, $pepper, false);
-
- // نخزن فقط الهاش باستخدام password_hash
- $pwdHash = password_hash($hmacHex, PASSWORD_DEFAULT);
-
- $update->execute([
- ':pwd' => $pwdHash,
- ':id' => $id,
- ]);
-
- $count++;
-
- // لوج بسيط كل 100 سائق
- if ($count % 100 === 0) {
- $elapsed = round(microtime(true) - $startTime, 2);
- error_log("[MIGRATE] Progress: updated {$count} drivers, skipped {$skipped}, elapsed {$elapsed}s");
- }
- }
-
- $totalTime = round(microtime(true) - $startTime, 2);
- error_log("[MIGRATE] Done. Updated {$count} driver passwords, skipped {$skipped}. Total time: {$totalTime}s");
- echo "Migration finished. Updated {$count} drivers, skipped {$skipped}. Time: {$totalTime}s\n";
-
-} catch (PDOException $e) {
- error_log("[MIGRATE][PDO] " . $e->getMessage());
- echo "Migration failed (DB error).\n";
- exit(1);
-} catch (Exception $e) {
- error_log("[MIGRATE][GENERAL] " . $e->getMessage());
- echo "Migration failed (general error).\n";
- exit(1);
-}
\ No newline at end of file
diff --git a/backend/migration_create_table.php b/backend/migration_create_table.php
deleted file mode 100644
index 09456f24..00000000
--- a/backend/migration_create_table.php
+++ /dev/null
@@ -1,21 +0,0 @@
-exec($sql);
- echo "SUCCESS: passenger_opening_locations table created successfully.\n";
-} catch (Exception $e) {
- echo "An internal error occurred" . "\n";
-}
-?>
diff --git a/backend/test_add_driver_and_car.php b/backend/test_add_driver_and_car.php
deleted file mode 100644
index 0636d76c..00000000
--- a/backend/test_add_driver_and_car.php
+++ /dev/null
@@ -1,210 +0,0 @@
-encryptData($phone);
- $encEmail = $encryptionHelper->encryptData($email);
- $encFirstName = $encryptionHelper->encryptData($first_name);
- $encLastName = $encryptionHelper->encryptData($last_name);
- $encNameArabic = $encryptionHelper->encryptData("$first_name $last_name");
- $encGender = $encryptionHelper->encryptData($gender);
- $encNationalNumber = $national_number ? $encryptionHelper->encryptData($national_number) : '';
- $encBirthdate = $encryptionHelper->encryptData($birthdate);
- $encSite = $encryptionHelper->encryptData($site);
- $encOwner = $encryptionHelper->encryptData($owner);
- $encCarPlate = $encryptionHelper->encryptData($car_plate);
- $encVin = $encryptionHelper->encryptData($vin);
-
- $passwordHashed = password_hash($password, PASSWORD_DEFAULT);
-
- $con = Database::get('main');
-
- /* ================== التحقق من التكرار ================== */
- $dup = $con->prepare("SELECT id FROM driver WHERE phone = :p OR email = :e");
- $dup->execute([':p' => $encPhone, ':e' => $encEmail]);
- if ($dup->rowCount() > 0) {
- jsonError("Phone or email already registered.");
- exit;
- }
-
- $con->beginTransaction();
-
- /* ================== 1) إدراج السائق ================== */
- $sqlDriver = "
- INSERT INTO driver (
- id, phone, email, password, gender, license_type, national_number,
- name_arabic, issue_date, expiry_date, license_categories,
- address, licenseIssueDate, status, birthdate, site,
- first_name, last_name, accountBank, bankCode,
- employmentType, maritalStatus, fullNameMaritial, expirationDate,
- created_at, updated_at
- ) VALUES (
- :id, :phone, :email, :pwd, :gender, :license_type, :national_number,
- :name_arabic, :issue_date, :expiry_date, :license_categories,
- :address, :licenseIssueDate, :status, :birthdate, :site,
- :first_name, :last_name, :accountBank, :bankCode,
- :employmentType, :maritalStatus, :fullNameMaritial, :expirationDate,
- NOW(), NOW()
- )
- ";
- $insD = $con->prepare($sqlDriver);
- $insD->execute([
- ':id' => $driverId,
- ':phone' => $encPhone,
- ':email' => $encEmail,
- ':pwd' => $passwordHashed,
- ':gender' => $encGender,
- ':license_type' => $license_type,
- ':national_number' => $encNationalNumber,
- ':name_arabic' => $encNameArabic,
- ':issue_date' => '2020-01-01',
- ':expiry_date' => '2030-01-01',
- ':license_categories' => 'B',
- ':address' => $encSite,
- ':licenseIssueDate' => '2020-01-01',
- ':status' => 'pending_review',
- ':birthdate' => $encBirthdate,
- ':site' => $encSite,
- ':first_name' => $encFirstName,
- ':last_name' => $encLastName,
- ':accountBank' => 'yet',
- ':bankCode' => 'CIB',
- ':employmentType' => $employmentType,
- ':maritalStatus' => 'Single',
- ':fullNameMaritial' => '',
- ':expirationDate' => date('Y-m-d', strtotime('+5 years')),
- ]);
-
- /* ================== 2) إدراج السيارة ================== */
- $sqlCar = "
- INSERT INTO CarRegistration (
- driverID, vin, car_plate, make, model, year, expiration_date,
- color, owner, color_hex, fuel,
- vehicle_category_id, fuel_type_id,
- isDefault, created_at, status
- ) VALUES (
- :driverID, :vin, :car_plate, :make, :model, :year, :expiration_date,
- :color, :owner, :color_hex, :fuel,
- :vehicle_category_id, :fuel_type_id,
- :isDefault, NOW(), 'active'
- )
- ";
- $insC = $con->prepare($sqlCar);
- $insC->execute([
- ':driverID' => $driverId,
- ':vin' => $encVin,
- ':car_plate' => $encCarPlate,
- ':make' => $make,
- ':model' => $model,
- ':year' => $year,
- ':expiration_date' => $expiration_date,
- ':color' => $color,
- ':owner' => $encOwner,
- ':color_hex' => $color_hex,
- ':fuel' => $fuel,
- ':vehicle_category_id' => 1,
- ':fuel_type_id' => 1,
- ':isDefault' => 1,
- ]);
-
- $carRegID = $con->lastInsertId();
-
- /* ================== 3) توكن السائق ================== */
- $token = bin2hex(random_bytes(20));
- $sqlToken = "
- INSERT INTO driverToken (token, captain_id, fingerPrint, created_at)
- VALUES (:token, :captain_id, :fingerPrint, NOW())
- ";
- $con->prepare($sqlToken)->execute([
- ':token' => $token,
- ':captain_id' => $driverId,
- ':fingerPrint' => 'test_fingerprint',
- ]);
-
- /* ================== 4) توثيق رقم الهاتف ================== */
- $sqlPhoneVer = "
- INSERT INTO phone_verification (phone_number, driverId, email, token_code, expiration_time, is_verified, created_at)
- VALUES (:phone, :driverId, :email, :token_code, DATE_ADD(NOW(), INTERVAL 1 YEAR), 1, NOW())
- ";
- $con->prepare($sqlPhoneVer)->execute([
- ':phone' => $encPhone,
- ':driverId' => $driverId,
- ':email' => $encEmail,
- ':token_code' => $encryptionHelper->encryptData('999'),
- ]);
-
- /* ================== Commit ================== */
- $con->commit();
-
- printSuccess([
- 'driverID' => $driverId,
- 'carRegID' => $carRegID,
- 'status' => 'success',
- 'message' => "Driver $first_name $last_name created successfully with status pending_review.",
- ]);
-
-} catch (Exception $e) {
- if (isset($con) && $con instanceof PDO && $con->inTransaction()) {
- $con->rollBack();
- }
- error_log("[test_add_driver] " . $e->getMessage());
- jsonError($e->getMessage());
-}
diff --git a/backend/test_signed_pricing.php b/backend/test_signed_pricing.php
deleted file mode 100644
index 9154c101..00000000
--- a/backend/test_signed_pricing.php
+++ /dev/null
@@ -1,94 +0,0 @@
-
diff --git a/docs/دراسة_نظام_أتمتة_السوق_الذكي.md b/docs/دراسة_نظام_أتمتة_السوق_الذكي.md
new file mode 100644
index 00000000..0875bc11
--- /dev/null
+++ b/docs/دراسة_نظام_أتمتة_السوق_الذكي.md
@@ -0,0 +1,704 @@
+# دراسة نظام أتمتة السوق الذكي - Siro
+
+**التاريخ: 26 يونيو 2026**
+**إعداد: فريق التطوير**
+
+---
+
+## فهرس المحتويات
+
+1. [توحيد السناك بار (Snackbar System)](#1-توحيد-السناك-بار-snackbar-system)
+2. [نظرة عامة على نظام أتمتة السوق الذكي](#2-نظرة-عامة-على-نظام-أتمتة-السوق-الذكي)
+3. [مكونات النظام بالتفصيل](#3-مكونات-النظام-بالتفصيل)
+4. [تدفق البيانات (Data Flow)](#4-تدفق-البيانات-data-flow)
+5. [قاعدة البيانات وجداولها](#5-قاعدة-البيانات-وجدولها)
+6. [خريطة مفاتيح Redis](#6-خريطة-مفاتيح-redis)
+7. [الخدمات الخارجية المستخدمة](#7-الخدمات-الخارجية-المستخدمة)
+8. [ملفات الخلفية - مراجعة أمنية شاملة](#8-ملفات-الخلفية---مراجعة-أمنية-شاملة)
+9. [قائمة الملفات المطلوب حذفها فوراً](#9-قائمة-الملفات-المطلوب-حذفها-فوراً)
+10. [ثغرات SQL Injection](#10-ثغرات-sql-injection)
+11. [التوصيات النهائية](#11-التوصيات-النهائية)
+
+---
+
+## 1. توحيد السناك بار (Snackbar System)
+
+### الوضع الحالي - 4 تطبيقات و 4 طرق مختلفة
+
+يوجد حاليًا 4 تطبيقات Flutter ولكل منها نظام سناك بار مختلف تمامًا:
+
+---
+
+### 1.1 siro_admin - الإصدار القديم (GetX)
+
+**الملف:** `siro_admin/lib/views/widgets/snackbar.dart`
+
+- يستخدم `Get.snackbar()` حصريًا
+- يوجد دالتان فقط: `mySnackeBarError()` و `mySnackbarSuccess()`
+- **لا يوجد** `mySnackbarWarning()` ولا `mySnackbarInfo()`
+- الألوان: أحمر للخطأ (`AppColor.redColor`)، أخضر للنجاح (`AppColor.greenColor`)
+- يستخدم `SnackbarConfig` للثوابت (مدة 3 ثوان، زوايا 12، ظل)
+- التوقيع: `SnackbarController mySnackeBarError(String message)`
+- النصوص بالإنكليزية: `'Error'.tr`, `'Success'.tr`
+- 30+ استخدامًا في التطبيق
+
+### 1.2 siro_service - الإصدار القديم (GetX)
+
+**الملف:** `siro_service/lib/views/widgets/mycircular.dart`
+
+- يستخدم `Get.snackbar()` حصريًا
+- يوجد 3 دوال: `mySnackbarError()` و `mySnackbarWarning()` و `mySnackbarSuccess()`
+- الألوان: أحمر (`AppColor.redColor`)، أصفر (`AppColor.yellowColor` + نص أسود)، أخضر (`AppColor.greenColor`)
+- نفس `SnackbarConfig` للثوابت
+- 70+ استخدامًا في التطبيق
+
+### 1.3 siro_driver - الإصدار الحديث (Custom Widget)
+
+**الملف:** `siro_driver/lib/views/widgets/error_snakbar.dart`
+
+- ويج محسّن مخصص مع `_SnackContent` و `AnimationController`
+- يوجد 4 دوال: `mySnackbarSuccess()` و `mySnackeBarError()` و `mySnackbarInfo()` و `mySnackbarWarning()`
+- **4 متغيرات (variants):** success, error, info, warning
+- ألوان: أخضر (`#1A9E5C`)، أحمر (`#D93025`)، أزرق (`#1A73E8`)، برتقالي (`#F29900`)
+- ألوان السطح: `#F0FBF5`, `#FEF2F1`, `#F0F6FF`, `#FFF8E6`
+- تأثيرات: `ScaleTransition` مع `Curves.elasticOut`، شريط تقدم تنازلي
+- إخفاء يدوي مع زر Close + Haptic Feedback
+- يتحقق أولاً من `Overlay`، فإن لم يجده يستخدم `ScaffoldMessenger`
+- **مشكلة:** يستخدم `Get.snackbar()` الذي يرمي `FlutterError` إذا لم يكن `Overlay` جاهزًا
+- 100+ استخدامًا في التطبيق
+
+### 1.4 siro_rider - الإصدار الحديث المحسّن (ScaffoldMessenger)
+
+**الملف:** `siro_rider/lib/views/widgets/error_snakbar.dart`
+
+- نفس تصميم `siro_driver` مع تحسينات جوهرية
+- **يستخدم فقط `ScaffoldMessenger`** بدلاً من `Get.snackbar()` (لتجنب أخطاء `Overlay`)
+- يحتوي على آلية إعادة محاولة (retry) تصل إلى 3 مرات
+- `messenger.clearSnackBars()` قبل العرض (يمنع التراكم)
+- يعيد `SnackbarController?` (nullable) لأن `Get.snackbar` لم يعد مستخدمًا
+- التوقيع: `SnackbarController? mySnackbarSuccess(String message)` - ملاحظة: الرجوع `?`
+- 100+ استخدامًا في التطبيق
+
+### 1.5 دوال Toast البسيطة
+
+- **siro_driver** و **siro_rider** لديهما `lib/controller/functions/toast.dart`
+- `Toast.show(BuildContext context, String message, Color color)` - دالة بسيطة جدًا
+- تستخدم `ScaffoldMessenger.of(context).showSnackBar()` مع `SnackBar` مادة
+- 10+ استخدامات في كل تطبيق
+
+---
+
+### الفروقات بين الإصدارين (القديم والحديث)
+
+| الخاصية | siro_admin (قديم) | siro_driver (حديث) | siro_rider (حديث) | siro_service (قديم) |
+|---------|:-----------------:|:------------------:|:-----------------:|:-------------------:|
+| عدد الدوال | 2 | 4 | 4 | 3 |
+| `mySnackbarWarning` | ❌ | ✅ | ✅ | ✅ |
+| `mySnackbarInfo` | ❌ | ✅ | ✅ | ❌ |
+| `mySnackbarSuccess` | ✅ | ✅ | ✅ | ✅ |
+| `mySnackeBarError` | ✅ | ✅ | ✅ | ✅ |
+| آلية العرض | `Get.snackbar()` | `Get.snackbar()` + `ScaffoldMessenger` | `ScaffoldMessenger` فقط | `Get.snackbar()` |
+| التصميم | نص فقط | أيقونة + نص + زر إغلاق + شريط تقدم | أيقونة + نص + زر إغلاق + شريط تقدم | نص فقط |
+| Retry | ❌ | ❌ | ✅ (3 مرات) | ❌ |
+| Null Safety | `SnackbarController` | `SnackbarController` | `SnackbarController?` | `SnackbarController` |
+| أخطاء `Overlay` | ✅ (GetX آمن) | ⚠️ (قد يحدث) | ✅ (متفادي) | ✅ (GetX آمن) |
+
+---
+
+### خطة التوحيد المقترحة
+
+#### الهدف: إنشاء package مشترك واحد لجميع التطبيقات الأربعة
+
+**الخطوة 1:** إنشاء مجلد مشترك (shared package) في المسار:
+```
+siro_admin/lib/shared/widgets/snackbar/
+```
+
+**الخطوة 2:** توحيد الواجهة (API) لتصبح:
+
+```dart
+// الاستخدام الموحد - نفس التوقيع في كل التطبيقات
+void mySnackbarSuccess(String message);
+void mySnackbarError(String message);
+void mySnackbarWarning(String message);
+void mySnackbarInfo(String message);
+```
+
+**الخطوة 3:** الاعتماد على `ScaffoldMessenger` فقط (مثل siro_rider) لتجنب مشاكل `Overlay` في GetX.
+
+**الخطوة 4:** توحيد الألوان والثوابت:
+
+| المتغير | اللون الأساسي | لون السطح | الأيقونة |
+|---------|:------------:|:---------:|:--------:|
+| success | `#1A9E5C` (أخضر) | `#F0FBF5` | `check_circle_rounded` |
+| error | `#D93025` (أحمر) | `#FEF2F1` | `error_rounded` |
+| info | `#1A73E8` (أزرق) | `#F0F6FF` | `info_rounded` |
+| warning | `#F29900` (برتقالي) | `#FFF8E6` | `warning_amber_rounded` |
+
+**الخطوة 5:** تحويل ملفات `toast.dart` في `siro_driver` و `siro_rider` لاستخدام دالة واحدة موحدة بدلاً من التكرار.
+
+**الخطوة 6:** إزالة دوال `Get.snackbar()` المباشرة من جميع ملفات التحكم (controllers) والاستعاضة عنها بالدوال الموحدة.
+
+---
+
+## 2. نظرة عامة على نظام أتمتة السوق الذكي
+
+### ما هو النظام؟
+
+**"أتمتة السوق الذكي"** هو نظام متكامل لذكاء السوق والتسعير الديناميكي في Siro. يقوم النظام بـ:
+
+1. **جمع بيانات المنافسين** تلقائيًا عبر Android Bot
+2. **تحليل الفجوات السعرية** بين Siro والمنافسين (YallaGo, Zaken, Tufaddal)
+3. **تعديل أسعار Siro تلقائيًا** بناءً على تحليل السوق
+4. **كشف فرص رفع الأسعار** (Surge Opportunities) في المناطق التي يرتفع فيها الطلب
+5. **توليد حملات تسويقية ذكية** باستخدام AI (Google Gemini)
+6. **تقديم تقارير أسبوعية** عن صحة السوق (Market Health Reports)
+7. **محاكاة "What-If"** لمعرفة أثر تغيير الأسعار
+
+### طبقات النظام
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ طبقة العرض (Admin Dashboard) │
+│ siro_admin → لوحة تحكم المدير → Flutter Web │
+│ endpoints: Admin/marketing/*.php │
+├─────────────────────────────────────────────────────────────────────────────┤
+│ طبقة API (PHP Backend) │
+│ cron_jobs → bot/*.php │ pricing → ride/pricing/*.php │
+│ heatmap → ride/heatmap/*.php │ marketing → Admin/marketing/*.php │
+├─────────────────────────────────────────────────────────────────────────────┤
+│ طبقة الذكاء والتحليل │
+│ Google Gemini AI │ SiroGeminiService │ Redis Analytics │
+├─────────────────────────────────────────────────────────────────────────────┤
+│ طبقة جمع البيانات │
+│ Android Bot ←→ worker.php ←→ competitor_prices (MySQL) │
+│ generate_price_tasks.php (cron) → Redis Queue → Bot │
+├─────────────────────────────────────────────────────────────────────────────┤
+│ قاعدة البيانات والذاكرة المؤقتة │
+│ MySQL (main, tracking, ride) │ Redis (مفاتيح surge, demand) │
+└─────────────────────────────────────────────────────────────────────────────┘
+```
+
+### الدول المدعومة
+
+| الدولة | رمز البلد | المنطقة الزمنية | عملة |
+|:------:|:--------:|:--------------:|:----:|
+| سوريا | SY | Asia/Damascus | ل.س |
+| الأردن | JO | Asia/Amman | د.أ |
+| مصر | EG | Africa/Cairo | ج.م |
+| العراق | IQ | Asia/Baghdad | د.ع |
+
+---
+
+## 3. مكونات النظام بالتفصيل
+
+### 3.1 نظام البوت (Bot System) - `backend/bot/`
+
+#### 3.1.1 `generate_price_tasks.php`
+- **الجدولة:** كل 15 دقيقة
+- **الوظيفة:** يولد مهام فحص أسعار المنافسين ويدفعها إلى Redis Queue
+- **آلية العمل:**
+ - ينشئ جدول `competitor_prices` تلقائيًا إذا لم يكن موجودًا
+ - يحتوي على 10 مناطق رئيسية في دمشق (ساحة الأمويين، المزة، المالكي، كفرسوسة، الميدان، باب توما، ركن الدين، دمر، برامكة، المهاجرين)
+ - المنافسون: `['yallago', 'zaken', 'tufaddal']`
+ - لكل منطقة يولد نقطة انطلاق عشوائية ضمن 2km، ثم رحلة قصيرة (2-5km) وأخرى طويلة (10-15km)
+ - يضغط المهام في Redis list: `queue:bot:tasks`
+- **الملفات المكتوبة:** Redis key `queue:bot:tasks`
+- **ملاحظة:** إنشاء الجدول داخل cron job أمر غير محبذ - يجب أن يكون في migration
+
+#### 3.1.2 `worker.php`
+- **الوظيفة:** نقطة نهاية API لبوت Android لسحب المهام وإرسال النتائج
+- **الأمان:** HMAC-SHA256 مع نافذة 5 دقائق + `BOT_SECRET_KEY` من البيئة
+- **الأجهزة المسموحة:** `['SHAM_CASH_BOT_01', 'PRICE_SCRAPER_BOT_01']`
+- **GET:** يسحب مهمة من قائمة Redis (`RPOP`)
+- **POST:** يستقبل النتيجة:
+ - `price_check`: يحسب `pricePerKm` ويدرج في جدول `competitor_prices` و Redis `competitor:price_history`
+ - `payment`: يسجل نجاح الدفع
+ - `failed`: يسجل الخطأ
+- **Redis:** `queue:bot:tasks` (قراءة)، `competitor:price_history:{app}` (كتابة)
+- **MySQL:** إدراج في `competitor_prices`
+
+#### 3.1.3 `standalone_worker.php`
+- **الوظيفة:** نسخة بديلة كاملة بذاتها بدون Redis أو MySQL - تستخدم ملفات JSON
+- **الاستخدام:** اختبار محلي / تطوير
+- **الميزات:** لوحة تحكم HTML داكنة مع Bootstrap، منشئ مهام، سجل مهام
+- **الأمان:** HMAC-SHA256 مع نافذة 15 دقيقة
+- **لا يشكل خطرًا أمنيًا في الإنتاج طالما لا يمكن الوصول إليه عبر الويب**
+
+#### 3.1.4 `cron_surge_opportunity.php` (قلب النظام)
+- **الجدولة:** كل 10 دقائق
+- **الوظيفة:** محرك كشف فرص رفع الأسعار (Surge Detection)
+- **آلية العمل:**
+ 1. يستعلم من `competitor_prices` ويجمّع البيانات بخلايا جغرافية (~1.5km)
+ 2. يحسب خط الأساس (baseline): متوسط سعر كل منافس لآخر 7 أيام
+ 3. يحسب السعر الحالي: متوسط لكل منافس في آخر ساعتين
+ 4. يشترط وجود 2 عينة على الأقل لكل منافس في كل خلية
+ 5. يكتشف الفرصة عندما **جميع** المنافسين في الخلية رفعوا أسعارهم
+ 6. يقترح مضاعف السعر: `1.0 + (avg_competitor_surge_ratio - 1.0) * 0.6` (يقلل عن المنافسين بـ 40%)
+ 7. يحفظ في Redis: `surge:opportunities` مع TTL 10 دقائق
+- **SQL الرئيسي:**
+ ```sql
+ SELECT ROUND(lat * 74) / 74 AS lat_group, ROUND(lng * 74) / 74 AS lng_group,
+ competitor_name, country_code,
+ AVG(CASE WHEN created_at < DATE_SUB(NOW(), INTERVAL 6 HOUR)
+ THEN price_per_km END) AS baseline_avg,
+ AVG(CASE WHEN created_at >= DATE_SUB(NOW(), INTERVAL 2 HOUR)
+ THEN price_per_km END) AS current_avg,
+ COUNT(CASE WHEN created_at >= DATE_SUB(NOW(), INTERVAL 2 HOUR)
+ THEN 1 END) AS recent_samples
+ FROM competitor_prices
+ GROUP BY lat_group, lng_group, competitor_name, country_code
+ HAVING recent_samples >= 2
+ ```
+
+#### 3.1.5 `cron_kazan_adjuster.php`
+- **الجدولة:** كل 10 دقائق
+- **الوظيفة:** يخفف عمولة Siro (Kazan) في المناطق التي يرتفع فيها سعر المنافسين
+- **آلية العمل:**
+ - يقرأ `surge:opportunities:{country}` من Redis
+ - للخلايا ذات surge > 1.2: يطبق تخفيض 30% على العمولة (0.70x)
+ - للخلايا ذات surge 1.05-1.2: يطبق تخفيض 15% (0.85x)
+ - يحفظ في Redis: `surge:kazan_discounts:{country}` مع TTL 20 دقيقة
+- **ملاحظة:** يستدعي دالة `getRedisConnection()` غير المعرّفة - سيفشل في وقت التشغيل
+
+#### 3.1.6 `cron_seasonal_pricing.php`
+- **الجدولة:** كل 30-60 دقيقة
+- **الوظيفة:** يطبق مضاعفات موسمية (رمضان، عيد، طقس سيء)
+- **القواعد:**
+ - `ramadan_iftar` (18:00-20:00): 1.25x
+ - `eid`: 1.15x (غير نشط حاليًا)
+ - `severe_weather`: 1.30x (غير نشط حاليًا)
+- **ملاحظة:** نفس مشكلة `getRedisConnection()` غير المعرّفة
+
+#### 3.1.7 `cron_weekly_health_report.php`
+- **الجدولة:** أسبوعيًا (ليلة الأحد)
+- **الوظيفة:** ينشئ تقرير صحة السوق الأسبوعي
+- **المقاييس:**
+ - **PCI (Price Competitiveness Index):** `siroPrice / compPrice`
+ - **حصة السوق:** % من الرحلات التي Siro فيها أرخص
+ - **عدد الشذوذ (anomalies):** من جدول `price_anomalies`
+ - **عدد الحملات:** من جدول `marketing_campaigns_log`
+- **Redis:** لا يستخدم
+- **MySQL:** يدرج في `market_health_reports`
+
+---
+
+### 3.2 نظام التسعير الديناميكي - `backend/ride/pricing/`
+
+#### 3.2.1 `auto_adapt.php`
+- **الجدولة:** كل 30-60 دقيقة
+- **الوظيفة:** يعدل جميع أسعار Siro بناءً على أدنى متوسط سعر للمنافسين
+- **المعادلة:** `new_price_per_km = lowest_competitor_avg * 0.92` (أقل من المنافسين بـ 8%)
+- **النطاق:** بين 85% و 115% من السعر الحالي
+- **الأعمدة المحدثة:** speedPrice, comfortPrice, ladyPrice, electricPrice, vanPrice, deliveryPrice, mishwarVipPrice, fixedPrice, awfarPrice
+- **الدول:** SY, JO, EG, IQ
+- **SQL الرئيسي:**
+ ```sql
+ SELECT competitor_name, AVG(price_per_km) AS avg_ppm
+ FROM competitor_prices
+ WHERE country_code = :cc AND created_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR)
+ AND price_per_km > 0
+ GROUP BY competitor_name
+ ORDER BY avg_ppm ASC
+ ```
+
+#### 3.2.2 `get.php` (نقطة تسعير الرحلة - 511 سطرًا)
+- **الوظيفة:** يحسب سعر الرحلة عند الطلب (أثناء تشغيل التطبيق)
+- **آلية العمل المعقدة:**
+ 1. **حساب الخلية الجغرافية:** من إحداثيات الراكب (~1.5km)
+ 2. **قراءة الطلب:** `$redis->get("demand:grid:" . $grid_id)`
+ 3. **قراءة surge المنافسين:** من `surge:opportunities` في Redis
+ 4. **حساب توفر السائقين:** `$redisLocation->georadius()` ضمن 0.75km
+ 5. **Surge Calculation:** إذا `طلب/سائقين > 1.2`، يطبق مضاعف يصل إلى 3.0x
+ 6. **التسعير الزمني:** الليل (21:00-01:00) ← `latePrice`، الفجر (01:00-05:00) ← مضاعف، الظهر (14:00-17:00) ← `heavyPrice`
+ 7. **تخفيضات المسافات الطويلة:** 40km+ و 100km+
+ 8. **مطابقة المنافسين:** مستويين من المطابقة:
+ - المستوى 1: يطابق نقطة البداية والنهاية معًا
+ - المستوى 2: يطابق نقطة البداية فقط
+ - يخفض السعر بنسبة 8% إذا كان السعر المحسوب أعلى من متوسط المنافسين
+ 9. **العمولة:** `price * (1 + kazanPercent / 100)`
+ 10. **التحقق من العروض:** يتحقق من جدول `promos`
+ 11. **ديون الراكب:** يقرأ من Redis `passenger_debt_{id}`
+ 12. **توليد توكن:** يشفر حمولة السعر بانتهاء صلاحية 7 دقائق لمنع التلاعب
+- **المفاتيح الأساسية:** `kazan`, `promos`, `competitor_prices`
+- **Redis:** `demand:grid:{id}`, `surge:opportunities`, `passenger_debt_{id}`
+
+---
+
+### 3.3 نظام الخريطة الحرارية (Heatmap) - `backend/ride/heatmap/`
+
+#### 3.3.1 `log_demand.php`
+- **الوظيفة:** يسجل الطلب عند كل خلية جغرافية
+- **Redis:** `INCR` على `demand:grid:{grid_id}` مع TTL 60 ثانية
+- **يُستدعى من:** تطبيق الراكب عند طلب رحلة
+
+#### 3.3.2 `get_surge_heatmap.php`
+- **الوظيفة:** يعرض بؤر surge لتطبيق السائق (Driver App)
+- **المصادقة:** يتطلب دور `driver`
+- **Redis:** يقرأ `surge:opportunities:{countryCode}`
+- **الفلترة:** فقط الخلايا ذات مضاعف > 1.05
+
+#### 3.3.3 `heatmap_live.php`
+- **الوظيفة:** خريطة حية لتطبيق الكابتن - تجمع بين الطلب وتوفر السائقين
+- **تحذير:** يستخدم `$redis->keys("demand:grid:*")` - أمر `KEYS` بطيء مع عدد كبير من المفاتيح
+- **التصنيف:** عالي (نسبة > 2.0 أو عدد ≥ 5)، متوسط (نسبة > 1.2 أو عدد ≥ 3)، منخفض
+
+---
+
+### 3.4 نقط نهاية التسويق - `backend/Admin/marketing/`
+
+#### 3.4.1 `trigger_campaign.php` (200 سطر - الأكثر تعقيدًا)
+- **الوظيفة:** يطلق حملة تسويقية مدعومة بالذكاء الاصطناعي
+- **الخطوات:**
+ 1. يجلب آخر 10 أسعار منافسين من MySQL
+ 2. يستدعي `SiroGeminiService->analyzeMarketAndDraftCampaign()` مع بيانات السوق
+ 3. إذا تم اكتشاف فرصة (`opportunity_detected`):
+ - يجد الركاب المستهدفين من `passenger_opening_locations`
+ - ينشئ كود خصم في جدول `promos` (صالحة 7 أيام)
+ - لكل راكب:
+ - إذا لديه FCM token: يرسل إشعار دفع
+ - إذا لا: يتحقق من anti-spam (24 ساعة)، ثم WhatsApp → SMS
+ 4. يسجل في `admin_audit_log`
+- **الخدمات الخارجية:** Google Gemini, WhatsApp Bot, Firebase Cloud Messaging
+- **الحماية:** Anti-spam (24h cooldown للـ SMS/WhatsApp)
+
+#### 3.4.2 `ai_price_prediction.php`
+- **الوظيفة:** يتوقع ساعات الذروة بناءً على بيانات 14 يومًا
+- **المنطق:** `SELECT HOUR(created_at) FROM price_anomalies WHERE anomaly_type = 'opportunity' GROUP BY HOUR ORDER BY COUNT(*) DESC LIMIT 3`
+- **النتيجة:** أفضل 3 ساعات متوقعة + نسبة ثقة 85%
+
+#### 3.4.3 `what_if_simulator.php`
+- **الوظيفة:** محاكي "ماذا لو" - يقترح السعر الأمثل
+- **المنطق:**
+ - يأخذ `speed_price` مقترح
+ - يحاكي السعر لآخر 500 رحلة منافس
+ - يحسب PCI الجديد وحصة السوق المتوقعة
+ - التوصية: PCI < 0.8 تحذير (ربح قليل)، 0.9-0.95 ممتاز، > 1.0 خطر
+
+#### 3.4.4 `surge_opportunity_index.php`
+- **الوظيفة:** نسخة Admin من `cron_surge_opportunity.php` - استعلام فوري
+- **الفرق:** يُستدعى عبر HTTP، يعرض تفاصيل كل منطقة ومنافس
+
+#### 3.4.5 `winback_hotspot_targets.php`
+- **الوظيفة:** يجد الركاب الخاملين (30 يوم بدون رحلة) في مناطق surge
+- **Redis:** يقرأ `surge:opportunities:{countryCode}`
+- **MySQL:** `users JOIN passenger_opening_locations`
+
+#### 3.4.6 `get_price_gap_heatmap.php`
+- **الوظيفة:** خريطة حرارية توضح أين Siro أرخص/أغلى من المنافسين
+- **المنطق:** لكل خلية جغرافية يحسب `pci = currentSpeedPrice / avg_competitor_price` و `weight = pci - 1.0` مقيد بـ [-1, 1]
+
+#### 3.4.7 باقي نقاط النهاية
+| الملف | الوظيفة |
+|-------|---------|
+| `get_campaigns_log.php` | سجل الحملات التسويقية |
+| `get_market_anomalies.php` | الشذوذ السعري + آخر أسعار المنافسين |
+| `get_market_share_analytics.php` | بيانات حصة السوق للرسوم البيانية (آخر 12 أسبوع) |
+| `get_price_comparison.php` | مقارنة الأسعار: متوسطات الساعة، PCI حسب المنطقة، أسعار Siro |
+| `get_telemetry.php` | إحصائيات استخدام النظام: عدد الحملات، التكلفة التقديرية |
+
+---
+
+### 3.5 خدمة الذكاء الاصطناعي - `backend/core/Services/SiroGeminiService.php`
+
+- **النموذج:** `gemini-1.5-flash`
+- **الوظيفة:** تحليل السوق وكتابة الحملات التسويقية بالعربية
+- **المخرجات:** JSON يحتوي على `opportunity_detected`, `campaign_text`, `discount_percent`, `message_type`
+- **التكيف:** يضبط اللهجة حسب البلد (سوري، أردني، مصري، عراقي)
+
+---
+
+## 4. تدفق البيانات (Data Flow)
+
+```
+generate_price_tasks.php (cron/15min)
+ │
+ │ LPUSH → queue:bot:tasks (Redis)
+ ▼
+Android Bot (Scraper) ←→ worker.php (API)
+ │
+ │ POST result → INSERT competitor_prices
+ ▼
+┌────────────────────────────────────────────────────────────┐
+│ competitor_prices (MySQL) │
+│ country_code | lat | lng | price_per_km | competitor_name │
+└─────────────────────────────────────────────────────────────┘
+ │
+ ├─── cron_surge_opportunity.php (cron/10min)
+ │ │ SELECT baseline vs current
+ │ │ WRITE surge:opportunities (Redis)
+ │ ▼
+ ├─── cron_kazan_adjuster.php (cron/10min)
+ │ │ READ surge:opportunities:{CC}
+ │ │ WRITE surge:kazan_discounts:{CC}
+ │ ▼
+ ├─── cron_seasonal_pricing.php (cron/30-60min)
+ │ │ WRITE surge:seasonal:{CC}
+ │ ▼
+ ├─── auto_adapt.php (cron/30-60min)
+ │ │ SELECT AVG(price_per_km) → UPDATE kazan
+ │ ▼
+ ├─── cron_weekly_health_report.php (cron/weekly)
+ │ │ SELECT → INSERT market_health_reports
+ │ ▼
+ ├─── ride/pricing/get.php (API - عند طلب رحلة)
+ │ │ READ Redis (surge, demand, debt)
+ │ │ SELECT (competitor_prices, kazan, promos)
+ │ ▼
+ └─── Admin/marketing/*.php (API - لوحة التحكم)
+ │ READ (competitor_prices, anomalies, etc.)
+ │ WRITE (promos, campaigns_log, audit_log)
+ │ CALL Gemini API
+```
+
+---
+
+## 5. قاعدة البيانات وجداولها
+
+| الجدول | العمليات (SELECT) | العمليات (INSERT/UPDATE) |
+|--------|:-----------------:|:------------------------:|
+| `competitor_prices` | cron_surge_opportunity, cron_weekly_health, surge_opportunity_index, get_price_comparison, get_price_gap_heatmap, what_if_simulator, trigger_campaign, auto_adapt, pricing/get | worker.php (INSERT), generate_price_tasks (CREATE TABLE) |
+| `kazan` | cron_weekly_health, get_price_comparison, get_price_gap_heatmap, what_if_simulator, pricing/get | auto_adapt (UPDATE) |
+| `price_anomalies` | ai_price_prediction, get_market_anomalies, cron_weekly_health, get_telemetry | (خارج نطاق هذه الدراسة) |
+| `market_health_reports` | get_market_share_analytics | cron_weekly_health (INSERT) |
+| `marketing_campaigns_log` | get_campaigns_log, cron_weekly_health, get_telemetry | trigger_campaign (INSERT) |
+| `passenger_opening_locations` | trigger_campaign, winback_hotspot_targets | (خارج النطاق) |
+| `promos` | pricing/get | trigger_campaign (INSERT) |
+| `passengers` | get_campaigns_log, trigger_campaign | (خارج النطاق) |
+| `tokens` | trigger_campaign | (خارج النطاق) |
+| `users` | winback_hotspot_targets | (خارج النطاق) |
+| `admin_audit_log` | - | trigger_campaign (INSERT via logAudit) |
+
+---
+
+## 6. خريطة مفيكات Redis
+
+| نمط المفتاح | يُكتب بواسطة | يُقرأ بواسطة | TTL |
+|:-----------:|:------------:|:------------:|:---:|
+| `surge:opportunities` | cron_surge_opportunity, surge_opportunity_index | pricing/get, get_surge_heatmap | 600s |
+| `surge:opportunities:{CC}` | cron_kazan_adjuster | winback_hotspot_targets, get_surge_heatmap | 1200s |
+| `surge:kazan_discounts:{CC}` | cron_kazan_adjuster | (ride logic) | 1200s |
+| `surge:seasonal:{CC}` | cron_seasonal_pricing | (ride logic) | 3600s |
+| `queue:bot:tasks` | generate_price_tasks | worker.php (RPOP) | list |
+| `competitor:price_history:{app}` | worker.php | (تحليلات مستقبلية) | list/50 |
+| `demand:grid:{grid}` | log_demand | pricing/get, heatmap_live | 60s |
+| `passenger_debt_{id}` | (من نظام المحفظة) | pricing/get | متغير |
+
+---
+
+## 7. الخدمات الخارجية المستخدمة
+
+| الخدمة | الاستخدام | الملف المرتبط |
+|--------|:---------:|:-------------:|
+| **Google Gemini AI** | إنشاء محتوى الحملات التسويقية | `SiroGeminiService.php`, `trigger_campaign.php` |
+| **Firebase Cloud Messaging** | إشعارات الدفع للركاب | `trigger_campaign.php`, `FcmService.php` |
+| **WhatsApp Bot Servers** | إرسال رسائل واتساب | `trigger_campaign.php` |
+| **Android Bot (Scraper)** | جمع أسعار المنافسين | `worker.php`, `generate_price_tasks.php` |
+| **Location Socket Server** | مواقع السائقين اللحظية | `heatmap_live.php`, `pricing/get.php` |
+
+---
+
+## 8. ملفات الخلفية - مراجعة أمنية شاملة
+
+### 8.1 ملفات اختبار/تجربة عالية الخطورة (يجب حذفها فورًا)
+
+#### `backend/test_add_driver_and_car.php`
+- **الوصف:** سكربت اختبار يُنشئ سائق وسيارة في قاعدة البيانات مباشرة
+- **الخطر:** لا يتطلب أي مصادقة - أي زائر يمكنه إنشاء حسابات سائقين وهمية
+- **التصنيف:** **🚨 عالي جدًا - احذف فورًا**
+
+#### `backend/test_signed_pricing.php`
+- **الوصف:** سكربت اختبار يتحايل على المصادقة (`define('TESTING_BYPASS_AUTH', true)`)
+- **الخطر:** يمكنه إنشاء رحلات حقيقية ببيانات مزيفة
+- **التصنيف:** **🚨 عالي جدًا - احذف فورًا**
+
+#### `backend/diagnose_fingerprint.php`
+- **الوصف:** أداة تشخيص تفضي ببيانات البصمات المخزنة
+- **الخطر:** يعرض البيانات مفككة التشفير بدون مصادقة
+- **التصنيف:** **🚨 عالي جدًا - احذف فورًا**
+
+#### `backend/diagnose_login.php`
+- **الوصف:** أداة تشخيص تسجيل الدخول - تعرض أرقام الهواتف والأسماء
+- **الخطر:** يعرض PII (معلومات شخصية) مفككة التشفير
+- **التصنيف:** **🚨 عالي جدًا - احذف فورًا**
+
+#### `backend/auth/Tester/getTesterApp.php` و `updateTesterApp.php`
+- **الوصف:** نقاط نهاية اختبار بدون مصادقة مع SQL Injection
+- **الخطر:** SQL Injection صريح + لا مصادقة
+- **التصنيف:** **🚨 عالي جدًا - احذف فورًا**
+
+#### `backend/migration_create_table.php`
+- **الوصف:** سكربت إنشاء جدول (كان يجب حذفه بعد الاستخدام)
+- **الخطر:** يمكن إعادة تنفيذه لتعديل schema قاعدة البيانات
+- **التصنيف:** **🚨 عالي جدًا - احذف فورًا**
+
+#### `backend/migrate_driver_passwords.php`
+- **الوصف:** سكربت ترحيل كلمات المرور
+- **الخطر:** يحتوي على بيانات اعتماد قاعدة بيانات في الكود
+- **التصنيف:** **🚨 عالي جدًا - احذف فورًا**
+
+#### `backend/Admin/auth/migration_cryptography.php`
+- **الوصف:** يعيد تشفير جميع الأعمدة المشفرة (أكثر من 15 جدول)
+- **الخطر:** إعادة تنفيذه قد تفسد جميع البيانات المشفرة
+- **التصنيف:** **🚨 عالي - احذف فورًا**
+
+#### `backend/Admin/auth/migrate_db.php`
+- **الوصف:** سكربت ترحيل بنية قاعدة البيانات
+- **الخطر:** يمكنه تعديل schema الإنتاج
+- **التصنيف:** **🚨 عالي - احذف فورًا**
+
+#### `backend/Admin/Staff/add_super_admin.php`
+- **الوصف:** إضافة مشرف (Admin) جديد
+- **الخطر:** التحقق من الصلاحية معلّق (commented out) - أي زائر يمكنه إنشاء super admin
+- **التصنيف:** **🚨 عالي جدًا - احذف فورًا**
+
+#### `backend/Admin/Staff/setup.php`
+- **الوصف:** سكربت إعداد الموظفين الأولي
+- **الخطر:** يمكنه إعادة تعيين جميع صلاحيات المشرفين
+- **التصنيف:** **🚨 عالي - احذف بعد التأكد من الإعداد**
+
+### 8.2 ملفات ترحيل البصمات - مفتاح مشفر مكشوف
+
+#### `siro_admin/lib/views/admin/enceypt/fingerprint_migration.dart`
+#### `siro_admin/lib/views/admin/enceypt/driver_fingerprint_migration.dart`
+- **الوصف:** أدوات Flutter لترحيل البصمات
+- **الخطر:** يحتويان على مفتاح المشرف (admin key) بشكل نصي صريح:
+ ```
+ 'iuyweiruinakjbfkajkjlkmalkcxnlahd'
+ ```
+- **التصنيف:** **🔥 خطير جدًا - احذف فورًا وغيّر المفتاح في السيرفر**
+
+### 8.3 ملفات "ggg" - أدوات التشفير
+
+| الملف | الوظيفة | المصادقة | التصنيف |
+|-------|:-------:|:--------:|:-------:|
+| `backend/ggg.php` | تشفير/فك تشفير للمشرف | JWT admin/super_admin | ⚠️ متوسط |
+| `backend/Admin/ggg.php` | تشفير/فك تشفير | ADMIN_PHONE_NUMBERS | ⚠️ متوسط |
+
+**التوصية:** احذف بعد انتهاء الترحيل. هذه أدوات خطيرة لأنها تسمح بفك تشفير أي بيانات.
+
+### 8.4 ملفات الترحيل (Migration Files)
+
+| الملف | الوظيفة | التصنيف |
+|-------|:-------:|:-------:|
+| `backend/migration/get_all_fingerprints.php` | تصدير بصمات الركاب | ⚠️ متوسط |
+| `backend/migration/get_all_driver_fingerprints.php` | تصدير بصمات السائقين | ⚠️ متوسط |
+| `backend/migration/update_fingerprint_admin.php` | تحديث بصمة راكب | ⚠️ متوسط |
+| `backend/migration/update_driver_fingerprint_admin.php` | تحديث بصمة سائق | ⚠️ متوسط |
+
+**التوصية:** احذف جميع ملفات الترحيل بعد التأكد من اكتمال الترحيل في الإنتاج.
+
+### 8.5 ملفات أخرى
+
+#### `backend/intaleq_v1_secure_latest.md`
+- **الوصف:** ملف توثيق ضخم (33,608 سطر) يحتوي على كود المصدر الكامل
+- **الخطر:** إذا كان الوصول إليه ممكنًا عبر الويب، فإنه يسرب كامل قاعدة الشفرة
+- **التصنيف:** ⚠️ متوسط - انقل خارج جذر الويب أو احذف
+
+#### `siro_admin/lib/debug_jwt.dart`
+- **الوصف:** سكربت Flutter لاختبار JWT
+- **الخطر:** يكشف بنية JWT ومنهجية التشفير
+- **التصنيف:** ⚠️ منخفض - احذف من بنية الإنتاج
+
+---
+
+## 9. قائمة الملفات المطلوب حذفها فوراً
+
+### أولوية قصوى - خطر أمني مباشر 🔴
+
+| الملف | السبب |
+|:------|:-----:|
+| `backend/test_add_driver_and_car.php` | إنشاء سائقين بدون مصادقة |
+| `backend/test_signed_pricing.php` | تجاوز المصادقة |
+| `backend/diagnose_fingerprint.php` | كشف بيانات حساسة بدون مصادقة |
+| `backend/diagnose_login.php` | كشف PII بدون مصادقة |
+| `backend/migration_create_table.php` | تعديل schema بدون مصادقة |
+| `backend/migrate_driver_passwords.php` | بيانات اعتماد DB في الكود |
+| `backend/Admin/auth/migration_cryptography.php` | إعادة تشفير شامل |
+| `backend/Admin/auth/migrate_db.php` | تعديل schema الإنتاج |
+| `backend/Admin/Staff/add_super_admin.php` | صلاحية المشرِف معلّقة |
+| `backend/Admin/Staff/setup.php` | إعادة تعيين الصلاحيات |
+| `backend/auth/Tester/getTesterApp.php` | SQL Injection + لا مصادقة |
+| `backend/auth/Tester/updateTesterApp.php` | SQL Injection + لا مصادقة |
+| `siro_admin/lib/views/admin/enceypt/fingerprint_migration.dart` | مفتاح مكشوف 🔥 |
+| `siro_admin/lib/views/admin/enceypt/driver_fingerprint_migration.dart` | مفتاح مكشوف 🔥 |
+
+### أولوية متوسطة - أمان أو تنظيف 🟡
+
+| الملف | السبب |
+|:------|:-----:|
+| `backend/ggg.php` | أداة تشفير للمشرفين - احذف بعد الترحيل |
+| `backend/Admin/ggg.php` | أداة تشفير - احذف بعد الترحيل |
+| `backend/migration/get_all_fingerprints.php` | ترحيل بصمات - احذف بعد التأكد |
+| `backend/migration/get_all_driver_fingerprints.php` | ترحيل بصمات - احذف بعد التأكد |
+| `backend/migration/update_fingerprint_admin.php` | ترحيل بصمات - احذف بعد التأكد |
+| `backend/migration/update_driver_fingerprint_admin.php` | ترحيل بصمات - احذف بعد التأكد |
+| `backend/Admin/auth/register.php` | مراجعة - هل ما زال مستخدمًا؟ |
+| `siro_admin/lib/debug_jwt.dart` | تصحيح JWT - احذف من الإنتاج |
+| `backend/intaleq_v1_secure_latest.md` (33k سطر) | كود مصدر كامل - انقل أو احذف |
+
+### أولوية منخفضة - تحسين أمني 🟢
+
+| الملف | السبب |
+|:------|:-----:|
+| `walletintaleq.intaleq.xyz/v2/main/ride/payment/delete.php` | ملف فارغ تمامًا |
+| `backend/logo.png` | (تحقق ما إذا كان ضروريًا) |
+
+---
+
+## 10. ثغرات SQL Injection
+
+### 10.1 ملفات ذات SQL Injection مباشر
+
+هذه الملفات تستخدم `prepare()` بعد `$sql = "... WHERE id = '$var'"` مما يبطل تمامًا حماية prepared statements:
+
+| الملف | السطر | الكود المخترق |
+|:------|:-----:|:--------------|
+| `wallet/ride/passengerWallet/delete.php` | `$sql = "DELETE FROM passengerWallet WHERE id = '$id'"` |
+| `wallet/ride/passengerWallet/update.php` | `$sql = "UPDATE passengerWallet SET balance = '$balance' WHERE id = '$id'"` |
+| `wallet/ride/passengerWallet/getAllPassengerTransaction.php` | `WHERE passenger_id = '$passenger_id'` |
+| `wallet/ride/passengerWallet/getPassengerWalletArchive.php` | `WHERE passenger_id = '$passenger_id'` |
+| `wallet/ride/driverPayment/delete.php` | `DELETE FROM paymentsDriverPoints WHERE id = '$id'` |
+| `wallet/ride/driverPayment/update.php` | `UPDATE ... SET amount = '$amount', ...` |
+| `wallet/ride/driverPayment/add.php` | `INSERT INTO paymentsDriverPoints VALUES ('$amount', '$paymentMethod', '$driverID')` |
+| `wallet/ride/driverWallet/get.php` | `WHERE driverID = '$driverID'` (في subquery) |
+| `wallet/ride/payment/update.php` | بناء `SET` ديناميكي + SQL Injection |
+| `backend/Admin/adminUser/get.php` | `WHERE device_number = '$device_number'` |
+
+### 10.2 الإجراء المطلوب
+
+1. **فوري:** استبدال `$sql = "... WHERE id = '$var'"` بـ `$sql = "... WHERE id = :id"` مع `bindParam(':id', $id)`
+2. **مراجعة:** جميع ملفات `walletintaleq/` لديها مشكلة أمنية منهجية - تحتاج مراجعة كاملة
+3. **تحذير:** ملفات `driverPayment/` لا تملك أي مصادقة JWT - يمكن لأي زوار الوصول إليها
+
+---
+
+## 11. التوصيات النهائية
+
+### أولاً - فوري (خلال 24 ساعة)
+1. **حذف جميع الملفات عالية الخطورة (القسم 9 - 🔴)**
+2. **تغيير مفتاح `MIGRATION_ADMIN_KEY`** في جميع السيرفرات (المفتاح `iuyweiruinakjbfkajkjlkmalkcxnlahd` أصبح مكشوفًا)
+3. **إصلاح ثغرات SQL Injection** في ملفات المحفظة (القسم 10)
+
+### ثانيًا - قصير المدى (خلال أسبوع)
+4. **توحيد السناك بار** حسب الخطة في القسم 1
+5. **حذف ملفات الترحيل** بعد التأكد من اكتمال الترحيل
+6. **نقل أو حذف `intaleq_v1_secure_latest.md`** من جذر الويب
+7. **مراجعة جميع ملفات `walletintaleq/`** - معظمها يفتقر إلى مصادقة JWT
+
+### ثالثًا - طويل المدى (شهر)
+8. **تبسيط الـ Redis architecture** - حاليًا يوجد اتصالان منفصلان (main + location)
+9. **استبدال `KEYS` command** في `heatmap_live.php` بـ `SCAN`
+10. **إصلاح الدوال غير المعرّفة** `getRedisConnection()` و `resolveAdminCountry()`
+11. **فصل إنشاء الجداول** من ملفات cron إلى migration scripts
+12. **توسيع التغطية الجغرافية** - حاليًا تتركز على دمشق فقط
+
+### رابعًا - تحسينات إضافية
+13. **إضافة مزيد من التوثيق** للـ cron jobs (جدولة، مخرجات، مراقبة)
+14. **إضافة نظام إنذار** عند فشل أحد cron jobs
+15. **تشفير جميع مفاتيح API** في ملف `.env` فقط
+16. **تطبيق pipeline CI/CD** يستبعد ملفات الاختبار والترحيل من الإنتاج
+
+---
+
+*تم إعداد هذه الدراسة بتاريخ 26 يونيو 2026 بناءً على تحليل شامل للكود المصدري لمشروع Siro.*
diff --git a/backend/intaleq_v1_secure_latest.md b/knowledge/intaleq_v1_secure_latest.md
similarity index 100%
rename from backend/intaleq_v1_secure_latest.md
rename to knowledge/intaleq_v1_secure_latest.md
diff --git a/siro_admin/lib/constant/links.dart b/siro_admin/lib/constant/links.dart
index 9b6b7597..316c0e6b 100644
--- a/siro_admin/lib/constant/links.dart
+++ b/siro_admin/lib/constant/links.dart
@@ -1,6 +1,5 @@
import '../env/env.dart';
import '../main.dart';
-import 'box_name.dart';
class AppLink {
static String seferPaymentServer =
diff --git a/siro_admin/lib/controller/admin/captain_admin_controller.dart b/siro_admin/lib/controller/admin/captain_admin_controller.dart
index a256772e..28062ff5 100644
--- a/siro_admin/lib/controller/admin/captain_admin_controller.dart
+++ b/siro_admin/lib/controller/admin/captain_admin_controller.dart
@@ -4,8 +4,8 @@ import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:local_auth/local_auth.dart';
-import '../../constant/colors.dart';
import '../../constant/links.dart';
+import '../../views/widgets/snackbar.dart';
import '../functions/crud.dart';
class CaptainAdminController extends GetxController {
@@ -60,8 +60,7 @@ class CaptainAdminController extends GetxController {
captainData = d;
} else {
captainData = {};
- Get.snackbar('Error', 'No captain found with this phone number',
- backgroundColor: AppColor.redColor);
+ mySnackbarError('No captain found with this phone number');
}
isLoading = false;
@@ -102,17 +101,16 @@ class CaptainAdminController extends GetxController {
if (didAuthenticate) {
// User authenticated successfully, proceed with payment
await addCaptainPrizeToWallet();
- Get.snackbar('Prize Added', '', backgroundColor: AppColor.greenColor);
+ mySnackbarSuccess('Prize Added');
} else {
// Authentication failed, handle accordingly
- Get.snackbar('Authentication failed', '',
- backgroundColor: AppColor.redColor);
+ mySnackbarError('Authentication failed');
// 'Authentication failed');
}
} else {
// Local authentication not available, proceed with payment without authentication
await addCaptainPrizeToWallet();
- Get.snackbar('Prize Added', '', backgroundColor: AppColor.greenColor);
+ mySnackbarSuccess('Prize Added');
}
} catch (e) {
rethrow;
diff --git a/siro_admin/lib/controller/admin/complaint_controller.dart b/siro_admin/lib/controller/admin/complaint_controller.dart
index 26a30623..2fbbada1 100644
--- a/siro_admin/lib/controller/admin/complaint_controller.dart
+++ b/siro_admin/lib/controller/admin/complaint_controller.dart
@@ -1,6 +1,7 @@
import 'dart:convert';
import 'package:get/get.dart';
import '../../constant/links.dart';
+import '../../views/widgets/snackbar.dart';
import '../functions/crud.dart';
class ComplaintController extends GetxController {
@@ -41,7 +42,7 @@ class ComplaintController extends GetxController {
complaintList.clear();
}
} catch (e) {
- Get.snackbar("خطأ", "فشل جلب الشكاوى: $e");
+ mySnackbarError("فشل جلب الشكاوى: $e");
} finally {
isLoading.value = false;
}
@@ -61,7 +62,7 @@ class ComplaintController extends GetxController {
}
return false;
} catch (e) {
- Get.snackbar("خطأ", "فشل تحديث الشكوى: $e");
+ mySnackbarError("فشل تحديث الشكوى: $e");
return false;
} finally {
isLoading.value = false;
diff --git a/siro_admin/lib/controller/admin/driver_docs_controller.dart b/siro_admin/lib/controller/admin/driver_docs_controller.dart
index ef88f70e..c6c0716c 100644
--- a/siro_admin/lib/controller/admin/driver_docs_controller.dart
+++ b/siro_admin/lib/controller/admin/driver_docs_controller.dart
@@ -1,6 +1,7 @@
import 'dart:convert';
import 'package:get/get.dart';
import '../../constant/links.dart';
+import '../../views/widgets/snackbar.dart';
import '../functions/crud.dart';
class DriverDocsController extends GetxController {
@@ -49,7 +50,7 @@ class DriverDocsController extends GetxController {
}
}
} catch (e) {
- Get.snackbar("خطأ", "فشل جلب السائقين: $e");
+ mySnackbarError("فشل جلب السائقين: $e");
} finally {
isLoading.value = false;
isMoreLoading.value = false;
@@ -70,7 +71,7 @@ class DriverDocsController extends GetxController {
}
}
} catch (e) {
- Get.snackbar("خطأ", "فشل جلب تفاصيل السائق: $e");
+ mySnackbarError("فشل جلب تفاصيل السائق: $e");
}
return null;
}
@@ -88,7 +89,7 @@ class DriverDocsController extends GetxController {
}
return false;
} catch (e) {
- Get.snackbar("خطأ", "فشل اعتماد السائق: $e");
+ mySnackbarError("فشل اعتماد السائق: $e");
return false;
} finally {
isLoading.value = false;
diff --git a/siro_admin/lib/controller/admin/kazan_controller.dart b/siro_admin/lib/controller/admin/kazan_controller.dart
index 9ab1ac84..9be1fd9c 100644
--- a/siro_admin/lib/controller/admin/kazan_controller.dart
+++ b/siro_admin/lib/controller/admin/kazan_controller.dart
@@ -1,34 +1,51 @@
import 'dart:convert';
import 'package:get/get.dart';
import '../../constant/links.dart';
+import '../../views/widgets/snackbar.dart';
import '../functions/crud.dart';
class KazanController extends GetxController {
var kazanData = {}.obs;
var isLoading = false.obs;
+ var selectedCountry = 'Syria'.obs;
final CRUD _crud = CRUD();
+ final List