From 3a8caf7b276a70a0c9e12fae7e68e459c4023594 Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Wed, 5 Aug 2026 00:39:30 +0300 Subject: [PATCH] =?UTF-8?q?fix(rider):=20=D8=AA=D8=B9=D8=B7=D9=8A=D9=84=20?= =?UTF-8?q?=D8=A3=D9=86=D9=88=D8=A7=D8=B9=20=D8=A7=D9=84=D8=B1=D8=AD=D9=84?= =?UTF-8?q?=D8=A7=D8=AA=20=D8=A8=D9=84=D8=A7=20=D8=AA=D8=B9=D8=B1=D9=81?= =?UTF-8?q?=D8=A9=20=D9=86=D8=B4=D8=B7=D8=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit فحص حيّ كشف أن `GET /ride-types` يرجّع ستّة أنواع للمستأجر siro بينما ثلاثة فقط لها تعرفة نشطة: economy 1.55 · comfort 1.85 · electric 1.75 ✅ family_van · women · scooter ❌ 404 أي أن الراكب يرى ستّة خيارات، وثلاثة منها لو اختارها يفشل طلب الرحلة بـ «No pricing available» بعد أن يكون قد أكمل تحديد وجهته. المعالجة في التطبيق: النوع بلا تعرفة يظهر **معطّلاً مكتوباً عليه «غير متاح»** لا مخفياً — إخفاؤه يترك المستخدم يتساءل أين ذهب الخيار، وإظهاره قابلاً للاختيار يوصله إلى فشل بعد خطوتين. وإن كان المختار بلا تعرفة يُنتقل تلقائياً لأول نوع مسعَّر، فلا يبقى زرّ التأكيد معطّلاً بلا سبب ظاهر. هذا علاج للعَرَض؛ السبب بيانات على الخادم: كتالوج ride-types والتعرفات غير متزامنين. Co-Authored-By: Claude Opus 5 --- apps/rider_new/lib/core/l10n/app_ar.arb | 3 +- apps/rider_new/lib/core/l10n/app_en.arb | 3 +- .../lib/features/trip/cubit/ride_cubit.dart | 8 +++- .../trip/view/widgets/ride_type_picker.dart | 45 ++++++++++++++----- 4 files changed, 46 insertions(+), 13 deletions(-) diff --git a/apps/rider_new/lib/core/l10n/app_ar.arb b/apps/rider_new/lib/core/l10n/app_ar.arb index a7afd8b..83099d2 100644 --- a/apps/rider_new/lib/core/l10n/app_ar.arb +++ b/apps/rider_new/lib/core/l10n/app_ar.arb @@ -200,5 +200,6 @@ "rideFrom": "من", "rideTo": "إلى", "walletChannel": "طريقة الدفع", - "walletTopupNote": "الشحن التجريبي حالياً بلا تحصيل فعلي — ربط البوابة قادم" + "walletTopupNote": "الشحن التجريبي حالياً بلا تحصيل فعلي — ربط البوابة قادم", + "rideTypeUnavailable": "غير متاح" } \ No newline at end of file diff --git a/apps/rider_new/lib/core/l10n/app_en.arb b/apps/rider_new/lib/core/l10n/app_en.arb index 7678cd7..e64ad05 100644 --- a/apps/rider_new/lib/core/l10n/app_en.arb +++ b/apps/rider_new/lib/core/l10n/app_en.arb @@ -200,5 +200,6 @@ "rideFrom": "From", "rideTo": "To", "walletChannel": "Payment method", - "walletTopupNote": "Top-up is currently a test cash-in with no real charge — gateway wiring is next" + "walletTopupNote": "Top-up is currently a test cash-in with no real charge — gateway wiring is next", + "rideTypeUnavailable": "Unavailable" } \ No newline at end of file diff --git a/apps/rider_new/lib/features/trip/cubit/ride_cubit.dart b/apps/rider_new/lib/features/trip/cubit/ride_cubit.dart index bb79014..0de84f8 100644 --- a/apps/rider_new/lib/features/trip/cubit/ride_cubit.dart +++ b/apps/rider_new/lib/features/trip/cubit/ride_cubit.dart @@ -283,10 +283,16 @@ class RideCubit extends Cubit { final quotes = Map.fromEntries( results.whereType>(), ); - final selected = state.selectedRideType; + // إن كان المختار بلا تعرفة، ننتقل لأول نوع مسعَّر: تركُه مختاراً يعني + // زرَّ تأكيد معطّلاً بلا سبب ظاهر. + var selected = state.selectedRideType; + if (selected == null || !quotes.containsKey(selected.code)) { + selected = types.where((t) => quotes.containsKey(t.code)).firstOrNull; + } emit(state.copyWith( quotes: quotes, + selectedRideType: selected, quote: selected == null ? null : quotes[selected.code], busy: false, error: quotes.isEmpty ? RideError.quoteFailed : RideError.none, diff --git a/apps/rider_new/lib/features/trip/view/widgets/ride_type_picker.dart b/apps/rider_new/lib/features/trip/view/widgets/ride_type_picker.dart index ae12f20..accc94e 100644 --- a/apps/rider_new/lib/features/trip/view/widgets/ride_type_picker.dart +++ b/apps/rider_new/lib/features/trip/view/widgets/ride_type_picker.dart @@ -4,6 +4,7 @@ import '../../../../core/design/tokens.dart'; import '../../../../core/design/tripz_colors.dart'; import '../../../../core/design/typography.dart'; import '../../../../core/format/money.dart'; +import '../../../../core/l10n/l10n.dart'; import '../../data/models/fare_quote.dart'; import '../../data/models/ride_type.dart'; @@ -41,11 +42,16 @@ class RideTypePicker extends StatelessWidget { separatorBuilder: (context, i) => const SizedBox(width: Space.xs), itemBuilder: (context, i) { final type = types[i]; + final quote = quotes[type.code]; return _Tile( type: type, - quote: quotes[type.code], + quote: quote, selected: type.id == selected?.id, label: type.nameFor(lang), + // نوعٌ بلا تعرفة نشطة **لا يُختار**: اختياره يُفشل طلب الرحلة + // بـ«No pricing available». يبقى ظاهراً معطّلاً كي يعرف المستخدم + // أن الخيار موجود لكنه غير متاح الآن — لا أن يختفي بلا تفسير. + enabled: quote != null, onTap: () => onSelect(type), ); }, @@ -60,6 +66,7 @@ class _Tile extends StatelessWidget { required this.quote, required this.selected, required this.label, + required this.enabled, required this.onTap, }); @@ -67,12 +74,14 @@ class _Tile extends StatelessWidget { final FareQuote? quote; final bool selected; final String label; + final bool enabled; final VoidCallback onTap; @override Widget build(BuildContext context) { final scheme = context.colors; final fare = quote; + final dim = scheme.onSurfaceVariant; return AnimatedContainer( duration: Motion.tap, @@ -88,7 +97,7 @@ class _Tile extends StatelessWidget { ), ), child: InkWell( - onTap: onTap, + onTap: enabled ? onTap : null, borderRadius: Radii.card_, child: Padding( padding: const EdgeInsets.all(Space.xs), @@ -102,7 +111,11 @@ class _Tile extends StatelessWidget { ? Icons.two_wheeler_rounded : Icons.directions_car_rounded, size: Sizes.icon, - color: selected ? scheme.onPrimaryContainer : scheme.onSurface, + color: !enabled + ? dim + : selected + ? scheme.onPrimaryContainer + : scheme.onSurface, ), const SizedBox(height: Space.xxs), Text( @@ -110,19 +123,31 @@ class _Tile extends StatelessWidget { maxLines: 1, overflow: TextOverflow.ellipsis, style: context.texts.labelMedium?.copyWith( - color: selected ? scheme.onPrimaryContainer : null, + color: !enabled + ? dim + : selected + ? scheme.onPrimaryContainer + : null, ), ), const SizedBox(height: Space.xxs), // نوعٌ بلا تعرفة نشطة يظهر بشرطة بدل أن يُخفى: إخفاؤه يترك // المستخدم يتساءل أين ذهب الخيار. - Text( - fare == null ? '—' : Money.formatNum(fare.total!, fare.currency), - maxLines: 1, - style: numericStyle(context.texts.titleSmall).copyWith( - color: selected ? scheme.onPrimaryContainer : scheme.primary, + if (fare == null) + Text( + context.l10n.rideTypeUnavailable, + maxLines: 1, + style: context.texts.labelSmall?.copyWith(color: dim), + ) + else + Text( + Money.formatNum(fare.total!, fare.currency), + maxLines: 1, + style: numericStyle(context.texts.titleSmall).copyWith( + color: + selected ? scheme.onPrimaryContainer : scheme.primary, + ), ), - ), ], ), ),