<div dir="rtl" lang="ar">

# تحليل نظام متابعة الرحلات في لوحة تحكم المشرف (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<String> 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. **بحث بنطاق تاريخي** — لاستعراض الرحلات حسب الفترة الزمنية

</div>