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:
co-authored by
Claude Opus 5
parent
29f375b122
commit
8976821a93
@@ -0,0 +1,48 @@
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
import '../design/tokens.dart';
|
||||
import '../design/tripz_colors.dart';
|
||||
import '../payments/payment_channels.dart';
|
||||
|
||||
/// اختيار قناة الدفع/السحب. القائمة تأتي من حزمة دولة المستأجر، فلا يرى
|
||||
/// الأردنيُّ «شام كاش» ولا السوريُّ «كليك».
|
||||
class ChannelPicker extends StatelessWidget {
|
||||
const ChannelPicker({
|
||||
super.key,
|
||||
required this.channels,
|
||||
required this.selected,
|
||||
required this.onSelect,
|
||||
});
|
||||
|
||||
final List<PaymentChannel> channels;
|
||||
final PaymentChannel? selected;
|
||||
final ValueChanged<PaymentChannel> onSelect;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (channels.isEmpty) return const SizedBox.shrink();
|
||||
final lang = Localizations.localeOf(context).languageCode;
|
||||
|
||||
return Wrap(
|
||||
spacing: Space.xs,
|
||||
runSpacing: Space.xs,
|
||||
children: [
|
||||
for (final channel in channels)
|
||||
ChoiceChip(
|
||||
selected: channel == selected,
|
||||
onSelected: (_) => onSelect(channel),
|
||||
avatar: Icon(_iconOf(channel), size: Sizes.iconSm),
|
||||
label: Text(channel.nameFor(lang)),
|
||||
labelStyle: context.texts.labelLarge,
|
||||
),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
static IconData _iconOf(PaymentChannel channel) => switch (channel) {
|
||||
PaymentChannel.cash => Icons.payments_outlined,
|
||||
PaymentChannel.cliq => Icons.account_balance_outlined,
|
||||
PaymentChannel.paymob => Icons.credit_card_outlined,
|
||||
_ => Icons.smartphone_outlined,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user