fix(apps): إصلاحات من أول تجربة حقيقية على الجهاز

## تعذّر حساب الأجرة — الخطأ في التطبيق لا في جدول الكازان
الخادم يبذر التعرفة بـ city: 'default' (tenants.service.ts:156) ويستعمل
dto.city ?? 'default' عند إنشاء الرحلة، وكنت أرسل city: '' فيرجّع 404
«No active tariff for /economy». تحقّق حيّ:
  city='default' → total 1.55 JOD ✅   city='' → 404 ❌   city='amman' → 404 ❌
لا تغيير مطلوب على السيرفر. أُضيف AppConfig.defaultCity موثّقاً بالسبب.

## التسعير مرة واحدة لكل الأنواع
كان نداء شبكة عند كل ضغطة نوع. صار _priceAll يسعّر كل الأنواع بالتوازي بعد
اكتمال المسار — المسافة والمدة ثابتتان والفرق في التعرفة وحدها. الراكب يرى
سعر الاقتصادي والمريح معاً تحت كل خيار ويقارن بنظرة، وتبديل النوع بلا نداء.

## الكاميرا لا تتبع الموقع
سببان: الموقع يصل قبل إنشاء الخريطة فيضيع التحريك، ولا فحص للإذن/الخدمة قبل
getCurrentPosition فيُبتلع الخطأ صامتاً.
- _onCreated يلحق بما فات عند جهوز الخريطة
- LocationService.ensureReady يفحص الخدمة والإذن أولاً
- didChangeAppLifecycleState يعيد المحاولة عند العودة من إعدادات النظام
- حالة noLocation تُعرض للمستخدم مع زر إعادة بدل خريطة صامتة

## الخريطة والتنقّل
- إطار المسار من حدوده الفعلية مع حشوة سفلية بمقدار ما تغطّيه الورقة
- علامتا البداية والنهاية دائرتان ملوّنتان تطابقان ألوان الخط الزمني
- زر رجوع في ورقة التأكيد + زر إعادة حساب عند فشل التسعيرة: كان المستخدم
  يعلق في الشاشة بلا مخرج

## المحفظة — على أي أساس يُشحن
POST /wallet/topup شحنٌ تجريبي بلا مزوّد دفع خلفه (الخادم نفسه يصفه كذلك).
أُضيف PaymentChannel مشتقّاً من countryPack المستأجر لا من لغة الجهاز:
jo → CliQ · sy → سيرياتيل/MTN/شام كاش · eg → PayMob. ويُعرض تنبيه صريح بأن
الشحن بلا تحصيل فعلي — إخفاؤه يجعل المستخدم يظنّ أنه دفع. وسحب السائق صار
يستعمل القناة المختارة بدل 'cliq' المكتوبة في الكود.

flutter analyze نظيف في التطبيقين.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Hamza-Ayed
2026-08-05 00:36:02 +03:00
co-authored by Claude Opus 5
parent 29f375b122
commit 8976821a93
18 changed files with 731 additions and 87 deletions
@@ -37,7 +37,16 @@ enum RidePhase {
/// أيّ حقلٍ يلتقط اختيار الخريطة.
enum PickTarget { origin, destination }
enum RideError { none, noDrivers, quoteFailed, routeFailed, requestFailed }
enum RideError {
none,
noDrivers,
quoteFailed,
routeFailed,
requestFailed,
/// الموقع مرفوض أو الخدمة مطفأة — بلا موقع لا خريطة مفيدة.
noLocation,
}
class RideState extends Equatable {
const RideState({
@@ -51,6 +60,7 @@ class RideState extends Equatable {
this.selectedRideType,
this.route,
this.quote,
this.quotes = const {},
this.trip,
this.driverLocation,
this.searchResults = const [],
@@ -74,8 +84,17 @@ class RideState extends Equatable {
final RideType? selectedRideType;
final RouteInfo? route;
/// تسعيرة النوع المختار — مشتقّة من [quotes] للراحة.
final FareQuote? quote;
/// **كل الأنواع مسعَّرة دفعةً واحدة** بمفتاح `RideType.code`.
///
/// الراكب يرى أسعار الاقتصادي والمريح والعائلي معاً فيقارن ويقرّر — بدل
/// نداء شبكة عند كل ضغطة نوع. المسافة والمدة ثابتتان للمسار، فالنداءات
/// متوازية ومرة واحدة.
final Map<String, FareQuote> quotes;
final Trip? trip;
/// موقع السائق الحيّ من `driver:location`.
@@ -96,6 +115,9 @@ class RideState extends Equatable {
/// كود الخصم — يُقيَّم على الخادم عند الطلب لا في التطبيق.
final String couponCode;
/// تسعيرة نوعٍ بعينه — لعرض السعر تحت كل خيار.
FareQuote? quoteFor(String code) => quotes[code];
bool get canConfirm =>
origin != null &&
destination != null &&
@@ -113,6 +135,7 @@ class RideState extends Equatable {
RideType? selectedRideType,
RouteInfo? route,
FareQuote? quote,
Map<String, FareQuote>? quotes,
Trip? trip,
GeoPoint? driverLocation,
List<Place>? searchResults,
@@ -140,6 +163,7 @@ class RideState extends Equatable {
selectedRideType: selectedRideType ?? this.selectedRideType,
route: clearRoute ? null : (route ?? this.route),
quote: clearRoute ? null : (quote ?? this.quote),
quotes: clearRoute ? const {} : (quotes ?? this.quotes),
trip: clearTrip ? null : (trip ?? this.trip),
driverLocation: driverLocation ?? this.driverLocation,
searchResults: searchResults ?? this.searchResults,
@@ -164,6 +188,7 @@ class RideState extends Equatable {
selectedRideType,
route,
quote,
quotes,
trip,
driverLocation,
searchResults,