Update codebase

This commit is contained in:
Hamza-Ayed
2026-08-05 14:12:41 +03:00
parent 58928cd258
commit 95e2e4f35d
51 changed files with 3141 additions and 390 deletions
+210
View File
@@ -0,0 +1,210 @@
# 40 — فجوات واجهة `rider_new` مقابل سيرو، وخطة الإغلاق
تاريخ: 2026-08-05 · الحالة: خطة معتمدة للتنفيذ · يمكن تسليمها لنموذج آخر بلا سياق إضافي
## 0. لماذا هذه الوثيقة
المالك راجع `rider_new` مقابل تطبيقَي سيرو المنشورين (App Store:
`id6458734951` راكب، `id6502189302` سائق؛ Play: `com.mobileapp.store.ride`)
وطلب تشخيصاً دقيقاً — لا وعوداً — لثلاث فجوات محدّدة، مع خطة قابلة للتنفيذ
من أي طرف. هذا فحص كودٍ فعلي على الشجرتين، لا افتراض.
## 1. النتيجة أوّلاً: الفجوة الحقيقية أضيق ممّا بدت
فحصت `apps/rider_new` مقابل `apps/rider` (سيرو) سطراً بسطر على النقاط
الثلاث التي ذكرها المالك:
| البند | الحالة في `rider_new` | الحكم |
|---|---|---|
| ارتفاع الخريطة (70-80%) | [ride_page.dart:87](../apps/rider_new/lib/features/trip/view/ride_page.dart#L87): `bottomInset = height * 0.34` → الخريطة تأخذ فعلياً **~66%** | **قريب من المطلوب أصلاً**، ليس فجوة صفرية |
| بحث الأماكن كديالوج كامل الشاشة | [planner_sheet.dart](../apps/rider_new/lib/features/trip/view/widgets/planner_sheet.dart) — حقل بحث **داخل** الورقة السفلية المطويّة (`AnimatedSize`) | **فجوة حقيقية** — راجع §2 |
| الدعوات/الكوبونات/المكافآت | `features/rewards/` كامل: `RewardsCubit`، `ReferralSummary`، `Coupon`، `share_plus` مُستخدَم في [rewards_page.dart](../apps/rider_new/lib/features/rewards/view/rewards_page.dart) و[app_drawer.dart](../apps/rider_new/lib/core/ui/app_drawer.dart) | **موجود ومبني**، ليس فراغاً — قد يحتاج ضبط تفاصيل بصرية فقط |
| المحفظة | `features/wallet/` كامل: `WalletCubit`، `WalletRepository`، `wallet_page.dart` | **موجود ومبني** |
**الخلاصة:** الادّعاء بأن «لا شيء جديد يعمل» غير دقيق مقارنةً بحالة الكود
الفعلية — رحلاتي/المحفظة/المكافآت مبنية وتستدعي مستودعات حقيقية لا بيانات
عشوائية. الفجوة القابلة للإشارة إليها بدقة هي بند واحد: **شكل بحث الأماكن**.
## 2. الفجوة الوحيدة المؤكَّدة: بحث الأماكن
### سيرو (المرجع، `apps/rider/lib/views/home/route_planner/rp_search_dialog.dart`)
ديالوج **كامل الشاشة** منفصل تماماً عن الورقة السفلية:
```dart
showGeneralDialog(
context: context,
barrierDismissible: true,
barrierColor: RP.sheet,
transitionDuration: const Duration(milliseconds: 300),
pageBuilder: (context, animation, secondaryAnimation) =>
RpSearchDialog(accent: accent, hintText: hintText, isOrigin: isOrigin),
transitionBuilder: (context, animation, secondaryAnimation, child) =>
SlideTransition(
position: Tween<Offset>(begin: const Offset(0, 1), end: Offset.zero)
.animate(CurvedAnimation(parent: animation, curve: Curves.easeOutCubic)),
child: child,
),
);
```
يُستدعى بنسختين — `isOrigin: true/false` — بدل حقل واحد يبحث عن الوجهة فقط.
### `rider_new` الحالي
`planner_sheet.dart` يعرض حقل البحث والنتائج **داخل** `_Sheet` المطوية على
الخريطة (`AnimatedSize` في [ride_page.dart:206](../apps/rider_new/lib/features/trip/view/ride_page.dart#L206)):
مساحة أضيق، النتائج تُقصّ عند حدّ الورقة، ولا يوجد تمييز بين البحث عن نقطة
البداية والوجهة (البحث الحالي يذهب دائماً لـ`PickTarget.destination` —
انظر `planner_sheet.dart:128`).
### خطة الإغلاق (قابلة للتنفيذ مباشرة)
1. **`PlaceSearchDialog` جديد** في `features/trip/view/widgets/` — `showGeneralDialog`
بنفس نمط السحب من الأسفل (300ms، `Curves.easeOutCubic`)، بارامتر
`PickTarget target` بدل `isOrigin` (النوع الموجود أصلاً في `RideCubit`).
2. يحوي: `RouteTimeline` (موجود) في الأعلى ثابتاً، ثم `TripzTextField`
بالبحث المباشر (`cubit.searchPlaces` موجود)، ثم قائمة نتائج بملء الشاشة
المتبقية — لا `Flexible` داخل `AnimatedSize`.
3. `_Collapsed._MapFab`/حقل «إلى أين؟» في `ride_page.dart:307` يستدعي
`PlaceSearchDialog.show(context, target: PickTarget.destination)` بدل
`cubit.openPlanner` (الذي يبقى للحالات الأخرى — تحديد من الخريطة، إلخ —
إن وُجدت استدعاءات أخرى له، تُفحص قبل الحذف).
4. `RouteTimeline.onTapOrigin` (الموجود في `planner_sheet.dart:60`) يفتح نفس
الديالوج بـ`PickTarget.origin`.
5. **لا تُنسخ** ملفات `rp_search_dialog.dart` وأخواتها حرفياً (docs/37 §2:
سيرو مرجع لا مصدر نسخ) — يُعاد بناؤها بـCubit/`TripzTextField`/نظام
التصميم الحالي، بنفس السلوك المرصود فقط.
## 3. ما لم يُثبَت بعد — يحتاج فحصاً بصرياً لا كودياً
الفحص أعلاه كودي (قراءة ملفات). البنود التالية **مبنية في الكود** لكن
مطابقتها البصرية لسيرو لم تُتحقّق على جهاز حقيقي:
- شكل بطاقة المكافآت/الكوبون في `rewards_page.dart` — الحقول موجودة
(`referrals`, `coupons`)، لكن التخطيط والألوان لم تُقارَن سطراً بسطر مع
سيرو.
- نص/زر مشاركة الدعوة تحديداً (رابط + كود) — `share_plus` مستورد، لكن نص
الرسالة المُشارَكة نفسه يحتاج تحقّقاً من محتواه مقابل ما يشاركه سيرو.
**لتنفيذ هذين البندين بدقة:** التقط لقطات شاشة من تطبيقَي سيرو المنشورين
(الروابط أعلاه) وضعها بجانب `rewards_page.dart`/`app_drawer.dart` الحاليين
— هذا الجزء يحتاج عيناً بشرية أو أداة مقارنة بصرية، لا قراءة كود وحدها.
## 3.ب — جرد الدراور مقابل سيرو (فحص كودي، 2026-08-05)
مرجع سيرو الحقيقي **ليس** `drawer_menu_page.dart` (ذاك ملف ميّت 25 سطراً
بلا مدخل واحد)، بل
`apps/rider/lib/views/home/map_widget.dart/map_menu_widget.dart`.
| مدخل سيرو | صفحته في سيرو | الحالة في `rider_new` |
|---|---|---|
| Mawasalati | `TransitHomePage` | ❌ منتج منفصل (`backend-transit/`) — خارج نطاق تطبيق الراكب |
| My Balance | `PassengerWallet` | ✅ `Routes.wallet` → `/wallet`, `/wallet/transactions` موجودتان |
| Order History | `OrderHistory` | ✅ `Routes.history` → `GET /trips/mine` |
| Promos | `PromosPassengerPage` | ✅ `Routes.rewards` → `/rewards/referrals`, `/rewards/coupons` |
| Contact Us | `ContactUsPage` | ✅ `Routes.support` |
| Complaint | `ComplaintPage` | ❌ **محجوب على الخادم** — لا وحدة `complaints` في `backend-archive/src/modules` |
| Driver | رابط خارجي | ✅ `BuildConfig.driverAppUrl` |
| Share App | `ShareAppPage` | ⚠️ عندنا مشاركة مباشرة لا صفحة — فرق شكل لا وظيفة |
| Privacy Policy | رابط خارجي | ✅ |
| (صفّ الأيقونات) Profile | `PassengerProfilePage` | ✅ `Routes.profileEdit` |
| (صفّ الأيقونات) Notification | `NotificationPage` | ✅ **أُنجز 2026-08-05** — انظر أدناه |
| (صفّ الأيقونات) Setting | `SettingPage` | ✅ `Routes.settings` |
**تصحيح لتشخيص أسبق:** لا شيء في الدراور «بيانات ديمو». كل صفحة تستدعي
نقطة حقيقية موجودة في `backend-archive` (تحقّقت من `wallet.controller.ts`,
`rewards.controller.ts`, `trips.controller.ts`, `payments/*`). ما يبدو
فارغاً هو خادمٌ لم تُبذر بياناته، لا واجهةٌ وهمية.
### صندوق الإشعارات — أُنجز، بحدٍّ معروف
`features/notifications/` + `core/notifications/notification_store.dart`.
الخادم يوفّر `POST /notifications/token` فقط — **لا نقطة سرد**
(`backend-archive/src/modules/notifications/notifications.controller.ts`
فيه `@Post('token')` وحده). فالصندوق **محلّي**: `PushService` يلتقط
`onMessage` و`onMessageOpenedApp` و`getInitialMessage` ويحفظها في
`SharedPreferences` (سقف 50)، والدراور يعرض عدّاد غير المقروء.
**الحدّ:** ما يصل والتطبيق مغلق تماماً لا يُلتقط (لا `background handler`
مسجَّل). حين تُضاف نقطة سرد على الخادم يُستبدل المصدر وحده ويبقى شكل
الشاشة.
### شاشات الدراور الثلاث — أُنجزت 2026-08-05
**رحلاتي.** البطاقة كانت ثلاثة عناصر (نقطة · حالة · سعر). والسبب لم يكن
نقصاً في الخادم بل **قراءةً ناقصة**: `trip.entity.ts` فيه `requested_at`
(`CreateDateColumn`) و`distance_km` و`duration_min` و`payment_method`
و`service_class`، وكلها تصل مع `GET /trips/mine` وكان `Trip.fromJson`
يتجاهلها. صارت تُقرأ وتُعرض: التاريخ والوقت، المسافة والمدة، وشارة طريقة
الدفع.
- **العناوين النصّية ما زالت مستحيلة**: الكيان يخزّن `origin_lat/lng` بلا
`origin_address`. سيرو يعرض عناوين لأن PHP يخزّنها. عرضُ إحداثيات خام
أسوأ من لا شيء — يحتاج حقلاً على الخادم.
**الإعدادات.** كانت قائمة مسطّحة (مظهر + لغة). صارت بأقسام كسيرو
(`setting_page.dart`: General · Support & Info)، وأُضيف: مدخل المساعدة،
مشاركة التطبيق، سياسة الخصوصية، **ورقم الإصدار** (مفتاح `aboutVersion` كان
موجوداً بلا شاشة تستعمله).
- **باگ مُصلَح:** تبديل اللغة كان **محلياً فقط**. الخادم يصوغ الإشعارات
بلغة المستخدم المخزّنة عنده و`PATCH /users/me` يقبل `language`
(docs/38 §3) — فكانت الواجهة عربية والإشعارات إنجليزية. صار التبديل
يُزامَن، وفشلُ الشبكة لا يمنع التبديل المحلي.
**حسابي.** أُضيفت ترويسة (حرف الاسم · الاسم · الرقم · **التقييم**).
`AppUser.rating` كان يصل من `GET /users/me` ولا تعرضه أي شاشة، بينما الراكب
يُقيَّم من السائق ويحقّ له رؤية نتيجته.
- **لا صورة شخصية**: الخادم لا يوفّر رفع صور (سيرو يفعل عبر PHP). وحقول
سيرو الأخرى (الجنس، التعليم، الحالة الاجتماعية، هاتف الطوارئ) غير ممكنة:
`PATCH /users/me` يقبل `{name, language}` حصراً.
الثلاث مُطبَّقة على `driver_new` أيضاً (كانت متطابقة بايتاً ببايت مع
`rider_new` قبل التعديل، فالنسخ آمن).
## 3.ج — تطبيق السائق: ما أُصلح قبل التجربة الحقيقية (2026-08-05)
**السائق كان أعمى أثناء الرحلة.** `duty_page.dart` كان يمرّر
`RideMap(origin: me, cameraTarget: me)` فقط — بلا نقطة الراكب، بلا الوجهة،
بلا خطّ يقوده. تجربةٌ حقيقية بهذه الشاشة مستحيلة.
- `DutyState` أخذ `navRoute` و`navTarget`؛ والهدف تشتقّه **آلة الحالات**:
نقطة الراكب في `assigned/driver_arriving/driver_arrived`، ووجهته في
`in_progress`.
- `DutyCubit` صار يحسب المسار من موقع السائق إلى الهدف، بنفس حدّ الـ50 متراً
المستعمل في تطبيق الراكب (يلتقط الانعطاف، يتجاهل رجفة GPS)، ويعيد الحساب
فوراً عند كل انتقال حالة بدل انتظار نبضة الموقع.
- الخريطة صارت تعرض النقطتين والمسار، ومرفوعة بالثلث كخريطة الراكب.
- شريط الوقت والمسافة (`NavEtaStrip`) في ورقة الرحلة.
**باگان مُصلَحان:**
1. لافتة نفاد الرصيد كانت تعرض `l10n.errorGeneric` («حدث خطأ غير متوقّع»)
بدل `l10n.creditBlocked` («رصيدك التشغيلي نفد — اشحنه لتستقبل الطلبات»).
السائق المحجوب كان يرى خطأً عاماً لا سبباً.
2. زرّ المحادثة كان `'\${Routes.chat}?trip=\\\${trip.id}'` — الهروب الزائد
يجعل الناتج نصّاً حرفياً `?trip=\${trip.id}`، فالمحادثة تُفتح بمعرّف رحلة
غير موجود ولا تحمّل رسالة واحدة أبداً. (نفس الخطأ كان في تطبيق الراكب
وزال حين استُبدل الزرّ بـ`TripContactRow`.)
**محجوب على الخادم:** لا اسم الراكب ولا رقمه يصلان السائق — لا
`rider_name`/`rider_phone` في أي وحدة من `backend-archive/src/modules`.
فزرّ «اتصل بالراكب» غير ممكن؛ المحادثة داخل التطبيق هي القناة الوحيدة.
### Complaint — محجوب، ويحتاج قراراً
بناؤه يتطلّب وحدة `complaints` في NestJS (كيان + `POST /complaints` +
`GET /complaints/mine`). هذا شغل باك إند يُبنى ويُختبر على الخادم
(`docs/15`), لا على الماك. لم أبنِ واجهةً لنقطة غير موجودة.
## 4. الأولوية والترتيب
1. ~~`PlaceSearchDialog` (§2)~~ — **أُنجز 2026-08-05**.
2. ~~رفع الخريطة عن القاع بالثلث~~ — **أُنجز**: `_sheetFraction = 0.33` في
`ride_page.dart`. الخريطة والدبّوس في `Positioned` واحدة (وإلا أشار
الدبّوس إلى غير النقطة المؤكَّدة)، والورقة لها `minHeight` بنفس النسبة.
3. ~~صندوق الإشعارات~~ — **أُنجز** (§3.ب).
4. `ComplaintPage` — **محجوب**: يحتاج وحدة `complaints` في NestJS أولاً.
5. مطابقة بصرية للمكافآت/المشاركة (§3) — تحتاج لقطات سيرو أولاً.
6. تكرار ما ينطبق على `driver_new` — فُحص: لا `PlannerSheet` ولا بحث أماكن
فيه، فلا شيء يُكرَّر من §2.
Related: [[docs/37]] (قاعدة سيرو مرجع لا مصدر نسخ) · [[docs/39]] (شاشات سيرو المرصودة)