# تحليل نظام متابعة الرحلات في لوحة تحكم المشرف (Siro Admin) ## دراسة شاملة للبحث، التتبع، التعديل، والتعامل مع الرحلات المعلقة --- ## 📋 فهرس المحتويات 1. [النظام الحالي لمتابعة الرحلات](#النظام-الحالي) 2. [تحليل شاشات وأدوات متابعة الرحلات](#تحليل-الشاشات) 3. [حالات الرحلة الفعلية من قاعدة البيانات](#حالات-الرحلة) 4. [مشكلة رقم الهاتف والدولة](#مشكلة-رقم-الهاتف-والدولة) 5. [الـ Endpoints المستخدمة واستجاباتها](#الـ-endpoints) 6. [ما هو موجود وما هو مفقود](#ما-هو-موجود-وما-هو-مفقود) 7. [التوصيات والتحسينات المطلوبة مع الإصلاحات](#التوصيات-والإصلاحات) --- ## النظام الحالي ### الملفات المشاركة في متابعة الرحلات | الملف | الدور | |-------|--------| | `siro_admin/lib/views/admin/rides/ride_lookup_page.dart` | لوحة إدارة الرحلات (Dashboard) - الأحدث | | `siro_admin/lib/views/admin/rides/rides.dart` | إحصائيات الرحلات الشهرية (قديم) | | `siro_admin/lib/views/admin/drivers/monitor_ride.dart` | شاشة مراقبة رحلة محددة بالخريطة | | `siro_admin/lib/controller/rides/ride_lookup_controller.dart` | تحكم البحث عن رحلة وتحديث حالتها | | `siro_admin/lib/controller/admin/ride_admin_controller.dart` | تحكم إحصائيات الرحلات | ### ملفات الباك إند | الملف | الوظيفة | |-------|---------| | `backend/Admin/rides/get_rides_by_status.php` | جلب الرحلات حسب الحالة (Begin, New, Finished, Canceled) | | `backend/Admin/rides/admin_get_rides_by_phone.php` | البحث عن رحلة برقم هاتف الراكب (مشفر) | | `backend/Admin/rides/admin_update_ride_status.php` | تحديث حالة الرحلة (Pending→Completed...) | | `backend/Admin/rides/monitorRide.php` | مراقبة رحلة نشطة برقم الهاتف (يبحث في السائق والراكب) | | `backend/Admin/rides/get_driver_live_pos.php` | جلب الموقع اللحظي للسائق من `car_locations` | --- ## حالات الرحلة الفعلية في قاعدة البيانات ### 📌 هيكل الجدول ```sql CREATE TABLE `ride` ( `id` int NOT NULL AUTO_INCREMENT, `status` varchar(200) NOT NULL DEFAULT 'nothing', -- ❌ NOT ENUM! نص عادي ... ) ENGINE=InnoDB; CREATE TABLE `waitingRides` ( `id` varchar(100) NOT NULL, `status` varchar(200) NOT NULL DEFAULT 'nothing', -- ❌ NOT ENUM! نص عادي ... ) ENGINE=InnoDB; ``` ### 🗺️ جميع حالات الرحلة المستخدمة فعلياً في الكود | الحالة في DB | المعنى | أين تستخدم | |-------------|--------|------------| | `nothing` | القيمة الافتراضية | عند إنشاء الرحلة في `add.php` و `addWaitingRide.php` | | `New` | رحلة جديدة (لم يتم تعيين سائق بعد) | في `Admin/rides/get_rides_by_status.php` للـ Admin Dashboard | | `waiting` | الرحلة قيد البحث عن سائق | في `retry_search_drivers.php` عند إعادة البحث | | `wait` | في انتظار (مستخدم في بعض السيرفرات) | في `getRideStatusFromStartApp.php` و `cancelRideByPassenger.php` | | `Apply` | سائق قبل الرحلة (قيد التوجه) | في `acceptRide.php`, `overLay/get.php`, `driver_statistic.php` | | `Applied` | تم قبول التطبيق | في `get_rides_by_status.php` (مع Begin) | | `arrived` | السائق وصل إلى موقع الراكب (lowercase) | في `arrive_ride.php` | | `Arrived` | السائق وصل (uppercase) | في `get_rides_by_status.php` (مستخدم أيضاً) | | `Begin` | **الرحلة قيد التشغيل** 🚗 | في `start_ride.php`, `monitorRide.php`, `finish_ride_updates.php` | | `started` | بدأت الرحلة (مرادف لـ Begin) | في `start_ride.php` (رد API) | | `Finished` | **الرحلة اكتملت** ✅ | في معظم التقارير والإحصائيات | | `Cancel` | ملغاة (عام) | في `cancelRideFromDriver.php`, `cancel_ride_by_passenger.php` | | `CancelFromDriver` | ألغى السائق | في `get_rides_by_status.php` | | `CancelFromDriverAfterApply` | ألغى السائق بعد القبول | في `get_rides_by_status.php` | | `CancelFromPassenger` | ألغى الراكب | في `get_rides_by_status.php` | | `TimeOut` | انتهت المهلة الزمنية | في `get_rides_by_status.php` | ### 🔄 تسلسل حالة الرحلة الطبيعي: ``` nothing / New │ ├──→ waiting ──→ Apply ──→ Applied ──→ Arrived ──→ Begin ──→ Finished │ │ │ │ │ │ │ │ │ │ │ └── ✅ مكتملة │ │ │ │ │ │ │ │ │ └── CancelFromDriverAfterApply │ │ │ │ │ │ │ └── CancelFromPassenger │ │ │ │ │ └── TimeOut (انتهاء المهلة) │ │ │ └── CancelFromDriver │ └── Cancel (إلغاء مباشر) ``` ### ❌ المشكلة في Admin Dashboard الحالي: في `get_rides_by_status.php`، التصنيف خاطئ: ```php case 'Begin': $whereClause = "WHERE r.status IN ('Begin','Apply','Applied')"; // ✅ Begin + Apply + Applied break; case 'New': $whereClause = "WHERE r.status = 'New'"; // ✅ جديد break; case 'Completed': $whereClause = "WHERE r.status = 'Finished'"; // ✅ مكتملة break; case 'Canceled': $whereClause = "WHERE r.status IN ('Cancel', 'CancelFromDriverAfterApply', 'TimeOut')"; // ❌ ناقص! break; ``` **🔴 ملاحظات على التصنيف:** 1. `CancelFromDriver` و `CancelFromPassenger` و `Cancel` و `arrived` (lowercase) **غير مشمولة** في أي تصنيف 2. `Apply` و `Applied` مصنفين مع `Begin` (جارية) — هذا صحيح جزئياً لأن السائق في طريقه 3. `TimeOut` مصنف مع `Canceled` — صحيح 4. الرحلة التي حالتها `wait` أو `waiting` **غير معروضة إطلاقاً** في أي تبويب --- ## تحليل الشاشات ### 1️⃣ شاشة إدارة الرحلات (RidesDashboardScreen) **الملف**: `ride_lookup_page.dart` #### الوظائف الحالية: - ✅ عرض الرحلات مصنفة حسب الحالة (جارية، جديدة، مكتملة، ملغاة) - ✅ إحصائيات فورية (عدد الرحلات لكل حالة + الإيرادات + المسافة) - ✅ شريط بحث ديناميكي يصفّي النتائج مباشرة - ✅ الضغط على رحلة → يفتح خريطة التتبع (RideMapMonitorScreen) - ✅ إظهار معلومات السائق والراكب مع أيقونة اتصال (للمشرف العام) #### منطق البحث الحالي: ```dart void filterRides(String query) { displayedRides.value = allRidesList.where((ride) { return ride.driverPhone.contains(query) || ride.passengerPhone.contains(query) || ride.driverName.toLowerCase().contains(query.toLowerCase()) || ride.passengerName.toLowerCase().contains(query.toLowerCase()) || ride.rideId.contains(query); }).toList(); } ``` 🔴 **مشكلة**: البحث يتم **محلياً** (في الذاكرة) بعد جلب كل الرحلات من API. إذا كانت الرحلات كثيرة (100+ رحلة)، البحث لا يشمل الرحلات الغير محملة. 🔴 **مشكلة**: الـ API يجلب فقط آخر 100 رحلة (LIMIT 100) ولا يوجد Pagination. #### الـ API المستخدم: ``` POST $server/Admin/rides/get_rides_by_status.php payload: { "status": "Begin" } ← { "status": "success", "message": [ {...}, {...} ] } ``` #### خريطة التتبع (RideMapMonitorScreen): - ✅ يعرض مسار الرحلة (نقطة بداية → نقطة نهاية) - ✅ يتابع موقع السائق اللحظي (Polling كل 10 ثوانٍ) - ✅ زر اتصال بالسائق أو الراكب - 🔴 **لا يوجد تحديث لحالة الرحلة** من هذه الشاشة --- ### 2️⃣ شاشة مراقبة رحلة (RideMonitorScreen) **الملف**: `monitor_ride.dart` #### الوظائف الحالية: - ✅ إدخال رقم هاتف للبحث عن رحلة نشطة - ✅ عرض خريطة كاملة مع مسار الرحلة وموقع السائق - ✅ تحديث تلقائي (Polling كل 10 ثوانٍ) - ✅ إظهار اسم السائق، حالة الرحلة، السرعة، آخر تحديث #### الـ API المستخدم: ``` POST $server/Admin/rides/monitorRide.php payload: { "phone": "963$phone" } ← { "status": "success", "message": { "ride_details": { ... }, "driver_details": { ... }, "driver_location": { "latitude": ..., "longitude": ..., "speed": ..., "heading": ... } }} ``` #### منطق البحث الحالي: ```dart final response = await CRUD().post( link: apiUrl, payload: {"phone": "963$phone"}, // ⚠️ Hardcoded Syria prefix! ); ``` 🔴 **مشكلة حرجة**: `963$phone` ثابت (سوريا فقط). الأردن يحتاج `962` ومصر تحتاج `20`. #### منطق الباك إند (`monitorRide.php`): ```php $encPhone = $encryptionHelper->encryptData($phone); // يبحث أولاً في جدول driver $driverQuery = $con->prepare("SELECT id FROM driver WHERE phone = :phone LIMIT 1"); // ثم في جدول passengers $customerQuery = $con->prepare("SELECT id FROM passengers WHERE phone = :phone LIMIT 1"); // ثم يجلب آخر رحلة status='Begin' ``` 🔴 **ملاحظة**: `monitorRide.php` يبحث فقط عن الرحلات التي حالتها `Begin`. إذا كانت الرحلة بحالة `Apply` أو `Arrived` لن يجدها. --- ### 3️⃣ شاشة البحث عن رحلة وتحديث حالتها (RideLookupController) **الملف**: `ride_lookup_controller.dart` #### الوظائف الحالية: - ✅ البحث برقم هاتف الراكب (مشفر) عبر `admin_get_rides_by_phone.php` - ✅ عرض بيانات الراكب + آخر رحلة له - ✅ تحديث حالة الرحلة عبر `admin_update_ride_status.php` - ✅ قائمة منسدلة للحالات المسموحة: `Pending, Accepted, EnRoute, Arrived, Started, Completed, Canceled` **🔴 هذه القائمة لا تطابق حالات الرحلة الفعلية في النظام!** | القيمة في التطبيق | هل تستخدم في DB؟ | الحالة الصحيحة | |------------------|------------------|----------------| | `Pending` | ❌ لا | `waiting` أو `New` أو `nothing` | | `Accepted` | ❌ لا | `Apply` أو `Applied` | | `EnRoute` | ❌ لا | `Apply` أو `Applied` (السائق في الطريق) | | `Arrived` | ✅ نعم | `arrived` أو `Arrived` | | `Started` | ❌ لا | `Begin` | | `Completed` | ✅ نعم | `Finished` | | `Canceled` | ✅ نعم | `Cancel` أو `CancelFrom*` | #### الـ API المستخدم للبحث: ``` POST $server/Admin/rides/admin_get_rides_by_phone.php payload: { "phone": "...", "status": "..." } ← { "status": "success", "message": { "passenger": { "id", "first_name", "last_name", "phone" }, "ride": { "id", "status", "start_location", ... } }} ``` 🔴 **مشكلة**: هذا الـ API يبحث فقط في جدول `passengers`. إذا كان الرقم تابعاً لسائق، لن يعثر على شيء. --- ## مشكلة رقم الهاتف والدولة ### كيف يتم تخزين أرقام الهواتف؟ في قاعدة البيانات، جميع الأرقام مشفرة باستخدام `encryptData()`: ```php $encryptedPhone = $encryptionHelper->encryptData($raw); ``` **الأرقام تخزن مع كود الدولة:** - الأردن: `9627XXXXXXXX` - مصر: `2010XXXXXXXX` - سوريا: `9639XXXXXXXX` ### كيف يتم البحث في تطبيق المشرف حالياً؟ #### 1️⃣ في `monitor_ride.dart`: ```dart payload: {"phone": "963$phone"} // المستخدم يدخل: 0992952235 // التطبيق يرسل: 9630992952235 ← غلط! الصحيح: 963992952235 ``` #### 2️⃣ في `admin_get_rides_by_phone.php`: ```php $phone = filterRequest('phone'); $enc_raw = $encryptionHelper->encryptData($raw); // يبحث فقط في passengers ``` ### جدول مقارنة تنسيق الأرقام: | الدولة | كود الدولة | تنسيق الإدخال | التخزين في DB | ما يرسله `monitor_ride.dart` | هل يتطابق؟ | |--------|-----------|--------------|---------------|------------------------------|-----------| | 🇯🇴 الأردن | `962` | `079XXXXXXX` | `96279XXXXXXX` | `963079XXXXXXX` ❌ | لا | | 🇪🇬 مصر | `20` | `010XXXXXXXX` | `2010XXXXXXXX` | `963010XXXXXXXX` ❌ | لا | | 🇸🇾 سوريا | `963` | `0992952235` | `963992952235` | `9630992952235` ❌ | لا (0 زائد) | ### تحليل المشكلة: 1. **المشرف يدخل**: `0992952235` (سوريا) أو `079XXXXXXX` (أردن) 2. **التطبيق يضيف**: `963` ثابت (سوريا فقط) 3. **الباك إند يبحث**: عن الرقم `9630992952235` أو `963079XXXXXXX` 4. **الرقم المخزن**: `963992952235` (بدون 0 بعد كود الدولة) أو `96279XXXXXXX` 5. **النتيجة**: فشل في البحث عن أي رقم غير سوري، وحتى السوري يفشل إذا كان المدخل بـ `0` --- ## الـ Endpoints ### الحالية في `backend/Admin/rides/`: | الإند بوينت | الطريقة | المدخلات | الاستجابة | المشاكل | |------------|---------|----------|-----------|---------| | `get_rides_by_status.php` | POST | `status` (Begin/New/Completed/Canceled) | قائمة رحلات (100) مع تفاصيل السائق والراكب | لا يوجد Pagination + بعض الحالات غير مشمولة (`wait`, `CancelFromDriver`, `arrived`) | | `admin_get_rides_by_phone.php` | POST | `phone` | بيانات الراكب + آخر رحلة | يبحث فقط في passengers | | `admin_update_ride_status.php` | POST | `id, status, reason (optional)` | الرحلة المحدّثة | Whitelist لا يطابق حالات DB الفعلية | | `monitorRide.php` | POST | `phone` | تفاصيل الرحلة + السائق + الموقع | يبحث فقط عن `Begin` + كود الدولة ثابت 963 | | `get_driver_live_pos.php` | POST | `driver_id` | آخر موقع للسائق | يعمل بشكل صحيح | --- ## التوصيات والإصلاحات ### 🔴 الإصلاح 1: تصحيح حالات الرحلة في Admin Dashboard **المشكلة**: `get_rides_by_status.php` يستخدم حالات غير دقيقة. **الحل**: تعديل mapping الحالات ليطابق الواقع: ```php // backend/Admin/rides/get_rides_by_status.php - تصحيح switch ($statusFilter) { case 'Begin': // الرحلات الجارية: من Apply إلى Begin $whereClause = "WHERE r.status IN ('Apply','Applied','Arrived','arrived','Begin')"; break; case 'New': // الرحلات الجديدة: بانتظار سائق $whereClause = "WHERE r.status IN ('New','nothing','waiting','wait')"; break; case 'Completed': $whereClause = "WHERE r.status = 'Finished'"; break; case 'Canceled': // جميع أنواع الإلغاء $whereClause = "WHERE r.status IN ('Cancel','CancelFromDriver','CancelFromDriverAfterApply','CancelFromPassenger','TimeOut')"; break; default: $whereClause = "WHERE r.status = ?"; $params[] = $statusFilter; break; } ``` --- ### 🔴 الإصلاح 2: تصحيح قائمة الحالات المسموحة في RideLookupController **المشكلة**: `statusOptions` في `ride_lookup_controller.dart` لا تطابق حالات DB. **الحل**: تعديل القائمة: ```dart // في ride_lookup_controller.dart final List statusOptions = const [ 'New', // جديد (بدلاً من Pending) 'waiting', // في انتظار سائق 'Apply', // سائق قبل (بدلاً من Accepted) 'Arrived', // وصل السائق 'Begin', // الرحلة بدأت (بدلاً من Started) 'Finished', // مكتملة (بدلاً من Completed) 'Cancel', // إلغاء (بدلاً من Canceled) ]; ``` **وتحديث `admin_update_ride_status.php`**: ```php // backend/Admin/rides/admin_update_ride_status.php $allowed = [ 'New', 'waiting', 'wait', 'Apply', 'Applied', 'Arrived', 'arrived', 'Begin', 'Finished', 'Cancel', 'CancelFromDriver', 'CancelFromPassenger', 'TimeOut' ]; ``` --- ### 🔴 الإصلاح 3: معالجة أرقام الهواتف حسب الدولة **المشكلة**: `monitor_ride.dart` يستخدم `963` ثابت. **الحل**: إضافة دالة لتوحيد تنسيق الرقم: ```dart // في monitor_ride.dart String normalizePhone(String input) { final clean = input.replaceAll(RegExp(r'\D+'), ''); // Syria: 099XXXXXXX or 9639XXXXXXX if (clean.length == 10 && clean.startsWith('09')) return '963${clean.substring(1)}'; if (clean.length == 12 && clean.startsWith('963')) return clean; if (clean.length == 9 && clean.startsWith('9')) return '963$clean'; // Jordan: 079XXXXXXX or 9627XXXXXXX if (clean.length == 10 && clean.startsWith('07')) return '962${clean.substring(1)}'; if (clean.length == 12 && clean.startsWith('962')) return clean; if (clean.length == 9 && clean.startsWith('7')) return '962$clean'; // Egypt: 010XXXXXXXX or 2010XXXXXXXX if (clean.length == 11 && clean.startsWith('01')) return '20${clean.substring(1)}'; if (clean.length == 13 && clean.startsWith('20')) return clean; return clean; } // عند البحث: final normalizedPhone = normalizePhone(phoneInputController.text.trim()); final response = await CRUD().post( link: apiUrl, payload: {"phone": normalizedPhone}, ); ``` **وفي الباك إند (`monitorRide.php` و `admin_get_rides_by_phone.php`)**: ```php function normalizePhone($phone) { $clean = preg_replace('/\D+/', '', $phone); // Syria: remove leading 0 after country code if (strlen($clean) === 10 && strpos($clean, '09') === 0) return '963' . substr($clean, 1); if (strlen($clean) === 12 && strpos($clean, '963') === 0) return $clean; if (strlen($clean) === 9 && strpos($clean, '9') === 0) return '963' . $clean; // Jordan if (strlen($clean) === 10 && strpos($clean, '07') === 0) return '962' . substr($clean, 1); if (strlen($clean) === 12 && strpos($clean, '962') === 0) return $clean; if (strlen($clean) === 9 && strpos($clean, '7') === 0) return '962' . $clean; // Egypt if (strlen($clean) === 11 && strpos($clean, '01') === 0) return '20' . substr($clean, 1); if (strlen($clean) === 13 && strpos($clean, '20') === 0) return $clean; return $clean; } ``` --- ### 🔴 الإصلاح 4: البحث في السائق والراكب معاً (مع جميع حالات الرحلة) **المشكلة**: `admin_get_rides_by_phone.php` يبحث فقط في `passengers` وحالة `Begin` فقط. **الحل**: تعديل `admin_get_rides_by_phone.php` و `monitorRide.php` ليشمل driver + passenger + جميع الحالات النشطة: ```php // backend/Admin/rides/admin_get_rides_by_phone.php - معدل $phone = filterRequest('phone'); if (!$phone) { jsonError("Phone is required"); exit; } $normalizedPhone = normalizePhone($phone); $encPhone = $encryptionHelper->encryptData($normalizedPhone); // 1) ابحث في driver $driverStmt = $con->prepare("SELECT id FROM driver WHERE phone = :phone LIMIT 1"); $driverStmt->execute([':phone' => $encPhone]); $driver = $driverStmt->fetch(); // 2) ابحث في passengers $passengerStmt = $con->prepare("SELECT id FROM passengers WHERE phone = :phone LIMIT 1"); $passengerStmt->execute([':phone' => $encPhone]); $passenger = $passengerStmt->fetch(); if (!$driver && !$passenger) { jsonError('Phone number not found in system'); exit; } // 3) اجلب الرحلة حسب النوع $userId = $driver ? $driver['id'] : $passenger['id']; $userField = $driver ? 'r.driver_id' : 'r.passenger_id'; $filterStatus = filterRequest('status') ?: 'all'; $whereExtra = ''; if ($filterStatus !== 'all') { $whereExtra = "AND r.status = :filter_status"; } $rideStmt = $con->prepare(" SELECT r.*, d.first_name as d_fname, d.last_name as d_lname, d.phone as d_phone, p.first_name as p_fname, p.last_name as p_lname, p.phone as p_phone FROM ride r LEFT JOIN driver d ON r.driver_id = d.id LEFT JOIN passengers p ON r.passenger_id = p.id WHERE $userField = :uid $whereExtra ORDER BY r.id DESC LIMIT 20 "); $params = [':uid' => $userId]; if ($filterStatus !== 'all') $params[':filter_status'] = $filterStatus; $rideStmt->execute($params); $rides = $rideStmt->fetchAll(PDO::FETCH_ASSOC); ``` --- ### 🟡 الإصلاح 5: إضافة شاشة للرحلات المعلقة (Pending/Waiting) **المشكلة**: لا توجد واجهة لعرض الرحلات العالقة (`waiting`, `New`, `nothing`). **الحل المقترح**: - إضافة تبويب خامس باسم **"معلقة"** في RidesDashboardScreen - يعرض الرحلات ذات الحالة: `New`, `waiting`, `wait`, `nothing` - إضافة زر "إلغاء الرحلة" مع إدخال سبب - تحديث `admin_update_ride_status.php` ليدعم الإلغاء مع إشعار ```dart // إضافة في ride_lookup_page.dart (RidesListController) void changeTab(String status) { currentStatus = status; searchController.clear(); fetchRides(); } // إضافة في RidesDashboardScreen Tab(text: 'معلقة', icon: Icon(Icons.hourglass_empty_rounded)), // عند اختيار هذا التبويب، يرسل status='Pending' // الـ API يتعامل معها كـ: IN ('New','waiting','wait','nothing') ``` --- ### 🟢 الإصلاح 6: إضافة البحث برقم الرحلة (Ride ID) + Pagination **لماذا Ride ID هو الأفضل؟** - المعرف الوحيد الفريد (Primary Key) - لا توجد مشاكل تشفير (الأرقام المعرفية غير مشفرة) - لا توجد مشاكل تنسيق دولة - دقيق 100% **مثال الإند بوينت الجديد**: ```php // backend/Admin/rides/admin_get_ride_by_id.php $rideId = filterRequest('id'); $stmt = $con->prepare(" SELECT r.*, d.first_name as d_fname, d.last_name as d_lname, d.phone as d_phone, p.first_name as p_fname, p.last_name as p_lname, p.phone as p_phone FROM ride r LEFT JOIN driver d ON r.driver_id = d.id LEFT JOIN passengers p ON r.passenger_id = p.id WHERE r.id = :id LIMIT 1 "); $stmt->execute([':id' => $rideId]); jsonSuccess($stmt->fetch()); ``` **Pagination في `get_rides_by_status.php`**: ```php $page = (int)(filterRequest('page') ?? 1); $limit = 50; $offset = ($page - 1) * $limit; $sql .= " ORDER BY r.id DESC LIMIT $limit OFFSET $offset"; ``` --- ## الملخص النهائي ### جدول حالات الرحلة - ما هو صحيح وما هو خاطئ: | ما في النظام (DB) | ما في التطبيق (Admin) | التصحيح | |-------------------|----------------------|---------| | `nothing` / `New` | `New` ✅ | ✅ صحيح (جديد) | | `waiting` / `wait` | غير موجود ❌ | 🟡 يجب إضافته (معلق) | | `Apply` / `Applied` | `Begin` (مصنف مع الجارية) ✅ | ✅ صحيح (جاري التوصيل) | | `arrived` / `Arrived` | غير موجود ❌ | 🟡 يجب إضافته مع الجارية | | `Begin` | `Begin` ✅ | ✅ صحيح (قيد التشغيل) | | `Finished` | `Completed` ✅ | ✅ صحيح (مكتملة) | | `Cancel` / `CancelFrom*` / `TimeOut` | `Canceled` (ناقص) ❌ | 🔴 يجب إضافة `CancelFromDriver` و `CancelFromPassenger` | ### قائمة الإصلاحات: | # | الإصلاح | الملف | الأولوية | |---|---------|-------|----------| | 1 | تصحيح حالات الرحلة في `get_rides_by_status.php` | `backend/Admin/rides/get_rides_by_status.php` | 🔴 عاجل | | 2 | تصحيح `statusOptions` في `ride_lookup_controller.dart` | `siro_admin/lib/controller/rides/ride_lookup_controller.dart` | 🔴 عاجل | | 3 | تصحيح whitelist في `admin_update_ride_status.php` | `backend/Admin/rides/admin_update_ride_status.php` | 🔴 عاجل | | 4 | إضافة `normalizePhone()` في `monitor_ride.dart` | `siro_admin/lib/views/admin/drivers/monitor_ride.dart` | 🔴 عاجل | | 5 | إضافة `normalizePhone()` في `admin_get_rides_by_phone.php` | `backend/Admin/rides/admin_get_rides_by_phone.php` | 🔴 عاجل | | 6 | إضافة `normalizePhone()` في `monitorRide.php` | `backend/Admin/rides/monitorRide.php` | 🔴 عاجل | | 7 | إضافة البحث في driver + passenger في `admin_get_rides_by_phone.php` | `backend/Admin/rides/admin_get_rides_by_phone.php` | 🟡 ضروري | | 8 | إضافة شاشة للرحلات المعلقة (Pending/Waiting) | `siro_admin/lib/views/admin/rides/ride_lookup_page.dart` | 🟡 ضروري | | 9 | إضافة البحث برقم الرحلة (Ride ID) | Endpoint + Flutter جديد | 🟡 ضروري | | 10 | إضافة Pagination | `backend/Admin/rides/get_rides_by_status.php` | 🟢 مفيد | ### أفضل طرق البحث (مرتبة حسب الأولوية): 1. **رقم الرحلة (Ride ID)** — الأدق، بدون مشاكل تشفير أو دولة 2. **رقم هاتف الراكب/السائق** — مع معالجة الدولة (normalizePhone) 3. **بحث بنطاق تاريخي** — لاستعراض الرحلات حسب الفترة الزمنية