From e22cc5c7e4211e6db39112ef38bf9c45f022864b Mon Sep 17 00:00:00 2001 From: Hamza-Ayed Date: Wed, 5 Aug 2026 01:08:18 +0300 Subject: [PATCH] =?UTF-8?q?feat(apps):=20=D8=AB=D9=8A=D9=85=20=D8=B3=D9=8A?= =?UTF-8?q?=D8=B1=D9=88=20=D9=88=D9=85=D9=81=D8=AA=D8=A7=D8=AD=20=D8=A7?= =?UTF-8?q?=D9=84=D8=AF=D9=88=D9=84=D8=A9=20=D9=88=D8=A7=D9=84=D8=B9=D8=B1?= =?UTF-8?q?=D8=A7=D9=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## الثيم مقرَّب من سيرو (قرار المالك 2026-08-05) - البذرة صارت كحليّ سيرو #1A2340 بدل الأزرق العام، والثانوي #8C9CF8 - fromSeed وحدها تنتج من بذرة داكنة أسطحاً باهتة، فثُبّت الأساسي والثانوي والأسطح صراحةً وتُرك الباقي للمشتقّ — شكل سيرو مع تباين M3 - الأدوار الدلالية بأرقام سيرو الميدانية: نجاح #22C55E · تحذير #F59E0B · خطأ #EF4444، وبطاقة الليلي #2C2C2E ## مفتاح الدولة عاد core/ui/phone_field.dart — النمط المجرَّب في سيرو، لكن بلا intl_phone_field: تلك الحزمة تحمل 240 دولة ونحتاج أربعاً. CountryCode صريح ومقصور على أسواقنا. - الاختيار من ورقة سفلية لا قائمة منسدلة: أهداف لمس أكبر وأربع دول لا مئتان - الترتيب يبدأ بحزمة دولة المستأجر — الأردنيّ يرى الأردن أولاً بلا بحث - الرقم يُرسل كاملاً بالمفتاح بلا + (962790000001) كما يقبله الخادم - التحقّق صار بطول الدولة لا برقم ثابت: jo/sy تسع خانات، eg/iq عشر ## العراق - CountryCode.iraq (+964) في قائمة الهاتف - قنوات دفعه: زين كاش · كي كارد · نقد ⚠️ الباك إند لا يعرف حزمة `iq`: default-tariffs.ts فيه jo/sy/eg فقط، فمستأجر عراقي يُزوَّد بلا أي تعرفة. موثَّق في الكود — وهو شغل خادم يسبق أي إطلاق عراقي. flutter analyze نظيف في التطبيقين. Co-Authored-By: Claude Opus 5 --- apps/driver_new/lib/app.dart | 9 -- apps/driver_new/lib/core/api/antlaq_api.dart | 1 - apps/driver_new/lib/core/api/api_client.dart | 3 - .../lib/core/api/auth_interceptor.dart | 2 - apps/driver_new/lib/core/build_config.dart | 8 +- apps/driver_new/lib/core/design/theme.dart | 26 +++- .../lib/core/design/tripz_colors.dart | 40 ++--- .../lib/core/notifications/push_service.dart | 3 - .../lib/core/payments/country_codes.dart | 48 ++++++ .../lib/core/payments/payment_channels.dart | 10 ++ .../lib/core/session/session_cubit.dart | 3 - .../lib/core/tenant/feature_gate.dart | 1 - .../lib/core/tenant/tenant_cubit.dart | 4 - apps/driver_new/lib/core/ui/app_drawer.dart | 10 -- apps/driver_new/lib/core/ui/empty_view.dart | 3 - apps/driver_new/lib/core/ui/error_view.dart | 3 - apps/driver_new/lib/core/ui/failure_text.dart | 4 - apps/driver_new/lib/core/ui/form_header.dart | 1 - apps/driver_new/lib/core/ui/phone_field.dart | 141 ++++++++++++++++++ apps/driver_new/lib/core/ui/skeleton.dart | 1 - .../driver_new/lib/core/ui/status_banner.dart | 1 - .../lib/core/ui/tripz_otp_field.dart | 2 - .../lib/core/ui/tripz_scaffold.dart | 4 - .../lib/core/ui/tripz_text_field.dart | 2 - .../lib/features/auth/cubit/login_cubit.dart | 25 ++-- .../lib/features/auth/cubit/login_state.dart | 14 +- .../lib/features/auth/cubit/phone_error.dart | 8 +- .../features/auth/data/auth_repository.dart | 3 - .../features/auth/view/agreement_view.dart | 16 +- .../lib/features/auth/view/login_page.dart | 8 - .../lib/features/auth/view/otp_view.dart | 20 +-- .../features/auth/view/permission_view.dart | 18 +-- .../lib/features/auth/view/phone_view.dart | 51 +++++-- .../features/profile/cubit/profile_cubit.dart | 4 - .../profile/view/profile_edit_page.dart | 9 -- .../profile/view/profile_setup_page.dart | 16 +- .../features/rating/cubit/rating_cubit.dart | 2 - .../lib/features/rating/view/rating_page.dart | 11 -- .../lib/features/rating/view/star_row.dart | 1 - .../settings/cubit/settings_cubit.dart | 2 - .../settings/cubit/settings_state.dart | 1 - .../features/settings/view/settings_page.dart | 6 - .../lib/features/splash/view/splash_page.dart | 7 - .../features/support/view/support_page.dart | 5 - .../features/trip/cubit/history_cubit.dart | 3 - .../trip/data/history_repository.dart | 1 - .../features/trip/data/maps_repository.dart | 3 - .../features/trip/data/trip_repository.dart | 4 - .../lib/features/trip/view/history_page.dart | 11 -- .../trip/view/widgets/driver_badge.dart | 3 - .../trip/view/widgets/fare_summary.dart | 5 - .../features/wallet/cubit/wallet_cubit.dart | 3 - .../features/wallet/cubit/wallet_state.dart | 2 - .../wallet/data/wallet_repository.dart | 2 - .../wallet/view/widgets/balance_card.dart | 3 - .../wallet/view/widgets/txn_tile.dart | 4 - apps/driver_new/lib/main.dart | 3 - apps/driver_new/tool/sync_from_rider.sh | 2 +- apps/rider_new/lib/app.dart | 9 -- apps/rider_new/lib/core/api/antlaq_api.dart | 1 - apps/rider_new/lib/core/api/api_client.dart | 3 - .../lib/core/api/auth_interceptor.dart | 2 - apps/rider_new/lib/core/build_config.dart | 8 +- apps/rider_new/lib/core/design/theme.dart | 26 +++- .../lib/core/design/tripz_colors.dart | 40 ++--- apps/rider_new/lib/core/di.dart | 24 --- .../lib/core/notifications/push_service.dart | 3 - .../lib/core/payments/country_codes.dart | 48 ++++++ .../lib/core/payments/payment_channels.dart | 10 ++ apps/rider_new/lib/core/router.dart | 14 -- .../lib/core/session/session_cubit.dart | 3 - .../lib/core/tenant/feature_gate.dart | 1 - .../lib/core/tenant/tenant_cubit.dart | 4 - apps/rider_new/lib/core/ui/app_drawer.dart | 10 -- apps/rider_new/lib/core/ui/empty_view.dart | 3 - apps/rider_new/lib/core/ui/error_view.dart | 3 - apps/rider_new/lib/core/ui/failure_text.dart | 4 - apps/rider_new/lib/core/ui/form_header.dart | 1 - apps/rider_new/lib/core/ui/phone_field.dart | 141 ++++++++++++++++++ apps/rider_new/lib/core/ui/skeleton.dart | 1 - apps/rider_new/lib/core/ui/status_banner.dart | 1 - .../lib/core/ui/tripz_otp_field.dart | 2 - .../rider_new/lib/core/ui/tripz_scaffold.dart | 4 - .../lib/core/ui/tripz_text_field.dart | 2 - .../lib/features/auth/cubit/login_cubit.dart | 25 ++-- .../lib/features/auth/cubit/login_state.dart | 14 +- .../lib/features/auth/cubit/phone_error.dart | 8 +- .../features/auth/data/auth_repository.dart | 3 - .../features/auth/view/agreement_view.dart | 16 +- .../lib/features/auth/view/login_page.dart | 8 - .../lib/features/auth/view/otp_view.dart | 20 +-- .../features/auth/view/permission_view.dart | 18 +-- .../lib/features/auth/view/phone_view.dart | 51 +++++-- .../features/profile/cubit/profile_cubit.dart | 4 - .../profile/view/profile_edit_page.dart | 9 -- .../profile/view/profile_setup_page.dart | 16 +- .../features/rating/cubit/rating_cubit.dart | 2 - .../lib/features/rating/view/rating_page.dart | 11 -- .../lib/features/rating/view/star_row.dart | 1 - .../settings/cubit/settings_cubit.dart | 2 - .../settings/cubit/settings_state.dart | 1 - .../features/settings/view/settings_page.dart | 6 - .../lib/features/splash/view/splash_page.dart | 7 - .../features/support/view/support_page.dart | 5 - .../features/trip/cubit/history_cubit.dart | 3 - .../lib/features/trip/cubit/ride_cubit.dart | 12 +- .../lib/features/trip/cubit/ride_state.dart | 5 - .../trip/data/history_repository.dart | 1 - .../features/trip/data/maps_repository.dart | 3 - .../features/trip/data/trip_repository.dart | 4 - .../lib/features/trip/view/history_page.dart | 11 -- .../lib/features/trip/view/ride_page.dart | 15 -- .../trip/view/widgets/active_trip_sheet.dart | 13 +- .../trip/view/widgets/cancel_sheet.dart | 3 - .../trip/view/widgets/center_pin.dart | 1 - .../trip/view/widgets/confirm_sheet.dart | 20 +-- .../trip/view/widgets/driver_badge.dart | 3 - .../trip/view/widgets/fare_summary.dart | 5 - .../trip/view/widgets/planner_sheet.dart | 7 - .../trip/view/widgets/route_timeline.dart | 2 - .../trip/view/widgets/searching_sheet.dart | 5 - .../features/wallet/cubit/wallet_cubit.dart | 3 - .../features/wallet/cubit/wallet_state.dart | 2 - .../wallet/data/wallet_repository.dart | 2 - .../lib/features/wallet/view/wallet_page.dart | 19 +-- .../wallet/view/widgets/balance_card.dart | 3 - .../wallet/view/widgets/txn_tile.dart | 4 - apps/rider_new/lib/main.dart | 3 - 128 files changed, 684 insertions(+), 666 deletions(-) create mode 100644 apps/driver_new/lib/core/payments/country_codes.dart create mode 100644 apps/driver_new/lib/core/ui/phone_field.dart create mode 100644 apps/rider_new/lib/core/payments/country_codes.dart create mode 100644 apps/rider_new/lib/core/ui/phone_field.dart diff --git a/apps/driver_new/lib/app.dart b/apps/driver_new/lib/app.dart index 950cc6d..fb42a30 100644 --- a/apps/driver_new/lib/app.dart +++ b/apps/driver_new/lib/app.dart @@ -1,23 +1,14 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; - import 'package:flutter_localizations/flutter_localizations.dart'; import 'core/build_config.dart'; - import 'core/design/theme.dart'; - import 'core/di.dart'; - import 'core/l10n/l10n.dart'; - import 'core/router.dart'; - import 'core/tenant/tenant_cubit.dart'; - import 'features/settings/cubit/settings_cubit.dart'; - import 'features/settings/cubit/settings_state.dart'; class TripzApp extends StatelessWidget { diff --git a/apps/driver_new/lib/core/api/antlaq_api.dart b/apps/driver_new/lib/core/api/antlaq_api.dart index 3b4990c..9470484 100644 --- a/apps/driver_new/lib/core/api/antlaq_api.dart +++ b/apps/driver_new/lib/core/api/antlaq_api.dart @@ -1,7 +1,6 @@ import 'package:dio/dio.dart'; import '../config.dart'; - import 'api_exception.dart'; /// عميل map-saas — **منفصل تماماً** عن `ApiClient`. diff --git a/apps/driver_new/lib/core/api/api_client.dart b/apps/driver_new/lib/core/api/api_client.dart index 7bd8bcc..053330c 100644 --- a/apps/driver_new/lib/core/api/api_client.dart +++ b/apps/driver_new/lib/core/api/api_client.dart @@ -1,11 +1,8 @@ import 'package:dio/dio.dart'; import '../config.dart'; - import '../storage/token_store.dart'; - import 'api_exception.dart'; - import 'auth_interceptor.dart'; /// الواجهة الوحيدة للخادم. لا تُنشأ `Dio` في أي مكان آخر. diff --git a/apps/driver_new/lib/core/api/auth_interceptor.dart b/apps/driver_new/lib/core/api/auth_interceptor.dart index ebda130..4663e27 100644 --- a/apps/driver_new/lib/core/api/auth_interceptor.dart +++ b/apps/driver_new/lib/core/api/auth_interceptor.dart @@ -3,9 +3,7 @@ import 'dart:async'; import 'package:dio/dio.dart'; import '../build_config.dart'; - import '../config.dart'; - import '../storage/token_store.dart'; /// يثبّت ترويسات كل طلب، ويجدّد التوكن **استباقياً** قبل انتهائه. diff --git a/apps/driver_new/lib/core/build_config.dart b/apps/driver_new/lib/core/build_config.dart index e9aaf9a..5aa721e 100644 --- a/apps/driver_new/lib/core/build_config.dart +++ b/apps/driver_new/lib/core/build_config.dart @@ -12,7 +12,13 @@ class BuildConfig { static const String tenantSlug = 'siro'; /// اللون البذرة — منه تُشتقّ `ColorScheme` للوضعين. - static const Color seedColor = Color(0xFF1F6FEB); + /// + /// كحليّ سيرو (`#1A2340`) بقرار المالك 2026-08-05: الهوية البصرية تقترب من + /// سيرو المجرَّب ميدانياً بدل الأزرق العام. + static const Color seedColor = Color(0xFF1A2340); + + /// اللون الثانوي — بنفسجي فاتح، يُستعمل للتمييز والحالات النشطة. + static const Color accentColor = Color(0xFF8C9CF8); static const String logoAsset = 'assets/images/logo.png'; static const String splashLogoAsset = 'assets/images/splash_logo.png'; diff --git a/apps/driver_new/lib/core/design/theme.dart b/apps/driver_new/lib/core/design/theme.dart index 0b7204c..fe1141b 100644 --- a/apps/driver_new/lib/core/design/theme.dart +++ b/apps/driver_new/lib/core/design/theme.dart @@ -1,11 +1,8 @@ import 'package:flutter/material.dart'; import '../build_config.dart'; - import 'tokens.dart'; - import 'tripz_colors.dart'; - import 'typography.dart'; /// دالة بناء الثيم الوحيدة (docs/26 §4): مدخلان لا ثالث لهما. @@ -13,9 +10,32 @@ import 'typography.dart'; /// الاتجاه (RTL/LTR) **لا يُفرض هنا** — `MaterialApp` يشتقّه من الـlocale /// وحده. فرض `Directionality.rtl` يكسر الإنجليزية (docs/26 §4). ThemeData buildTheme(Brightness brightness, Locale locale) { + // البذرة الكحلية داكنة جداً، و`fromSeed` وحدها تنتج منها أسطحاً باهتة. + // نثبّت الأساسي والثانوي صراحةً ونترك للمشتقّ باقي الأدوار — فيبقى الشكل + // قريباً من سيرو مع ضمان التباين الذي يوفّره M3. final scheme = ColorScheme.fromSeed( seedColor: BuildConfig.seedColor, brightness: brightness, + ).copyWith( + primary: brightness == Brightness.dark + ? BuildConfig.accentColor + : BuildConfig.seedColor, + onPrimary: brightness == Brightness.dark + ? BuildConfig.seedColor + : Colors.white, + secondary: BuildConfig.accentColor, + surface: brightness == Brightness.dark + ? const Color(0xFF161618) + : const Color(0xFFF8FAFC), + onSurface: brightness == Brightness.dark + ? Colors.white + : const Color(0xFF0F172A), + onSurfaceVariant: brightness == Brightness.dark + ? const Color(0xFFB0B3B8) + : const Color(0xFF64748B), + outlineVariant: brightness == Brightness.dark + ? const Color(0xFF3A3A3C) + : const Color(0xFFE2E8F0), ); final isDark = brightness == Brightness.dark; final texts = buildTextTheme(brightness, locale); diff --git a/apps/driver_new/lib/core/design/tripz_colors.dart b/apps/driver_new/lib/core/design/tripz_colors.dart index 23c9cdb..97f59e4 100644 --- a/apps/driver_new/lib/core/design/tripz_colors.dart +++ b/apps/driver_new/lib/core/design/tripz_colors.dart @@ -40,35 +40,37 @@ class TripzColors extends ThemeExtension { /// لون هيكل التحميل (skeleton) — لا spinner فارغ (docs/26 §8.4). final Color skeleton; + // الأرقام من نظام سيرو الميداني: نجاح `#22C55E` · تحذير `#F59E0B` · + // خطأ `#EF4444` — مألوفة لمستخدمي سيرو ومجرَّبة على شاشات الشمس. static const light = TripzColors( - success: Color(0xFF1B8A5A), + success: Color(0xFF22C55E), onSuccess: Color(0xFFFFFFFF), - warning: Color(0xFFB4690E), - onWarning: Color(0xFFFFFFFF), - danger: Color(0xFFC5303B), + warning: Color(0xFFF59E0B), + onWarning: Color(0xFF2A1A00), + danger: Color(0xFFEF4444), onDanger: Color(0xFFFFFFFF), - info: Color(0xFF2563A8), + info: Color(0xFF8C9CF8), surfaceRaised: Color(0xFFFFFFFF), - mapRoute: Color(0xFF1F6FEB), - mapOrigin: Color(0xFF1B8A5A), - mapDestination: Color(0xFFC5303B), - skeleton: Color(0xFFE6E8EC), + mapRoute: Color(0xFF1A2340), + mapOrigin: Color(0xFF22C55E), + mapDestination: Color(0xFFEF4444), + skeleton: Color(0xFFE2E8F0), ); static const dark = TripzColors( - success: Color(0xFF4ECB8D), + success: Color(0xFF4ADE80), onSuccess: Color(0xFF00281A), - warning: Color(0xFFE9A63B), + warning: Color(0xFFFBBF24), onWarning: Color(0xFF2A1A00), - danger: Color(0xFFF2707A), + danger: Color(0xFFF87171), onDanger: Color(0xFF33000A), - info: Color(0xFF7FB2F0), - // ليس أسود صرفاً — رمادي داكن متدرّج. - surfaceRaised: Color(0xFF20232A), - mapRoute: Color(0xFF5B9BFF), - mapOrigin: Color(0xFF4ECB8D), - mapDestination: Color(0xFFF2707A), - skeleton: Color(0xFF2C3038), + info: Color(0xFF8C9CF8), + // ليس أسود صرفاً — رمادي داكن متدرّج (بطاقة سيرو `#2C2C2E`). + surfaceRaised: Color(0xFF2C2C2E), + mapRoute: Color(0xFF8C9CF8), + mapOrigin: Color(0xFF4ADE80), + mapDestination: Color(0xFFF87171), + skeleton: Color(0xFF3A3A3C), ); @override diff --git a/apps/driver_new/lib/core/notifications/push_service.dart b/apps/driver_new/lib/core/notifications/push_service.dart index f9de3a7..8001472 100644 --- a/apps/driver_new/lib/core/notifications/push_service.dart +++ b/apps/driver_new/lib/core/notifications/push_service.dart @@ -1,11 +1,8 @@ import 'package:firebase_core/firebase_core.dart'; - import 'package:firebase_messaging/firebase_messaging.dart'; - import 'package:flutter/foundation.dart'; import '../api/api_client.dart'; - import '../api/api_exception.dart'; /// إشعارات FCM — **الطبقة الوحيدة التي تعرف Firebase**. diff --git a/apps/driver_new/lib/core/payments/country_codes.dart b/apps/driver_new/lib/core/payments/country_codes.dart new file mode 100644 index 0000000..0c64dfe --- /dev/null +++ b/apps/driver_new/lib/core/payments/country_codes.dart @@ -0,0 +1,48 @@ +/// مفاتيح الدول المدعومة. +/// +/// حزمة `intl_phone_field` التي يستعملها سيرو تحمل **240 دولة** ولا نحتاج +/// منها إلا أربعاً، وتجرّ معها أصولاً وتبعيّات. القائمة هنا صريحة ومقصورة +/// على أسواقنا — وتتوسّع بسطر واحد. +/// +/// ترتيب العرض يبدأ بحزمة دولة المستأجر (`countryPack`)، فالمشغّل الأردني +/// يرى الأردن أولاً بلا بحث. +enum CountryCode { + jordan('jo', '962', 'الأردن', 'Jordan', '🇯🇴', 9), + syria('sy', '963', 'سوريا', 'Syria', '🇸🇾', 9), + egypt('eg', '20', 'مصر', 'Egypt', '🇪🇬', 10), + iraq('iq', '964', 'العراق', 'Iraq', '🇮🇶', 10); + + const CountryCode( + this.pack, + this.dial, + this.nameAr, + this.nameEn, + this.flag, + this.nationalLength, + ); + + /// يطابق `countryPack` في إعداد المستأجر. + final String pack; + + /// المفتاح الدولي بلا `+`. + final String dial; + + final String nameAr; + final String nameEn; + final String flag; + + /// طول الرقم الوطني **بلا الصفر البادئ** — أساس التحقّق المحلّي. + final int nationalLength; + + String nameFor(String languageCode) => + languageCode == 'ar' ? nameAr : nameEn; + + static CountryCode fromPack(String pack) => + values.firstWhere((c) => c.pack == pack, orElse: () => jordan); + + /// المستأجر أولاً ثم الباقي — لا ترتيب أبجدي يدفن سوق المشغّل للأسفل. + static List orderedFor(String pack) { + final first = fromPack(pack); + return [first, ...values.where((c) => c != first)]; + } +} diff --git a/apps/driver_new/lib/core/payments/payment_channels.dart b/apps/driver_new/lib/core/payments/payment_channels.dart index 03cab81..4e7b7a4 100644 --- a/apps/driver_new/lib/core/payments/payment_channels.dart +++ b/apps/driver_new/lib/core/payments/payment_channels.dart @@ -14,6 +14,8 @@ enum PaymentChannel { mtn('mtn_cash', 'MTN Cash', 'إم تي إن كاش'), shamCash('sham_cash', 'Sham Cash', 'شام كاش'), paymob('paymob', 'PayMob', 'باي موب'), + zainCash('zain_cash', 'ZainCash', 'زين كاش'), + qiCard('qi_card', 'Qi Card', 'كي كارد'), cash('cash', 'Cash', 'نقداً'); const PaymentChannel(this.wire, this.nameEn, this.nameAr); @@ -38,6 +40,14 @@ enum PaymentChannel { PaymentChannel.cash, ], 'eg' => const [PaymentChannel.paymob, PaymentChannel.cash], + // العراق: المحافظ الأشيع محلياً. ⚠️ الباك إند لا يعرف حزمة `iq` بعد + // (`default-tariffs.ts` فيه jo/sy/eg فقط) — مستأجر عراقي يُزوَّد بلا + // تعرفة، وهذا شغل خادم يسبق أي إطلاق عراقي. + 'iq' => const [ + PaymentChannel.zainCash, + PaymentChannel.qiCard, + PaymentChannel.cash, + ], // حزمة غير معروفة: النقد وحده — لا نعرض قناة قد لا تعمل في سوقها. _ => const [PaymentChannel.cash], }; diff --git a/apps/driver_new/lib/core/session/session_cubit.dart b/apps/driver_new/lib/core/session/session_cubit.dart index 622377a..d390208 100644 --- a/apps/driver_new/lib/core/session/session_cubit.dart +++ b/apps/driver_new/lib/core/session/session_cubit.dart @@ -3,11 +3,8 @@ import 'dart:async'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../api/api_exception.dart'; - import 'app_user.dart'; - import 'session_source.dart'; - import 'session_state.dart'; /// حالة الجلسة على مستوى التطبيق — مصدر قرار التوجيه الوحيد diff --git a/apps/driver_new/lib/core/tenant/feature_gate.dart b/apps/driver_new/lib/core/tenant/feature_gate.dart index 9d51ae4..9e5d07a 100644 --- a/apps/driver_new/lib/core/tenant/feature_gate.dart +++ b/apps/driver_new/lib/core/tenant/feature_gate.dart @@ -1,5 +1,4 @@ import 'package:flutter/widgets.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import 'tenant_cubit.dart'; diff --git a/apps/driver_new/lib/core/tenant/tenant_cubit.dart b/apps/driver_new/lib/core/tenant/tenant_cubit.dart index 1ae1444..c56eebd 100644 --- a/apps/driver_new/lib/core/tenant/tenant_cubit.dart +++ b/apps/driver_new/lib/core/tenant/tenant_cubit.dart @@ -1,13 +1,9 @@ import 'package:flutter_bloc/flutter_bloc.dart'; - import 'package:shared_preferences/shared_preferences.dart'; import '../api/api_client.dart'; - import '../api/api_exception.dart'; - import '../build_config.dart'; - import 'tenant_config.dart'; /// يجلب إعداد المستأجر مرة عند الإقلاع، ويخزّنه محلياً. diff --git a/apps/driver_new/lib/core/ui/app_drawer.dart b/apps/driver_new/lib/core/ui/app_drawer.dart index a164663..4b7805f 100644 --- a/apps/driver_new/lib/core/ui/app_drawer.dart +++ b/apps/driver_new/lib/core/ui/app_drawer.dart @@ -1,25 +1,15 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; - import 'package:go_router/go_router.dart'; import '../build_config.dart'; - import '../config.dart'; - import '../design/tokens.dart'; - import '../design/tripz_colors.dart'; - import '../l10n/l10n.dart'; - import '../router.dart'; - import '../session/session_cubit.dart'; - import '../session/session_state.dart'; - import '../tenant/feature_gate.dart'; /// الدراور — نقطة تطبيق **طبقتَي الميزات** معاً (docs/23 §5 + docs/38 §10): diff --git a/apps/driver_new/lib/core/ui/empty_view.dart b/apps/driver_new/lib/core/ui/empty_view.dart index bcd69aa..bae9cdb 100644 --- a/apps/driver_new/lib/core/ui/empty_view.dart +++ b/apps/driver_new/lib/core/ui/empty_view.dart @@ -1,11 +1,8 @@ import 'package:flutter/material.dart'; import '../design/tokens.dart'; - import '../design/tripz_colors.dart'; - import '../l10n/l10n.dart'; - import 'tripz_button.dart'; /// حالة الفراغ: رسالة **تدلّ على الفعل التالي** لا مجرّد «لا يوجد» diff --git a/apps/driver_new/lib/core/ui/error_view.dart b/apps/driver_new/lib/core/ui/error_view.dart index e348218..0e0eb7c 100644 --- a/apps/driver_new/lib/core/ui/error_view.dart +++ b/apps/driver_new/lib/core/ui/error_view.dart @@ -1,11 +1,8 @@ import 'package:flutter/material.dart'; import '../design/tokens.dart'; - import '../design/tripz_colors.dart'; - import '../l10n/l10n.dart'; - import 'tripz_button.dart'; /// حالة الخطأ: رسالة **بالعربية** مع «أعد المحاولة» — لا شاشة صامتة diff --git a/apps/driver_new/lib/core/ui/failure_text.dart b/apps/driver_new/lib/core/ui/failure_text.dart index d13b933..50e14d1 100644 --- a/apps/driver_new/lib/core/ui/failure_text.dart +++ b/apps/driver_new/lib/core/ui/failure_text.dart @@ -1,13 +1,9 @@ import 'package:flutter/material.dart'; import '../../features/auth/cubit/phone_error.dart'; - import '../api/api_failure.dart'; - import '../design/tokens.dart'; - import '../design/tripz_colors.dart'; - import '../l10n/l10n.dart'; /// النقطة **الوحيدة** التي تُترجَم فيها رموز الفشل إلى نصّ. diff --git a/apps/driver_new/lib/core/ui/form_header.dart b/apps/driver_new/lib/core/ui/form_header.dart index 201213d..5f4ae60 100644 --- a/apps/driver_new/lib/core/ui/form_header.dart +++ b/apps/driver_new/lib/core/ui/form_header.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import '../design/tokens.dart'; - import '../design/tripz_colors.dart'; /// ترويسة موحّدة لشاشات الدخول: أيقونة ثم عنوان ثم سطر شارح. diff --git a/apps/driver_new/lib/core/ui/phone_field.dart b/apps/driver_new/lib/core/ui/phone_field.dart new file mode 100644 index 0000000..95e0024 --- /dev/null +++ b/apps/driver_new/lib/core/ui/phone_field.dart @@ -0,0 +1,141 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +import '../design/tokens.dart'; +import '../design/tripz_colors.dart'; +import '../design/typography.dart'; +import '../payments/country_codes.dart'; + +/// حقل الهاتف بمفتاح دولة — النمط المجرَّب في سيرو. +/// +/// المفتاح يُختار من ورقة سفلية لا من قائمة منسدلة: الأصابع على الهاتف +/// أدقّ مع أهداف كبيرة، والقائمة أربع دول لا مئتان. +/// +/// يُخرج الرقم **كاملاً بالمفتاح بلا `+`** (`962790000001`) — وهو ما يقبله +/// الخادم ويطبّعه (docs/38 §2). +class PhoneField extends StatelessWidget { + const PhoneField({ + super.key, + required this.controller, + required this.country, + required this.onCountryChanged, + this.label, + this.hint, + this.errorText, + this.enabled = true, + this.onSubmitted, + }); + + final TextEditingController controller; + final CountryCode country; + final ValueChanged onCountryChanged; + final String? label; + final String? hint; + final String? errorText; + final bool enabled; + final ValueChanged? onSubmitted; + + @override + Widget build(BuildContext context) { + return TextField( + controller: controller, + enabled: enabled, + autofocus: true, + keyboardType: TextInputType.phone, + textInputAction: TextInputAction.done, + onSubmitted: onSubmitted, + maxLength: country.nationalLength + 1, + inputFormatters: [FilteringTextInputFormatter.digitsOnly], + autofillHints: const [AutofillHints.telephoneNumber], + style: numericStyle(context.texts.bodyLarge), + // الرقم يُقرأ يساراً-ليميناً حتى في واجهة عربية. + textDirection: TextDirection.ltr, + decoration: InputDecoration( + labelText: label, + hintText: hint, + errorText: errorText, + counterText: '', + prefixIcon: _DialPrefix( + country: country, + enabled: enabled, + onTap: () => _pickCountry(context), + ), + prefixIconConstraints: const BoxConstraints(minWidth: 0), + ), + ); + } + + Future _pickCountry(BuildContext context) async { + final lang = Localizations.localeOf(context).languageCode; + final picked = await showModalBottomSheet( + context: context, + builder: (context) => SafeArea( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + for (final c in CountryCode.values) + ListTile( + leading: Text(c.flag, style: const TextStyle(fontSize: 24)), + title: Text(c.nameFor(lang)), + trailing: Text( + '+${c.dial}', + textDirection: TextDirection.ltr, + style: numericStyle(context.texts.bodyMedium), + ), + selected: c == country, + onTap: () => Navigator.of(context).pop(c), + ), + ], + ), + ), + ); + if (picked != null) onCountryChanged(picked); + } +} + +class _DialPrefix extends StatelessWidget { + const _DialPrefix({ + required this.country, + required this.enabled, + required this.onTap, + }); + + final CountryCode country; + final bool enabled; + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: enabled ? onTap : null, + borderRadius: Radii.field_, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: Space.sm), + child: Row( + mainAxisSize: MainAxisSize.min, + textDirection: TextDirection.ltr, + children: [ + Text(country.flag, style: const TextStyle(fontSize: 20)), + const SizedBox(width: Space.xxs), + Text( + '+${country.dial}', + style: numericStyle(context.texts.bodyLarge), + ), + Icon( + Icons.arrow_drop_down_rounded, + color: context.colors.onSurfaceVariant, + ), + const SizedBox(width: Space.xxs), + SizedBox( + height: 24, + child: VerticalDivider( + width: 1, + color: context.colors.outlineVariant, + ), + ), + ], + ), + ), + ); + } +} diff --git a/apps/driver_new/lib/core/ui/skeleton.dart b/apps/driver_new/lib/core/ui/skeleton.dart index f72c366..9c5fddb 100644 --- a/apps/driver_new/lib/core/ui/skeleton.dart +++ b/apps/driver_new/lib/core/ui/skeleton.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import '../design/tokens.dart'; - import '../design/tripz_colors.dart'; /// هيكل تحميل — **لا spinner فارغ** (docs/26 §8.4). diff --git a/apps/driver_new/lib/core/ui/status_banner.dart b/apps/driver_new/lib/core/ui/status_banner.dart index 4007c18..b2e3c9d 100644 --- a/apps/driver_new/lib/core/ui/status_banner.dart +++ b/apps/driver_new/lib/core/ui/status_banner.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import '../design/tokens.dart'; - import '../design/tripz_colors.dart'; enum BannerTone { info, success, warning, danger } diff --git a/apps/driver_new/lib/core/ui/tripz_otp_field.dart b/apps/driver_new/lib/core/ui/tripz_otp_field.dart index ad1a22d..0779f88 100644 --- a/apps/driver_new/lib/core/ui/tripz_otp_field.dart +++ b/apps/driver_new/lib/core/ui/tripz_otp_field.dart @@ -1,9 +1,7 @@ import 'package:flutter/material.dart'; - import 'package:flutter/services.dart'; import '../design/tokens.dart'; - import '../design/typography.dart'; /// حقل رمز التحقّق — أربع خانات (طول رمز تريبز، docs/38 §2). diff --git a/apps/driver_new/lib/core/ui/tripz_scaffold.dart b/apps/driver_new/lib/core/ui/tripz_scaffold.dart index 9972c2b..8e5fdcf 100644 --- a/apps/driver_new/lib/core/ui/tripz_scaffold.dart +++ b/apps/driver_new/lib/core/ui/tripz_scaffold.dart @@ -1,13 +1,9 @@ import 'package:flutter/material.dart'; import '../design/tokens.dart'; - import 'empty_view.dart'; - import 'error_view.dart'; - import 'skeleton.dart'; - import 'view_status.dart'; /// نقطة التنظيم المركزية (docs/26 §6). **ممنوع `Scaffold` خام في diff --git a/apps/driver_new/lib/core/ui/tripz_text_field.dart b/apps/driver_new/lib/core/ui/tripz_text_field.dart index a070057..b0e0acb 100644 --- a/apps/driver_new/lib/core/ui/tripz_text_field.dart +++ b/apps/driver_new/lib/core/ui/tripz_text_field.dart @@ -1,9 +1,7 @@ import 'package:flutter/material.dart'; - import 'package:flutter/services.dart'; import '../design/tokens.dart'; - import '../design/typography.dart'; /// حقل الإدخال الموحّد (docs/26 §7): نصّ · هاتف · بحث. diff --git a/apps/driver_new/lib/features/auth/cubit/login_cubit.dart b/apps/driver_new/lib/features/auth/cubit/login_cubit.dart index bc9fec6..7ba625d 100644 --- a/apps/driver_new/lib/features/auth/cubit/login_cubit.dart +++ b/apps/driver_new/lib/features/auth/cubit/login_cubit.dart @@ -1,22 +1,16 @@ import 'dart:async'; import 'package:flutter_bloc/flutter_bloc.dart'; - import 'package:geolocator/geolocator.dart'; import 'package:permission_handler/permission_handler.dart'; - import 'package:shared_preferences/shared_preferences.dart'; import '../../../core/api/api_exception.dart'; - import '../../../core/api/api_failure.dart'; - import '../../../core/config.dart'; - +import '../../../core/payments/country_codes.dart'; import '../data/auth_repository.dart'; - import 'login_state.dart'; - import 'phone_error.dart'; /// تدفّق الدخول كاملاً: الشروط ← إذن الموقع ← الهاتف ← الرمز. @@ -121,6 +115,15 @@ class LoginCubit extends Cubit { // ── الهاتف ───────────────────────────────────────────────────────────── + /// يُنادى عند الإقلاع بحزمة دولة المستأجر — فيرى الأردنيُّ الأردن أولاً. + void applyTenantCountry(String countryPack) { + if (state.phone.isNotEmpty) return; // لا نغيّر اختياراً واعياً للمستخدم + emit(state.copyWith(country: CountryCode.fromPack(countryPack))); + } + + void countryChanged(CountryCode country) => + emit(state.copyWith(country: country, clearPhoneError: true)); + void phoneChanged(String value) { emit(state.copyWith( phone: value, @@ -130,14 +133,14 @@ class LoginCubit extends Cubit { } Future submitPhone() async { - final error = validatePhone(state.phone); + final error = validatePhone(state.phone, state.country); if (error != null) { emit(state.copyWith(phoneError: error)); return; } emit(state.copyWith(status: LoginStatus.submitting, clearFailure: true)); try { - await _repo.sendOtp(state.phone.trim()); + await _repo.sendOtp(state.e164); emit(state.copyWith(status: LoginStatus.idle, step: LoginStep.otp)); _startResendCountdown(); } on ApiException catch (e) { @@ -164,7 +167,7 @@ class LoginCubit extends Cubit { if (!state.canResend) return; emit(state.copyWith(status: LoginStatus.submitting, clearFailure: true)); try { - await _repo.sendOtp(state.phone.trim()); + await _repo.sendOtp(state.e164); emit(state.copyWith(status: LoginStatus.idle)); _startResendCountdown(); } on ApiException catch (e) { @@ -182,7 +185,7 @@ class LoginCubit extends Cubit { if (code.length != AppConfig.otpLength) return; emit(state.copyWith(status: LoginStatus.submitting, clearFailure: true)); try { - final user = await _repo.verifyOtp(phone: state.phone.trim(), code: code); + final user = await _repo.verifyOtp(phone: state.e164, code: code); _resendTimer?.cancel(); emit(state.copyWith( status: LoginStatus.success, diff --git a/apps/driver_new/lib/features/auth/cubit/login_state.dart b/apps/driver_new/lib/features/auth/cubit/login_state.dart index c778d23..b599375 100644 --- a/apps/driver_new/lib/features/auth/cubit/login_state.dart +++ b/apps/driver_new/lib/features/auth/cubit/login_state.dart @@ -1,9 +1,8 @@ import 'package:equatable/equatable.dart'; import '../../../core/api/api_failure.dart'; - +import '../../../core/payments/country_codes.dart'; import '../../../core/session/app_user.dart'; - import 'phone_error.dart'; /// خطوات الدخول بالترتيب الميداني المثبت في سيرو (docs/39 §2): بوابتان @@ -17,6 +16,7 @@ class LoginState extends Equatable { this.step = LoginStep.agreement, this.status = LoginStatus.idle, this.phone = '', + this.country = CountryCode.jordan, this.agreed = false, this.failure, this.phoneError, @@ -31,6 +31,13 @@ class LoginState extends Equatable { /// الرقم كما أدخله المستخدم — الخادم يطبّعه، فلا نطبّعه نحن (docs/38 §2). final String phone; + /// الدولة المختارة — تحدّد المفتاح الدولي وطول التحقّق. تُبدأ من + /// `countryPack` المستأجر لا من لغة الجهاز. + final CountryCode country; + + /// ما يُرسل للخادم: المفتاح + الرقم بلا `+` (`962790000001`). + String get e164 => '${country.dial}${phone.trim()}'; + final bool agreed; final ApiFailure? failure; final PhoneError? phoneError; @@ -51,6 +58,7 @@ class LoginState extends Equatable { LoginStep? step, LoginStatus? status, String? phone, + CountryCode? country, bool? agreed, ApiFailure? failure, PhoneError? phoneError, @@ -64,6 +72,7 @@ class LoginState extends Equatable { step: step ?? this.step, status: status ?? this.status, phone: phone ?? this.phone, + country: country ?? this.country, agreed: agreed ?? this.agreed, failure: clearFailure ? null : (failure ?? this.failure), phoneError: clearPhoneError ? null : (phoneError ?? this.phoneError), @@ -79,6 +88,7 @@ class LoginState extends Equatable { step, status, phone, + country, agreed, failure, phoneError, diff --git a/apps/driver_new/lib/features/auth/cubit/phone_error.dart b/apps/driver_new/lib/features/auth/cubit/phone_error.dart index ad8ac94..70e754f 100644 --- a/apps/driver_new/lib/features/auth/cubit/phone_error.dart +++ b/apps/driver_new/lib/features/auth/cubit/phone_error.dart @@ -1,11 +1,15 @@ +import '../../../core/payments/country_codes.dart'; + /// أخطاء تحقّق **محليّة** لرقم الهاتف — تُكتشف قبل أي نداء شبكة. /// الشروط الثلاثة منقولة من التحقّق المطبَّق ميدانياً في سيرو (docs/39 §2). enum PhoneError { empty, leadingZero, tooShort } -PhoneError? validatePhone(String raw) { +/// الطول المتوقّع من **الدولة المختارة** لا رقماً ثابتاً: الأردن وسوريا 9 +/// خانات، ومصر والعراق 10. +PhoneError? validatePhone(String raw, CountryCode country) { final v = raw.trim(); if (v.isEmpty) return PhoneError.empty; if (v.startsWith('0')) return PhoneError.leadingZero; - if (v.length < 9) return PhoneError.tooShort; + if (v.length < country.nationalLength) return PhoneError.tooShort; return null; } diff --git a/apps/driver_new/lib/features/auth/data/auth_repository.dart b/apps/driver_new/lib/features/auth/data/auth_repository.dart index 3cc1ea3..fc7473d 100644 --- a/apps/driver_new/lib/features/auth/data/auth_repository.dart +++ b/apps/driver_new/lib/features/auth/data/auth_repository.dart @@ -1,9 +1,6 @@ import '../../../core/api/api_client.dart'; - import '../../../core/session/app_user.dart'; - import '../../../core/session/session_source.dart'; - import '../../../core/storage/token_store.dart'; /// كل نداءات المصادقة (docs/38 §2). الـCubit لا ينادي Dio مباشرة أبداً diff --git a/apps/driver_new/lib/features/auth/view/agreement_view.dart b/apps/driver_new/lib/features/auth/view/agreement_view.dart index 8a77f9c..6750178 100644 --- a/apps/driver_new/lib/features/auth/view/agreement_view.dart +++ b/apps/driver_new/lib/features/auth/view/agreement_view.dart @@ -1,22 +1,14 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/design/tokens.dart'; - import '../../../core/design/tripz_colors.dart'; - import '../../../core/l10n/l10n.dart'; - -import '../../../core/ui/tripz_button.dart'; - -import '../../../core/ui/tripz_scaffold.dart'; - -import '../cubit/login_cubit.dart'; - -import '../cubit/login_state.dart'; - import '../../../core/ui/form_header.dart'; +import '../../../core/ui/tripz_button.dart'; +import '../../../core/ui/tripz_scaffold.dart'; +import '../cubit/login_cubit.dart'; +import '../cubit/login_state.dart'; /// البوابة الأولى: شاشة كاملة لا حوار، وزر متابعة **معطّل حتى يُؤشَّر** /// (docs/39 §2). تُحفظ الموافقة فلا تتكرر. diff --git a/apps/driver_new/lib/features/auth/view/login_page.dart b/apps/driver_new/lib/features/auth/view/login_page.dart index 48dee03..64c4c38 100644 --- a/apps/driver_new/lib/features/auth/view/login_page.dart +++ b/apps/driver_new/lib/features/auth/view/login_page.dart @@ -1,21 +1,13 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/di.dart'; - import '../../../core/session/session_cubit.dart'; - import '../cubit/login_cubit.dart'; - import '../cubit/login_state.dart'; - import 'agreement_view.dart'; - import 'otp_view.dart'; - import 'permission_view.dart'; - import 'phone_view.dart'; /// مضيف تدفّق الدخول: خطوة واحدة معروضة في كل لحظة، والانتقال بينها من diff --git a/apps/driver_new/lib/features/auth/view/otp_view.dart b/apps/driver_new/lib/features/auth/view/otp_view.dart index 8a7c883..82a8cb4 100644 --- a/apps/driver_new/lib/features/auth/view/otp_view.dart +++ b/apps/driver_new/lib/features/auth/view/otp_view.dart @@ -1,26 +1,16 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/design/tokens.dart'; - import '../../../core/design/tripz_colors.dart'; - import '../../../core/l10n/l10n.dart'; - import '../../../core/ui/failure_text.dart'; - -import '../../../core/ui/tripz_button.dart'; - -import '../../../core/ui/tripz_otp_field.dart'; - -import '../../../core/ui/tripz_scaffold.dart'; - -import '../cubit/login_cubit.dart'; - -import '../cubit/login_state.dart'; - import '../../../core/ui/form_header.dart'; +import '../../../core/ui/tripz_button.dart'; +import '../../../core/ui/tripz_otp_field.dart'; +import '../../../core/ui/tripz_scaffold.dart'; +import '../cubit/login_cubit.dart'; +import '../cubit/login_state.dart'; class OtpView extends StatefulWidget { const OtpView({super.key}); diff --git a/apps/driver_new/lib/features/auth/view/permission_view.dart b/apps/driver_new/lib/features/auth/view/permission_view.dart index 449d77b..e086f51 100644 --- a/apps/driver_new/lib/features/auth/view/permission_view.dart +++ b/apps/driver_new/lib/features/auth/view/permission_view.dart @@ -1,22 +1,14 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/design/tokens.dart'; - import '../../../core/l10n/l10n.dart'; - -import '../../../core/ui/status_banner.dart'; - -import '../../../core/ui/tripz_button.dart'; - -import '../../../core/ui/tripz_scaffold.dart'; - -import '../cubit/login_cubit.dart'; - -import '../cubit/login_state.dart'; - import '../../../core/ui/form_header.dart'; +import '../../../core/ui/status_banner.dart'; +import '../../../core/ui/tripz_button.dart'; +import '../../../core/ui/tripz_scaffold.dart'; +import '../cubit/login_cubit.dart'; +import '../cubit/login_state.dart'; /// البوابة الثانية: إذن الموقع. عند الرفض النهائي لا يبقى إلا فتح إعدادات /// النظام — والعودة منها يلتقطها `LoginPage` بمراقبة دورة الحياة. diff --git a/apps/driver_new/lib/features/auth/view/phone_view.dart b/apps/driver_new/lib/features/auth/view/phone_view.dart index 93d294a..91106de 100644 --- a/apps/driver_new/lib/features/auth/view/phone_view.dart +++ b/apps/driver_new/lib/features/auth/view/phone_view.dart @@ -1,28 +1,48 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/design/tokens.dart'; - import '../../../core/l10n/l10n.dart'; - +import '../../../core/tenant/tenant_cubit.dart'; import '../../../core/ui/failure_text.dart'; - +import '../../../core/ui/form_header.dart'; +import '../../../core/ui/phone_field.dart'; import '../../../core/ui/tripz_button.dart'; - import '../../../core/ui/tripz_scaffold.dart'; - -import '../../../core/ui/tripz_text_field.dart'; - import '../cubit/login_cubit.dart'; - import '../cubit/login_state.dart'; -import '../../../core/ui/form_header.dart'; - -class PhoneView extends StatelessWidget { +class PhoneView extends StatefulWidget { const PhoneView({super.key}); + @override + State createState() => _PhoneViewState(); +} + +class _PhoneViewState extends State { + final _phone = TextEditingController(); + + @override + void initState() { + super.initState(); + _phone.addListener( + () => context.read().phoneChanged(_phone.text), + ); + // الدولة الافتراضية من المستأجر — يُطبَّق بعد أول إطار كي يتوفّر السياق. + WidgetsBinding.instance.addPostFrameCallback((_) { + if (!mounted) return; + context + .read() + .applyTenantCountry(context.read().state.countryPack); + }); + } + + @override + void dispose() { + _phone.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { final l10n = context.l10n; @@ -30,6 +50,7 @@ class PhoneView extends StatelessWidget { buildWhen: (p, n) => p.status != n.status || p.phoneError != n.phoneError || + p.country != n.country || p.failure != n.failure, builder: (context, state) { final cubit = context.read(); @@ -49,11 +70,13 @@ class PhoneView extends StatelessWidget { lead: l10n.phoneLead, ), const SizedBox(height: Space.lg), - TripzTextField.phone( + PhoneField( + controller: _phone, + country: state.country, + onCountryChanged: cubit.countryChanged, label: l10n.phoneLabel, hint: l10n.phoneHint, enabled: !state.isSubmitting, - onChanged: cubit.phoneChanged, onSubmitted: (_) => cubit.submitPhone(), errorText: state.phoneError == null ? null diff --git a/apps/driver_new/lib/features/profile/cubit/profile_cubit.dart b/apps/driver_new/lib/features/profile/cubit/profile_cubit.dart index 3fdfef9..3736c18 100644 --- a/apps/driver_new/lib/features/profile/cubit/profile_cubit.dart +++ b/apps/driver_new/lib/features/profile/cubit/profile_cubit.dart @@ -1,13 +1,9 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/api/api_exception.dart'; - import '../../../core/api/api_failure.dart'; - import '../../../core/session/app_user.dart'; - import '../../../core/ui/view_status.dart'; - import '../data/profile_repository.dart'; class ProfileState { diff --git a/apps/driver_new/lib/features/profile/view/profile_edit_page.dart b/apps/driver_new/lib/features/profile/view/profile_edit_page.dart index 4fe1654..c14f0a4 100644 --- a/apps/driver_new/lib/features/profile/view/profile_edit_page.dart +++ b/apps/driver_new/lib/features/profile/view/profile_edit_page.dart @@ -1,23 +1,14 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/design/tokens.dart'; - import '../../../core/design/tripz_colors.dart'; - import '../../../core/di.dart'; - import '../../../core/l10n/l10n.dart'; - import '../../../core/session/session_cubit.dart'; - import '../../../core/ui/tripz_button.dart'; - import '../../../core/ui/tripz_scaffold.dart'; - import '../../../core/ui/tripz_text_field.dart'; - import '../cubit/profile_cubit.dart'; /// تعديل الاسم. **الرقم لا يُعدَّل**: هو هوية الحساب على الخادم، وتغييره diff --git a/apps/driver_new/lib/features/profile/view/profile_setup_page.dart b/apps/driver_new/lib/features/profile/view/profile_setup_page.dart index bcb34ab..fb381b6 100644 --- a/apps/driver_new/lib/features/profile/view/profile_setup_page.dart +++ b/apps/driver_new/lib/features/profile/view/profile_setup_page.dart @@ -1,25 +1,15 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/design/tokens.dart'; - import '../../../core/di.dart'; - import '../../../core/l10n/l10n.dart'; - import '../../../core/session/session_cubit.dart'; - import '../../../core/ui/failure_text.dart'; - -import '../../../core/ui/tripz_button.dart'; - -import '../../../core/ui/tripz_scaffold.dart'; - -import '../../../core/ui/tripz_text_field.dart'; - import '../../../core/ui/form_header.dart'; - +import '../../../core/ui/tripz_button.dart'; +import '../../../core/ui/tripz_scaffold.dart'; +import '../../../core/ui/tripz_text_field.dart'; import '../cubit/profile_cubit.dart'; class ProfileSetupPage extends StatefulWidget { diff --git a/apps/driver_new/lib/features/rating/cubit/rating_cubit.dart b/apps/driver_new/lib/features/rating/cubit/rating_cubit.dart index d0d7364..05c47e0 100644 --- a/apps/driver_new/lib/features/rating/cubit/rating_cubit.dart +++ b/apps/driver_new/lib/features/rating/cubit/rating_cubit.dart @@ -1,9 +1,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/api/api_exception.dart'; - import '../data/rating_repository.dart'; - import 'rating_state.dart'; class RatingCubit extends Cubit { diff --git a/apps/driver_new/lib/features/rating/view/rating_page.dart b/apps/driver_new/lib/features/rating/view/rating_page.dart index 9b11882..24074fc 100644 --- a/apps/driver_new/lib/features/rating/view/rating_page.dart +++ b/apps/driver_new/lib/features/rating/view/rating_page.dart @@ -1,27 +1,16 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/design/tokens.dart'; - import '../../../core/design/tripz_colors.dart'; - import '../../../core/di.dart'; - import '../../../core/l10n/l10n.dart'; - import '../../../core/ui/tripz_button.dart'; - import '../../../core/ui/tripz_scaffold.dart'; - import '../../../core/ui/tripz_text_field.dart'; - import '../cubit/rating_cubit.dart'; - import '../cubit/rating_state.dart'; - import '../data/rating_repository.dart'; - import 'star_row.dart'; /// شاشة التقييم — **تُفرض عند فتح التطبيق** إن كانت هناك رحلة معلّقة diff --git a/apps/driver_new/lib/features/rating/view/star_row.dart b/apps/driver_new/lib/features/rating/view/star_row.dart index f118b82..bd8f664 100644 --- a/apps/driver_new/lib/features/rating/view/star_row.dart +++ b/apps/driver_new/lib/features/rating/view/star_row.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import '../../../core/design/tokens.dart'; - import '../../../core/design/tripz_colors.dart'; /// خمس نجوم — أهداف لمس ≥ 48 لأن الشاشة تُستعمل عادةً والمستخدم مستعجل. diff --git a/apps/driver_new/lib/features/settings/cubit/settings_cubit.dart b/apps/driver_new/lib/features/settings/cubit/settings_cubit.dart index b020ca3..4875dff 100644 --- a/apps/driver_new/lib/features/settings/cubit/settings_cubit.dart +++ b/apps/driver_new/lib/features/settings/cubit/settings_cubit.dart @@ -1,7 +1,5 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; - import 'package:shared_preferences/shared_preferences.dart'; import 'settings_state.dart'; diff --git a/apps/driver_new/lib/features/settings/cubit/settings_state.dart b/apps/driver_new/lib/features/settings/cubit/settings_state.dart index a0b4049..f841895 100644 --- a/apps/driver_new/lib/features/settings/cubit/settings_state.dart +++ b/apps/driver_new/lib/features/settings/cubit/settings_state.dart @@ -1,5 +1,4 @@ import 'package:equatable/equatable.dart'; - import 'package:flutter/material.dart'; /// المظهر واللغة (docs/26 §4) — محفوظان محلياً، وليسا سرّاً فيذهبان إلى diff --git a/apps/driver_new/lib/features/settings/view/settings_page.dart b/apps/driver_new/lib/features/settings/view/settings_page.dart index ce7d4c5..8c313d8 100644 --- a/apps/driver_new/lib/features/settings/view/settings_page.dart +++ b/apps/driver_new/lib/features/settings/view/settings_page.dart @@ -1,17 +1,11 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/design/tokens.dart'; - import '../../../core/design/tripz_colors.dart'; - import '../../../core/l10n/l10n.dart'; - import '../../../core/ui/tripz_scaffold.dart'; - import '../cubit/settings_cubit.dart'; - import '../cubit/settings_state.dart'; /// المظهر واللغة. تبديل اللغة **يقلب الاتجاه كاملاً** بلا إعادة تشغيل — diff --git a/apps/driver_new/lib/features/splash/view/splash_page.dart b/apps/driver_new/lib/features/splash/view/splash_page.dart index 0a0f48b..c043869 100644 --- a/apps/driver_new/lib/features/splash/view/splash_page.dart +++ b/apps/driver_new/lib/features/splash/view/splash_page.dart @@ -1,19 +1,12 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/build_config.dart'; - import '../../../core/config.dart'; - import '../../../core/design/tokens.dart'; - import '../../../core/design/tripz_colors.dart'; - import '../../../core/l10n/l10n.dart'; - import '../../../core/session/session_cubit.dart'; - import '../../../core/tenant/tenant_cubit.dart'; /// شاشة الإقلاع: تستعيد الجلسة ثم يتولّى الراوتر التوجيه. لا تنقّل من هنا — diff --git a/apps/driver_new/lib/features/support/view/support_page.dart b/apps/driver_new/lib/features/support/view/support_page.dart index 55bc86f..e9bcf0a 100644 --- a/apps/driver_new/lib/features/support/view/support_page.dart +++ b/apps/driver_new/lib/features/support/view/support_page.dart @@ -1,15 +1,10 @@ import 'package:flutter/material.dart'; import '../../../core/build_config.dart'; - import '../../../core/design/tokens.dart'; - import '../../../core/design/tripz_colors.dart'; - import '../../../core/l10n/l10n.dart'; - import '../../../core/ui/tripz_card.dart'; - import '../../../core/ui/tripz_scaffold.dart'; /// المساعدة و«عن التطبيق». محتوى محلّي عمداً: الخادم لا يوفّر نقطة أسئلة diff --git a/apps/driver_new/lib/features/trip/cubit/history_cubit.dart b/apps/driver_new/lib/features/trip/cubit/history_cubit.dart index 1f7b35e..3901e20 100644 --- a/apps/driver_new/lib/features/trip/cubit/history_cubit.dart +++ b/apps/driver_new/lib/features/trip/cubit/history_cubit.dart @@ -1,11 +1,8 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/api/api_exception.dart'; - import '../../../core/ui/view_status.dart'; - import '../data/history_repository.dart'; - import '../data/models/trip.dart'; class HistoryState { diff --git a/apps/driver_new/lib/features/trip/data/history_repository.dart b/apps/driver_new/lib/features/trip/data/history_repository.dart index b57ccb6..7667b08 100644 --- a/apps/driver_new/lib/features/trip/data/history_repository.dart +++ b/apps/driver_new/lib/features/trip/data/history_repository.dart @@ -1,5 +1,4 @@ import '../../../core/api/api_client.dart'; - import 'models/trip.dart'; /// سجلّ الرحلات. لا يستورد ميزة الرحلة كمنطق — يعيد استعمال نموذجها فقط، diff --git a/apps/driver_new/lib/features/trip/data/maps_repository.dart b/apps/driver_new/lib/features/trip/data/maps_repository.dart index 292cc26..f3dff78 100644 --- a/apps/driver_new/lib/features/trip/data/maps_repository.dart +++ b/apps/driver_new/lib/features/trip/data/maps_repository.dart @@ -1,9 +1,6 @@ import '../../../core/api/antlaq_api.dart'; - import 'models/geo_point.dart'; - import 'models/place.dart'; - import 'models/route_info.dart'; /// كل الخرائط من map-saas مباشرة (قرار 2026-07-20). نقاط `/maps/*` في باك إند diff --git a/apps/driver_new/lib/features/trip/data/trip_repository.dart b/apps/driver_new/lib/features/trip/data/trip_repository.dart index f82d925..9b1afe9 100644 --- a/apps/driver_new/lib/features/trip/data/trip_repository.dart +++ b/apps/driver_new/lib/features/trip/data/trip_repository.dart @@ -1,11 +1,7 @@ import '../../../core/api/api_client.dart'; - import 'models/fare_quote.dart'; - import 'models/geo_point.dart'; - import 'models/ride_type.dart'; - import 'models/trip.dart'; class TripRepository { diff --git a/apps/driver_new/lib/features/trip/view/history_page.dart b/apps/driver_new/lib/features/trip/view/history_page.dart index a62070b..856e7d3 100644 --- a/apps/driver_new/lib/features/trip/view/history_page.dart +++ b/apps/driver_new/lib/features/trip/view/history_page.dart @@ -1,27 +1,16 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/design/tokens.dart'; - import '../../../core/design/tripz_colors.dart'; - import '../../../core/design/typography.dart'; - import '../../../core/di.dart'; - import '../../../core/format/money.dart'; - import '../../../core/l10n/l10n.dart'; - import '../../../core/ui/empty_view.dart'; - import '../../../core/ui/tripz_card.dart'; - import '../../../core/ui/tripz_scaffold.dart'; - import '../cubit/history_cubit.dart'; - import '../data/models/trip.dart'; class HistoryPage extends StatefulWidget { diff --git a/apps/driver_new/lib/features/trip/view/widgets/driver_badge.dart b/apps/driver_new/lib/features/trip/view/widgets/driver_badge.dart index d4493cd..cabb7ac 100644 --- a/apps/driver_new/lib/features/trip/view/widgets/driver_badge.dart +++ b/apps/driver_new/lib/features/trip/view/widgets/driver_badge.dart @@ -1,11 +1,8 @@ import 'package:flutter/material.dart'; import '../../../../core/design/tokens.dart'; - import '../../../../core/design/tripz_colors.dart'; - import '../../../../core/design/typography.dart'; - import '../../data/models/trip.dart'; /// بطاقة السائق ولوحة المركبة — أهم ما يبحث عنه الراكب في الشارع، فيأخذ diff --git a/apps/driver_new/lib/features/trip/view/widgets/fare_summary.dart b/apps/driver_new/lib/features/trip/view/widgets/fare_summary.dart index 70110ba..baf4f83 100644 --- a/apps/driver_new/lib/features/trip/view/widgets/fare_summary.dart +++ b/apps/driver_new/lib/features/trip/view/widgets/fare_summary.dart @@ -1,15 +1,10 @@ import 'package:flutter/material.dart'; 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/trip.dart'; /// ملخّص الأجرة عند الإنهاء. diff --git a/apps/driver_new/lib/features/wallet/cubit/wallet_cubit.dart b/apps/driver_new/lib/features/wallet/cubit/wallet_cubit.dart index 2e23664..b8f743a 100644 --- a/apps/driver_new/lib/features/wallet/cubit/wallet_cubit.dart +++ b/apps/driver_new/lib/features/wallet/cubit/wallet_cubit.dart @@ -1,11 +1,8 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/api/api_exception.dart'; - import '../../../core/ui/view_status.dart'; - import '../data/wallet_repository.dart'; - import 'wallet_state.dart'; class WalletCubit extends Cubit { diff --git a/apps/driver_new/lib/features/wallet/cubit/wallet_state.dart b/apps/driver_new/lib/features/wallet/cubit/wallet_state.dart index 36f6287..5ddedc6 100644 --- a/apps/driver_new/lib/features/wallet/cubit/wallet_state.dart +++ b/apps/driver_new/lib/features/wallet/cubit/wallet_state.dart @@ -1,9 +1,7 @@ import 'package:equatable/equatable.dart'; import '../../../core/ui/view_status.dart'; - import '../data/models/wallet.dart'; - import '../data/models/wallet_txn.dart'; enum WalletAction { none, toppingUp, topupFailed, topupDone } diff --git a/apps/driver_new/lib/features/wallet/data/wallet_repository.dart b/apps/driver_new/lib/features/wallet/data/wallet_repository.dart index 31972d0..2f7a121 100644 --- a/apps/driver_new/lib/features/wallet/data/wallet_repository.dart +++ b/apps/driver_new/lib/features/wallet/data/wallet_repository.dart @@ -1,7 +1,5 @@ import '../../../core/api/api_client.dart'; - import 'models/wallet.dart'; - import 'models/wallet_txn.dart'; class WalletRepository { diff --git a/apps/driver_new/lib/features/wallet/view/widgets/balance_card.dart b/apps/driver_new/lib/features/wallet/view/widgets/balance_card.dart index 06ce746..469815a 100644 --- a/apps/driver_new/lib/features/wallet/view/widgets/balance_card.dart +++ b/apps/driver_new/lib/features/wallet/view/widgets/balance_card.dart @@ -1,11 +1,8 @@ import 'package:flutter/material.dart'; import '../../../../core/design/tokens.dart'; - import '../../../../core/design/tripz_colors.dart'; - import '../../../../core/design/typography.dart'; - import '../../../../core/format/money.dart'; /// بطاقة الرصيد — الرقم هو بطل الشاشة، فيأخذ أكبر حجم وأعلى تباين. diff --git a/apps/driver_new/lib/features/wallet/view/widgets/txn_tile.dart b/apps/driver_new/lib/features/wallet/view/widgets/txn_tile.dart index 3e06a1e..dacd33f 100644 --- a/apps/driver_new/lib/features/wallet/view/widgets/txn_tile.dart +++ b/apps/driver_new/lib/features/wallet/view/widgets/txn_tile.dart @@ -1,13 +1,9 @@ import 'package:flutter/material.dart'; import '../../../../core/design/tokens.dart'; - import '../../../../core/design/tripz_colors.dart'; - import '../../../../core/design/typography.dart'; - import '../../../../core/format/money.dart'; - import '../../data/models/wallet_txn.dart'; class TxnTile extends StatelessWidget { diff --git a/apps/driver_new/lib/main.dart b/apps/driver_new/lib/main.dart index 042ab85..955cded 100644 --- a/apps/driver_new/lib/main.dart +++ b/apps/driver_new/lib/main.dart @@ -1,11 +1,8 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import 'app.dart'; - import 'core/di.dart'; - import 'core/session/session_cubit.dart'; Future main() async { diff --git a/apps/driver_new/tool/sync_from_rider.sh b/apps/driver_new/tool/sync_from_rider.sh index 8a7e86b..936301d 100755 --- a/apps/driver_new/tool/sync_from_rider.sh +++ b/apps/driver_new/tool/sync_from_rider.sh @@ -12,7 +12,7 @@ cd "$(dirname "$0")/.." # ملفات يملكها السائق ولا تُنسخ من الراكب أبداً OWN=( "core/config.dart" # appRole = driver - "core/build_config.dart" # اسم التطبيق + "core/build_config.dart" # اسم التطبيق (الألوان تُحدَّث يدوياً معاً) "core/di.dart" # DutyCubit + OnboardingCubit + PayoutCubit "core/router.dart" # DutyPage + EarningsPage + بوابة التسجيل "core/l10n/app_ar.arb" diff --git a/apps/rider_new/lib/app.dart b/apps/rider_new/lib/app.dart index 950cc6d..fb42a30 100644 --- a/apps/rider_new/lib/app.dart +++ b/apps/rider_new/lib/app.dart @@ -1,23 +1,14 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; - import 'package:flutter_localizations/flutter_localizations.dart'; import 'core/build_config.dart'; - import 'core/design/theme.dart'; - import 'core/di.dart'; - import 'core/l10n/l10n.dart'; - import 'core/router.dart'; - import 'core/tenant/tenant_cubit.dart'; - import 'features/settings/cubit/settings_cubit.dart'; - import 'features/settings/cubit/settings_state.dart'; class TripzApp extends StatelessWidget { diff --git a/apps/rider_new/lib/core/api/antlaq_api.dart b/apps/rider_new/lib/core/api/antlaq_api.dart index 3b4990c..9470484 100644 --- a/apps/rider_new/lib/core/api/antlaq_api.dart +++ b/apps/rider_new/lib/core/api/antlaq_api.dart @@ -1,7 +1,6 @@ import 'package:dio/dio.dart'; import '../config.dart'; - import 'api_exception.dart'; /// عميل map-saas — **منفصل تماماً** عن `ApiClient`. diff --git a/apps/rider_new/lib/core/api/api_client.dart b/apps/rider_new/lib/core/api/api_client.dart index 7bd8bcc..053330c 100644 --- a/apps/rider_new/lib/core/api/api_client.dart +++ b/apps/rider_new/lib/core/api/api_client.dart @@ -1,11 +1,8 @@ import 'package:dio/dio.dart'; import '../config.dart'; - import '../storage/token_store.dart'; - import 'api_exception.dart'; - import 'auth_interceptor.dart'; /// الواجهة الوحيدة للخادم. لا تُنشأ `Dio` في أي مكان آخر. diff --git a/apps/rider_new/lib/core/api/auth_interceptor.dart b/apps/rider_new/lib/core/api/auth_interceptor.dart index ebda130..4663e27 100644 --- a/apps/rider_new/lib/core/api/auth_interceptor.dart +++ b/apps/rider_new/lib/core/api/auth_interceptor.dart @@ -3,9 +3,7 @@ import 'dart:async'; import 'package:dio/dio.dart'; import '../build_config.dart'; - import '../config.dart'; - import '../storage/token_store.dart'; /// يثبّت ترويسات كل طلب، ويجدّد التوكن **استباقياً** قبل انتهائه. diff --git a/apps/rider_new/lib/core/build_config.dart b/apps/rider_new/lib/core/build_config.dart index 59edb3f..16b5860 100644 --- a/apps/rider_new/lib/core/build_config.dart +++ b/apps/rider_new/lib/core/build_config.dart @@ -12,7 +12,13 @@ class BuildConfig { static const String tenantSlug = 'siro'; /// اللون البذرة — منه تُشتقّ `ColorScheme` للوضعين. - static const Color seedColor = Color(0xFF1F6FEB); + /// + /// كحليّ سيرو (`#1A2340`) بقرار المالك 2026-08-05: الهوية البصرية تقترب من + /// سيرو المجرَّب ميدانياً بدل الأزرق العام. + static const Color seedColor = Color(0xFF1A2340); + + /// اللون الثانوي — بنفسجي فاتح، يُستعمل للتمييز والحالات النشطة. + static const Color accentColor = Color(0xFF8C9CF8); static const String logoAsset = 'assets/images/logo.png'; static const String splashLogoAsset = 'assets/images/splash_logo.png'; diff --git a/apps/rider_new/lib/core/design/theme.dart b/apps/rider_new/lib/core/design/theme.dart index 0b7204c..fe1141b 100644 --- a/apps/rider_new/lib/core/design/theme.dart +++ b/apps/rider_new/lib/core/design/theme.dart @@ -1,11 +1,8 @@ import 'package:flutter/material.dart'; import '../build_config.dart'; - import 'tokens.dart'; - import 'tripz_colors.dart'; - import 'typography.dart'; /// دالة بناء الثيم الوحيدة (docs/26 §4): مدخلان لا ثالث لهما. @@ -13,9 +10,32 @@ import 'typography.dart'; /// الاتجاه (RTL/LTR) **لا يُفرض هنا** — `MaterialApp` يشتقّه من الـlocale /// وحده. فرض `Directionality.rtl` يكسر الإنجليزية (docs/26 §4). ThemeData buildTheme(Brightness brightness, Locale locale) { + // البذرة الكحلية داكنة جداً، و`fromSeed` وحدها تنتج منها أسطحاً باهتة. + // نثبّت الأساسي والثانوي صراحةً ونترك للمشتقّ باقي الأدوار — فيبقى الشكل + // قريباً من سيرو مع ضمان التباين الذي يوفّره M3. final scheme = ColorScheme.fromSeed( seedColor: BuildConfig.seedColor, brightness: brightness, + ).copyWith( + primary: brightness == Brightness.dark + ? BuildConfig.accentColor + : BuildConfig.seedColor, + onPrimary: brightness == Brightness.dark + ? BuildConfig.seedColor + : Colors.white, + secondary: BuildConfig.accentColor, + surface: brightness == Brightness.dark + ? const Color(0xFF161618) + : const Color(0xFFF8FAFC), + onSurface: brightness == Brightness.dark + ? Colors.white + : const Color(0xFF0F172A), + onSurfaceVariant: brightness == Brightness.dark + ? const Color(0xFFB0B3B8) + : const Color(0xFF64748B), + outlineVariant: brightness == Brightness.dark + ? const Color(0xFF3A3A3C) + : const Color(0xFFE2E8F0), ); final isDark = brightness == Brightness.dark; final texts = buildTextTheme(brightness, locale); diff --git a/apps/rider_new/lib/core/design/tripz_colors.dart b/apps/rider_new/lib/core/design/tripz_colors.dart index 23c9cdb..97f59e4 100644 --- a/apps/rider_new/lib/core/design/tripz_colors.dart +++ b/apps/rider_new/lib/core/design/tripz_colors.dart @@ -40,35 +40,37 @@ class TripzColors extends ThemeExtension { /// لون هيكل التحميل (skeleton) — لا spinner فارغ (docs/26 §8.4). final Color skeleton; + // الأرقام من نظام سيرو الميداني: نجاح `#22C55E` · تحذير `#F59E0B` · + // خطأ `#EF4444` — مألوفة لمستخدمي سيرو ومجرَّبة على شاشات الشمس. static const light = TripzColors( - success: Color(0xFF1B8A5A), + success: Color(0xFF22C55E), onSuccess: Color(0xFFFFFFFF), - warning: Color(0xFFB4690E), - onWarning: Color(0xFFFFFFFF), - danger: Color(0xFFC5303B), + warning: Color(0xFFF59E0B), + onWarning: Color(0xFF2A1A00), + danger: Color(0xFFEF4444), onDanger: Color(0xFFFFFFFF), - info: Color(0xFF2563A8), + info: Color(0xFF8C9CF8), surfaceRaised: Color(0xFFFFFFFF), - mapRoute: Color(0xFF1F6FEB), - mapOrigin: Color(0xFF1B8A5A), - mapDestination: Color(0xFFC5303B), - skeleton: Color(0xFFE6E8EC), + mapRoute: Color(0xFF1A2340), + mapOrigin: Color(0xFF22C55E), + mapDestination: Color(0xFFEF4444), + skeleton: Color(0xFFE2E8F0), ); static const dark = TripzColors( - success: Color(0xFF4ECB8D), + success: Color(0xFF4ADE80), onSuccess: Color(0xFF00281A), - warning: Color(0xFFE9A63B), + warning: Color(0xFFFBBF24), onWarning: Color(0xFF2A1A00), - danger: Color(0xFFF2707A), + danger: Color(0xFFF87171), onDanger: Color(0xFF33000A), - info: Color(0xFF7FB2F0), - // ليس أسود صرفاً — رمادي داكن متدرّج. - surfaceRaised: Color(0xFF20232A), - mapRoute: Color(0xFF5B9BFF), - mapOrigin: Color(0xFF4ECB8D), - mapDestination: Color(0xFFF2707A), - skeleton: Color(0xFF2C3038), + info: Color(0xFF8C9CF8), + // ليس أسود صرفاً — رمادي داكن متدرّج (بطاقة سيرو `#2C2C2E`). + surfaceRaised: Color(0xFF2C2C2E), + mapRoute: Color(0xFF8C9CF8), + mapOrigin: Color(0xFF4ADE80), + mapDestination: Color(0xFFF87171), + skeleton: Color(0xFF3A3A3C), ); @override diff --git a/apps/rider_new/lib/core/di.dart b/apps/rider_new/lib/core/di.dart index 1a753ba..6819186 100644 --- a/apps/rider_new/lib/core/di.dart +++ b/apps/rider_new/lib/core/di.dart @@ -1,57 +1,33 @@ import 'package:device_info_plus/device_info_plus.dart'; - import 'package:flutter/foundation.dart'; - import 'package:flutter_secure_storage/flutter_secure_storage.dart'; - import 'package:get_it/get_it.dart'; - import 'package:shared_preferences/shared_preferences.dart'; import '../features/auth/cubit/login_cubit.dart'; - import '../features/auth/data/auth_repository.dart'; import '../features/chat/cubit/chat_cubit.dart'; import '../features/chat/data/chat_repository.dart'; import '../features/profile/data/profile_repository.dart'; - import '../features/rating/cubit/rating_cubit.dart'; - import '../features/rating/data/rating_repository.dart'; - import '../features/settings/cubit/settings_cubit.dart'; - import '../features/trip/cubit/history_cubit.dart'; - import '../features/trip/cubit/ride_cubit.dart'; - import '../features/trip/data/history_repository.dart'; - import '../features/trip/data/maps_repository.dart'; - import '../features/trip/data/trip_repository.dart'; - import '../features/wallet/cubit/wallet_cubit.dart'; - import '../features/wallet/data/wallet_repository.dart'; - import 'api/antlaq_api.dart'; - import 'api/api_client.dart'; - import 'build_config.dart'; - import 'location/location_service.dart'; - import 'notifications/push_service.dart'; - import 'realtime/realtime_service.dart'; - import 'session/session_cubit.dart'; import 'session/session_source.dart'; - import 'storage/token_store.dart'; - import 'tenant/tenant_cubit.dart'; final sl = GetIt.instance; diff --git a/apps/rider_new/lib/core/notifications/push_service.dart b/apps/rider_new/lib/core/notifications/push_service.dart index f9de3a7..8001472 100644 --- a/apps/rider_new/lib/core/notifications/push_service.dart +++ b/apps/rider_new/lib/core/notifications/push_service.dart @@ -1,11 +1,8 @@ import 'package:firebase_core/firebase_core.dart'; - import 'package:firebase_messaging/firebase_messaging.dart'; - import 'package:flutter/foundation.dart'; import '../api/api_client.dart'; - import '../api/api_exception.dart'; /// إشعارات FCM — **الطبقة الوحيدة التي تعرف Firebase**. diff --git a/apps/rider_new/lib/core/payments/country_codes.dart b/apps/rider_new/lib/core/payments/country_codes.dart new file mode 100644 index 0000000..0c64dfe --- /dev/null +++ b/apps/rider_new/lib/core/payments/country_codes.dart @@ -0,0 +1,48 @@ +/// مفاتيح الدول المدعومة. +/// +/// حزمة `intl_phone_field` التي يستعملها سيرو تحمل **240 دولة** ولا نحتاج +/// منها إلا أربعاً، وتجرّ معها أصولاً وتبعيّات. القائمة هنا صريحة ومقصورة +/// على أسواقنا — وتتوسّع بسطر واحد. +/// +/// ترتيب العرض يبدأ بحزمة دولة المستأجر (`countryPack`)، فالمشغّل الأردني +/// يرى الأردن أولاً بلا بحث. +enum CountryCode { + jordan('jo', '962', 'الأردن', 'Jordan', '🇯🇴', 9), + syria('sy', '963', 'سوريا', 'Syria', '🇸🇾', 9), + egypt('eg', '20', 'مصر', 'Egypt', '🇪🇬', 10), + iraq('iq', '964', 'العراق', 'Iraq', '🇮🇶', 10); + + const CountryCode( + this.pack, + this.dial, + this.nameAr, + this.nameEn, + this.flag, + this.nationalLength, + ); + + /// يطابق `countryPack` في إعداد المستأجر. + final String pack; + + /// المفتاح الدولي بلا `+`. + final String dial; + + final String nameAr; + final String nameEn; + final String flag; + + /// طول الرقم الوطني **بلا الصفر البادئ** — أساس التحقّق المحلّي. + final int nationalLength; + + String nameFor(String languageCode) => + languageCode == 'ar' ? nameAr : nameEn; + + static CountryCode fromPack(String pack) => + values.firstWhere((c) => c.pack == pack, orElse: () => jordan); + + /// المستأجر أولاً ثم الباقي — لا ترتيب أبجدي يدفن سوق المشغّل للأسفل. + static List orderedFor(String pack) { + final first = fromPack(pack); + return [first, ...values.where((c) => c != first)]; + } +} diff --git a/apps/rider_new/lib/core/payments/payment_channels.dart b/apps/rider_new/lib/core/payments/payment_channels.dart index 03cab81..4e7b7a4 100644 --- a/apps/rider_new/lib/core/payments/payment_channels.dart +++ b/apps/rider_new/lib/core/payments/payment_channels.dart @@ -14,6 +14,8 @@ enum PaymentChannel { mtn('mtn_cash', 'MTN Cash', 'إم تي إن كاش'), shamCash('sham_cash', 'Sham Cash', 'شام كاش'), paymob('paymob', 'PayMob', 'باي موب'), + zainCash('zain_cash', 'ZainCash', 'زين كاش'), + qiCard('qi_card', 'Qi Card', 'كي كارد'), cash('cash', 'Cash', 'نقداً'); const PaymentChannel(this.wire, this.nameEn, this.nameAr); @@ -38,6 +40,14 @@ enum PaymentChannel { PaymentChannel.cash, ], 'eg' => const [PaymentChannel.paymob, PaymentChannel.cash], + // العراق: المحافظ الأشيع محلياً. ⚠️ الباك إند لا يعرف حزمة `iq` بعد + // (`default-tariffs.ts` فيه jo/sy/eg فقط) — مستأجر عراقي يُزوَّد بلا + // تعرفة، وهذا شغل خادم يسبق أي إطلاق عراقي. + 'iq' => const [ + PaymentChannel.zainCash, + PaymentChannel.qiCard, + PaymentChannel.cash, + ], // حزمة غير معروفة: النقد وحده — لا نعرض قناة قد لا تعمل في سوقها. _ => const [PaymentChannel.cash], }; diff --git a/apps/rider_new/lib/core/router.dart b/apps/rider_new/lib/core/router.dart index eb9ae61..fb4b9e1 100644 --- a/apps/rider_new/lib/core/router.dart +++ b/apps/rider_new/lib/core/router.dart @@ -1,36 +1,22 @@ import 'dart:async'; import 'package:flutter/widgets.dart'; - import 'package:go_router/go_router.dart'; import '../features/auth/view/login_page.dart'; - import '../features/chat/view/chat_page.dart'; import '../features/profile/view/profile_edit_page.dart'; - import '../features/profile/view/profile_setup_page.dart'; - import '../features/rating/data/rating_repository.dart'; - import '../features/rating/view/rating_page.dart'; - import '../features/settings/view/settings_page.dart'; - import '../features/splash/view/splash_page.dart'; - import '../features/support/view/support_page.dart'; - import '../features/trip/view/history_page.dart'; - import '../features/trip/view/ride_page.dart'; - import '../features/wallet/view/wallet_page.dart'; - import 'di.dart'; - import 'session/session_cubit.dart'; - import 'session/session_state.dart'; class Routes { diff --git a/apps/rider_new/lib/core/session/session_cubit.dart b/apps/rider_new/lib/core/session/session_cubit.dart index 622377a..d390208 100644 --- a/apps/rider_new/lib/core/session/session_cubit.dart +++ b/apps/rider_new/lib/core/session/session_cubit.dart @@ -3,11 +3,8 @@ import 'dart:async'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../api/api_exception.dart'; - import 'app_user.dart'; - import 'session_source.dart'; - import 'session_state.dart'; /// حالة الجلسة على مستوى التطبيق — مصدر قرار التوجيه الوحيد diff --git a/apps/rider_new/lib/core/tenant/feature_gate.dart b/apps/rider_new/lib/core/tenant/feature_gate.dart index 9d51ae4..9e5d07a 100644 --- a/apps/rider_new/lib/core/tenant/feature_gate.dart +++ b/apps/rider_new/lib/core/tenant/feature_gate.dart @@ -1,5 +1,4 @@ import 'package:flutter/widgets.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import 'tenant_cubit.dart'; diff --git a/apps/rider_new/lib/core/tenant/tenant_cubit.dart b/apps/rider_new/lib/core/tenant/tenant_cubit.dart index 1ae1444..c56eebd 100644 --- a/apps/rider_new/lib/core/tenant/tenant_cubit.dart +++ b/apps/rider_new/lib/core/tenant/tenant_cubit.dart @@ -1,13 +1,9 @@ import 'package:flutter_bloc/flutter_bloc.dart'; - import 'package:shared_preferences/shared_preferences.dart'; import '../api/api_client.dart'; - import '../api/api_exception.dart'; - import '../build_config.dart'; - import 'tenant_config.dart'; /// يجلب إعداد المستأجر مرة عند الإقلاع، ويخزّنه محلياً. diff --git a/apps/rider_new/lib/core/ui/app_drawer.dart b/apps/rider_new/lib/core/ui/app_drawer.dart index a164663..4b7805f 100644 --- a/apps/rider_new/lib/core/ui/app_drawer.dart +++ b/apps/rider_new/lib/core/ui/app_drawer.dart @@ -1,25 +1,15 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; - import 'package:go_router/go_router.dart'; import '../build_config.dart'; - import '../config.dart'; - import '../design/tokens.dart'; - import '../design/tripz_colors.dart'; - import '../l10n/l10n.dart'; - import '../router.dart'; - import '../session/session_cubit.dart'; - import '../session/session_state.dart'; - import '../tenant/feature_gate.dart'; /// الدراور — نقطة تطبيق **طبقتَي الميزات** معاً (docs/23 §5 + docs/38 §10): diff --git a/apps/rider_new/lib/core/ui/empty_view.dart b/apps/rider_new/lib/core/ui/empty_view.dart index bcd69aa..bae9cdb 100644 --- a/apps/rider_new/lib/core/ui/empty_view.dart +++ b/apps/rider_new/lib/core/ui/empty_view.dart @@ -1,11 +1,8 @@ import 'package:flutter/material.dart'; import '../design/tokens.dart'; - import '../design/tripz_colors.dart'; - import '../l10n/l10n.dart'; - import 'tripz_button.dart'; /// حالة الفراغ: رسالة **تدلّ على الفعل التالي** لا مجرّد «لا يوجد» diff --git a/apps/rider_new/lib/core/ui/error_view.dart b/apps/rider_new/lib/core/ui/error_view.dart index e348218..0e0eb7c 100644 --- a/apps/rider_new/lib/core/ui/error_view.dart +++ b/apps/rider_new/lib/core/ui/error_view.dart @@ -1,11 +1,8 @@ import 'package:flutter/material.dart'; import '../design/tokens.dart'; - import '../design/tripz_colors.dart'; - import '../l10n/l10n.dart'; - import 'tripz_button.dart'; /// حالة الخطأ: رسالة **بالعربية** مع «أعد المحاولة» — لا شاشة صامتة diff --git a/apps/rider_new/lib/core/ui/failure_text.dart b/apps/rider_new/lib/core/ui/failure_text.dart index d13b933..50e14d1 100644 --- a/apps/rider_new/lib/core/ui/failure_text.dart +++ b/apps/rider_new/lib/core/ui/failure_text.dart @@ -1,13 +1,9 @@ import 'package:flutter/material.dart'; import '../../features/auth/cubit/phone_error.dart'; - import '../api/api_failure.dart'; - import '../design/tokens.dart'; - import '../design/tripz_colors.dart'; - import '../l10n/l10n.dart'; /// النقطة **الوحيدة** التي تُترجَم فيها رموز الفشل إلى نصّ. diff --git a/apps/rider_new/lib/core/ui/form_header.dart b/apps/rider_new/lib/core/ui/form_header.dart index 201213d..5f4ae60 100644 --- a/apps/rider_new/lib/core/ui/form_header.dart +++ b/apps/rider_new/lib/core/ui/form_header.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import '../design/tokens.dart'; - import '../design/tripz_colors.dart'; /// ترويسة موحّدة لشاشات الدخول: أيقونة ثم عنوان ثم سطر شارح. diff --git a/apps/rider_new/lib/core/ui/phone_field.dart b/apps/rider_new/lib/core/ui/phone_field.dart new file mode 100644 index 0000000..95e0024 --- /dev/null +++ b/apps/rider_new/lib/core/ui/phone_field.dart @@ -0,0 +1,141 @@ +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +import '../design/tokens.dart'; +import '../design/tripz_colors.dart'; +import '../design/typography.dart'; +import '../payments/country_codes.dart'; + +/// حقل الهاتف بمفتاح دولة — النمط المجرَّب في سيرو. +/// +/// المفتاح يُختار من ورقة سفلية لا من قائمة منسدلة: الأصابع على الهاتف +/// أدقّ مع أهداف كبيرة، والقائمة أربع دول لا مئتان. +/// +/// يُخرج الرقم **كاملاً بالمفتاح بلا `+`** (`962790000001`) — وهو ما يقبله +/// الخادم ويطبّعه (docs/38 §2). +class PhoneField extends StatelessWidget { + const PhoneField({ + super.key, + required this.controller, + required this.country, + required this.onCountryChanged, + this.label, + this.hint, + this.errorText, + this.enabled = true, + this.onSubmitted, + }); + + final TextEditingController controller; + final CountryCode country; + final ValueChanged onCountryChanged; + final String? label; + final String? hint; + final String? errorText; + final bool enabled; + final ValueChanged? onSubmitted; + + @override + Widget build(BuildContext context) { + return TextField( + controller: controller, + enabled: enabled, + autofocus: true, + keyboardType: TextInputType.phone, + textInputAction: TextInputAction.done, + onSubmitted: onSubmitted, + maxLength: country.nationalLength + 1, + inputFormatters: [FilteringTextInputFormatter.digitsOnly], + autofillHints: const [AutofillHints.telephoneNumber], + style: numericStyle(context.texts.bodyLarge), + // الرقم يُقرأ يساراً-ليميناً حتى في واجهة عربية. + textDirection: TextDirection.ltr, + decoration: InputDecoration( + labelText: label, + hintText: hint, + errorText: errorText, + counterText: '', + prefixIcon: _DialPrefix( + country: country, + enabled: enabled, + onTap: () => _pickCountry(context), + ), + prefixIconConstraints: const BoxConstraints(minWidth: 0), + ), + ); + } + + Future _pickCountry(BuildContext context) async { + final lang = Localizations.localeOf(context).languageCode; + final picked = await showModalBottomSheet( + context: context, + builder: (context) => SafeArea( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + for (final c in CountryCode.values) + ListTile( + leading: Text(c.flag, style: const TextStyle(fontSize: 24)), + title: Text(c.nameFor(lang)), + trailing: Text( + '+${c.dial}', + textDirection: TextDirection.ltr, + style: numericStyle(context.texts.bodyMedium), + ), + selected: c == country, + onTap: () => Navigator.of(context).pop(c), + ), + ], + ), + ), + ); + if (picked != null) onCountryChanged(picked); + } +} + +class _DialPrefix extends StatelessWidget { + const _DialPrefix({ + required this.country, + required this.enabled, + required this.onTap, + }); + + final CountryCode country; + final bool enabled; + final VoidCallback onTap; + + @override + Widget build(BuildContext context) { + return InkWell( + onTap: enabled ? onTap : null, + borderRadius: Radii.field_, + child: Padding( + padding: const EdgeInsets.symmetric(horizontal: Space.sm), + child: Row( + mainAxisSize: MainAxisSize.min, + textDirection: TextDirection.ltr, + children: [ + Text(country.flag, style: const TextStyle(fontSize: 20)), + const SizedBox(width: Space.xxs), + Text( + '+${country.dial}', + style: numericStyle(context.texts.bodyLarge), + ), + Icon( + Icons.arrow_drop_down_rounded, + color: context.colors.onSurfaceVariant, + ), + const SizedBox(width: Space.xxs), + SizedBox( + height: 24, + child: VerticalDivider( + width: 1, + color: context.colors.outlineVariant, + ), + ), + ], + ), + ), + ); + } +} diff --git a/apps/rider_new/lib/core/ui/skeleton.dart b/apps/rider_new/lib/core/ui/skeleton.dart index f72c366..9c5fddb 100644 --- a/apps/rider_new/lib/core/ui/skeleton.dart +++ b/apps/rider_new/lib/core/ui/skeleton.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import '../design/tokens.dart'; - import '../design/tripz_colors.dart'; /// هيكل تحميل — **لا spinner فارغ** (docs/26 §8.4). diff --git a/apps/rider_new/lib/core/ui/status_banner.dart b/apps/rider_new/lib/core/ui/status_banner.dart index 4007c18..b2e3c9d 100644 --- a/apps/rider_new/lib/core/ui/status_banner.dart +++ b/apps/rider_new/lib/core/ui/status_banner.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import '../design/tokens.dart'; - import '../design/tripz_colors.dart'; enum BannerTone { info, success, warning, danger } diff --git a/apps/rider_new/lib/core/ui/tripz_otp_field.dart b/apps/rider_new/lib/core/ui/tripz_otp_field.dart index ad1a22d..0779f88 100644 --- a/apps/rider_new/lib/core/ui/tripz_otp_field.dart +++ b/apps/rider_new/lib/core/ui/tripz_otp_field.dart @@ -1,9 +1,7 @@ import 'package:flutter/material.dart'; - import 'package:flutter/services.dart'; import '../design/tokens.dart'; - import '../design/typography.dart'; /// حقل رمز التحقّق — أربع خانات (طول رمز تريبز، docs/38 §2). diff --git a/apps/rider_new/lib/core/ui/tripz_scaffold.dart b/apps/rider_new/lib/core/ui/tripz_scaffold.dart index 9972c2b..8e5fdcf 100644 --- a/apps/rider_new/lib/core/ui/tripz_scaffold.dart +++ b/apps/rider_new/lib/core/ui/tripz_scaffold.dart @@ -1,13 +1,9 @@ import 'package:flutter/material.dart'; import '../design/tokens.dart'; - import 'empty_view.dart'; - import 'error_view.dart'; - import 'skeleton.dart'; - import 'view_status.dart'; /// نقطة التنظيم المركزية (docs/26 §6). **ممنوع `Scaffold` خام في diff --git a/apps/rider_new/lib/core/ui/tripz_text_field.dart b/apps/rider_new/lib/core/ui/tripz_text_field.dart index a070057..b0e0acb 100644 --- a/apps/rider_new/lib/core/ui/tripz_text_field.dart +++ b/apps/rider_new/lib/core/ui/tripz_text_field.dart @@ -1,9 +1,7 @@ import 'package:flutter/material.dart'; - import 'package:flutter/services.dart'; import '../design/tokens.dart'; - import '../design/typography.dart'; /// حقل الإدخال الموحّد (docs/26 §7): نصّ · هاتف · بحث. diff --git a/apps/rider_new/lib/features/auth/cubit/login_cubit.dart b/apps/rider_new/lib/features/auth/cubit/login_cubit.dart index bc9fec6..7ba625d 100644 --- a/apps/rider_new/lib/features/auth/cubit/login_cubit.dart +++ b/apps/rider_new/lib/features/auth/cubit/login_cubit.dart @@ -1,22 +1,16 @@ import 'dart:async'; import 'package:flutter_bloc/flutter_bloc.dart'; - import 'package:geolocator/geolocator.dart'; import 'package:permission_handler/permission_handler.dart'; - import 'package:shared_preferences/shared_preferences.dart'; import '../../../core/api/api_exception.dart'; - import '../../../core/api/api_failure.dart'; - import '../../../core/config.dart'; - +import '../../../core/payments/country_codes.dart'; import '../data/auth_repository.dart'; - import 'login_state.dart'; - import 'phone_error.dart'; /// تدفّق الدخول كاملاً: الشروط ← إذن الموقع ← الهاتف ← الرمز. @@ -121,6 +115,15 @@ class LoginCubit extends Cubit { // ── الهاتف ───────────────────────────────────────────────────────────── + /// يُنادى عند الإقلاع بحزمة دولة المستأجر — فيرى الأردنيُّ الأردن أولاً. + void applyTenantCountry(String countryPack) { + if (state.phone.isNotEmpty) return; // لا نغيّر اختياراً واعياً للمستخدم + emit(state.copyWith(country: CountryCode.fromPack(countryPack))); + } + + void countryChanged(CountryCode country) => + emit(state.copyWith(country: country, clearPhoneError: true)); + void phoneChanged(String value) { emit(state.copyWith( phone: value, @@ -130,14 +133,14 @@ class LoginCubit extends Cubit { } Future submitPhone() async { - final error = validatePhone(state.phone); + final error = validatePhone(state.phone, state.country); if (error != null) { emit(state.copyWith(phoneError: error)); return; } emit(state.copyWith(status: LoginStatus.submitting, clearFailure: true)); try { - await _repo.sendOtp(state.phone.trim()); + await _repo.sendOtp(state.e164); emit(state.copyWith(status: LoginStatus.idle, step: LoginStep.otp)); _startResendCountdown(); } on ApiException catch (e) { @@ -164,7 +167,7 @@ class LoginCubit extends Cubit { if (!state.canResend) return; emit(state.copyWith(status: LoginStatus.submitting, clearFailure: true)); try { - await _repo.sendOtp(state.phone.trim()); + await _repo.sendOtp(state.e164); emit(state.copyWith(status: LoginStatus.idle)); _startResendCountdown(); } on ApiException catch (e) { @@ -182,7 +185,7 @@ class LoginCubit extends Cubit { if (code.length != AppConfig.otpLength) return; emit(state.copyWith(status: LoginStatus.submitting, clearFailure: true)); try { - final user = await _repo.verifyOtp(phone: state.phone.trim(), code: code); + final user = await _repo.verifyOtp(phone: state.e164, code: code); _resendTimer?.cancel(); emit(state.copyWith( status: LoginStatus.success, diff --git a/apps/rider_new/lib/features/auth/cubit/login_state.dart b/apps/rider_new/lib/features/auth/cubit/login_state.dart index c778d23..b599375 100644 --- a/apps/rider_new/lib/features/auth/cubit/login_state.dart +++ b/apps/rider_new/lib/features/auth/cubit/login_state.dart @@ -1,9 +1,8 @@ import 'package:equatable/equatable.dart'; import '../../../core/api/api_failure.dart'; - +import '../../../core/payments/country_codes.dart'; import '../../../core/session/app_user.dart'; - import 'phone_error.dart'; /// خطوات الدخول بالترتيب الميداني المثبت في سيرو (docs/39 §2): بوابتان @@ -17,6 +16,7 @@ class LoginState extends Equatable { this.step = LoginStep.agreement, this.status = LoginStatus.idle, this.phone = '', + this.country = CountryCode.jordan, this.agreed = false, this.failure, this.phoneError, @@ -31,6 +31,13 @@ class LoginState extends Equatable { /// الرقم كما أدخله المستخدم — الخادم يطبّعه، فلا نطبّعه نحن (docs/38 §2). final String phone; + /// الدولة المختارة — تحدّد المفتاح الدولي وطول التحقّق. تُبدأ من + /// `countryPack` المستأجر لا من لغة الجهاز. + final CountryCode country; + + /// ما يُرسل للخادم: المفتاح + الرقم بلا `+` (`962790000001`). + String get e164 => '${country.dial}${phone.trim()}'; + final bool agreed; final ApiFailure? failure; final PhoneError? phoneError; @@ -51,6 +58,7 @@ class LoginState extends Equatable { LoginStep? step, LoginStatus? status, String? phone, + CountryCode? country, bool? agreed, ApiFailure? failure, PhoneError? phoneError, @@ -64,6 +72,7 @@ class LoginState extends Equatable { step: step ?? this.step, status: status ?? this.status, phone: phone ?? this.phone, + country: country ?? this.country, agreed: agreed ?? this.agreed, failure: clearFailure ? null : (failure ?? this.failure), phoneError: clearPhoneError ? null : (phoneError ?? this.phoneError), @@ -79,6 +88,7 @@ class LoginState extends Equatable { step, status, phone, + country, agreed, failure, phoneError, diff --git a/apps/rider_new/lib/features/auth/cubit/phone_error.dart b/apps/rider_new/lib/features/auth/cubit/phone_error.dart index ad8ac94..70e754f 100644 --- a/apps/rider_new/lib/features/auth/cubit/phone_error.dart +++ b/apps/rider_new/lib/features/auth/cubit/phone_error.dart @@ -1,11 +1,15 @@ +import '../../../core/payments/country_codes.dart'; + /// أخطاء تحقّق **محليّة** لرقم الهاتف — تُكتشف قبل أي نداء شبكة. /// الشروط الثلاثة منقولة من التحقّق المطبَّق ميدانياً في سيرو (docs/39 §2). enum PhoneError { empty, leadingZero, tooShort } -PhoneError? validatePhone(String raw) { +/// الطول المتوقّع من **الدولة المختارة** لا رقماً ثابتاً: الأردن وسوريا 9 +/// خانات، ومصر والعراق 10. +PhoneError? validatePhone(String raw, CountryCode country) { final v = raw.trim(); if (v.isEmpty) return PhoneError.empty; if (v.startsWith('0')) return PhoneError.leadingZero; - if (v.length < 9) return PhoneError.tooShort; + if (v.length < country.nationalLength) return PhoneError.tooShort; return null; } diff --git a/apps/rider_new/lib/features/auth/data/auth_repository.dart b/apps/rider_new/lib/features/auth/data/auth_repository.dart index 3cc1ea3..fc7473d 100644 --- a/apps/rider_new/lib/features/auth/data/auth_repository.dart +++ b/apps/rider_new/lib/features/auth/data/auth_repository.dart @@ -1,9 +1,6 @@ import '../../../core/api/api_client.dart'; - import '../../../core/session/app_user.dart'; - import '../../../core/session/session_source.dart'; - import '../../../core/storage/token_store.dart'; /// كل نداءات المصادقة (docs/38 §2). الـCubit لا ينادي Dio مباشرة أبداً diff --git a/apps/rider_new/lib/features/auth/view/agreement_view.dart b/apps/rider_new/lib/features/auth/view/agreement_view.dart index 8a77f9c..6750178 100644 --- a/apps/rider_new/lib/features/auth/view/agreement_view.dart +++ b/apps/rider_new/lib/features/auth/view/agreement_view.dart @@ -1,22 +1,14 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/design/tokens.dart'; - import '../../../core/design/tripz_colors.dart'; - import '../../../core/l10n/l10n.dart'; - -import '../../../core/ui/tripz_button.dart'; - -import '../../../core/ui/tripz_scaffold.dart'; - -import '../cubit/login_cubit.dart'; - -import '../cubit/login_state.dart'; - import '../../../core/ui/form_header.dart'; +import '../../../core/ui/tripz_button.dart'; +import '../../../core/ui/tripz_scaffold.dart'; +import '../cubit/login_cubit.dart'; +import '../cubit/login_state.dart'; /// البوابة الأولى: شاشة كاملة لا حوار، وزر متابعة **معطّل حتى يُؤشَّر** /// (docs/39 §2). تُحفظ الموافقة فلا تتكرر. diff --git a/apps/rider_new/lib/features/auth/view/login_page.dart b/apps/rider_new/lib/features/auth/view/login_page.dart index 48dee03..64c4c38 100644 --- a/apps/rider_new/lib/features/auth/view/login_page.dart +++ b/apps/rider_new/lib/features/auth/view/login_page.dart @@ -1,21 +1,13 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/di.dart'; - import '../../../core/session/session_cubit.dart'; - import '../cubit/login_cubit.dart'; - import '../cubit/login_state.dart'; - import 'agreement_view.dart'; - import 'otp_view.dart'; - import 'permission_view.dart'; - import 'phone_view.dart'; /// مضيف تدفّق الدخول: خطوة واحدة معروضة في كل لحظة، والانتقال بينها من diff --git a/apps/rider_new/lib/features/auth/view/otp_view.dart b/apps/rider_new/lib/features/auth/view/otp_view.dart index 8a7c883..82a8cb4 100644 --- a/apps/rider_new/lib/features/auth/view/otp_view.dart +++ b/apps/rider_new/lib/features/auth/view/otp_view.dart @@ -1,26 +1,16 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/design/tokens.dart'; - import '../../../core/design/tripz_colors.dart'; - import '../../../core/l10n/l10n.dart'; - import '../../../core/ui/failure_text.dart'; - -import '../../../core/ui/tripz_button.dart'; - -import '../../../core/ui/tripz_otp_field.dart'; - -import '../../../core/ui/tripz_scaffold.dart'; - -import '../cubit/login_cubit.dart'; - -import '../cubit/login_state.dart'; - import '../../../core/ui/form_header.dart'; +import '../../../core/ui/tripz_button.dart'; +import '../../../core/ui/tripz_otp_field.dart'; +import '../../../core/ui/tripz_scaffold.dart'; +import '../cubit/login_cubit.dart'; +import '../cubit/login_state.dart'; class OtpView extends StatefulWidget { const OtpView({super.key}); diff --git a/apps/rider_new/lib/features/auth/view/permission_view.dart b/apps/rider_new/lib/features/auth/view/permission_view.dart index 449d77b..e086f51 100644 --- a/apps/rider_new/lib/features/auth/view/permission_view.dart +++ b/apps/rider_new/lib/features/auth/view/permission_view.dart @@ -1,22 +1,14 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/design/tokens.dart'; - import '../../../core/l10n/l10n.dart'; - -import '../../../core/ui/status_banner.dart'; - -import '../../../core/ui/tripz_button.dart'; - -import '../../../core/ui/tripz_scaffold.dart'; - -import '../cubit/login_cubit.dart'; - -import '../cubit/login_state.dart'; - import '../../../core/ui/form_header.dart'; +import '../../../core/ui/status_banner.dart'; +import '../../../core/ui/tripz_button.dart'; +import '../../../core/ui/tripz_scaffold.dart'; +import '../cubit/login_cubit.dart'; +import '../cubit/login_state.dart'; /// البوابة الثانية: إذن الموقع. عند الرفض النهائي لا يبقى إلا فتح إعدادات /// النظام — والعودة منها يلتقطها `LoginPage` بمراقبة دورة الحياة. diff --git a/apps/rider_new/lib/features/auth/view/phone_view.dart b/apps/rider_new/lib/features/auth/view/phone_view.dart index 93d294a..91106de 100644 --- a/apps/rider_new/lib/features/auth/view/phone_view.dart +++ b/apps/rider_new/lib/features/auth/view/phone_view.dart @@ -1,28 +1,48 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/design/tokens.dart'; - import '../../../core/l10n/l10n.dart'; - +import '../../../core/tenant/tenant_cubit.dart'; import '../../../core/ui/failure_text.dart'; - +import '../../../core/ui/form_header.dart'; +import '../../../core/ui/phone_field.dart'; import '../../../core/ui/tripz_button.dart'; - import '../../../core/ui/tripz_scaffold.dart'; - -import '../../../core/ui/tripz_text_field.dart'; - import '../cubit/login_cubit.dart'; - import '../cubit/login_state.dart'; -import '../../../core/ui/form_header.dart'; - -class PhoneView extends StatelessWidget { +class PhoneView extends StatefulWidget { const PhoneView({super.key}); + @override + State createState() => _PhoneViewState(); +} + +class _PhoneViewState extends State { + final _phone = TextEditingController(); + + @override + void initState() { + super.initState(); + _phone.addListener( + () => context.read().phoneChanged(_phone.text), + ); + // الدولة الافتراضية من المستأجر — يُطبَّق بعد أول إطار كي يتوفّر السياق. + WidgetsBinding.instance.addPostFrameCallback((_) { + if (!mounted) return; + context + .read() + .applyTenantCountry(context.read().state.countryPack); + }); + } + + @override + void dispose() { + _phone.dispose(); + super.dispose(); + } + @override Widget build(BuildContext context) { final l10n = context.l10n; @@ -30,6 +50,7 @@ class PhoneView extends StatelessWidget { buildWhen: (p, n) => p.status != n.status || p.phoneError != n.phoneError || + p.country != n.country || p.failure != n.failure, builder: (context, state) { final cubit = context.read(); @@ -49,11 +70,13 @@ class PhoneView extends StatelessWidget { lead: l10n.phoneLead, ), const SizedBox(height: Space.lg), - TripzTextField.phone( + PhoneField( + controller: _phone, + country: state.country, + onCountryChanged: cubit.countryChanged, label: l10n.phoneLabel, hint: l10n.phoneHint, enabled: !state.isSubmitting, - onChanged: cubit.phoneChanged, onSubmitted: (_) => cubit.submitPhone(), errorText: state.phoneError == null ? null diff --git a/apps/rider_new/lib/features/profile/cubit/profile_cubit.dart b/apps/rider_new/lib/features/profile/cubit/profile_cubit.dart index 3fdfef9..3736c18 100644 --- a/apps/rider_new/lib/features/profile/cubit/profile_cubit.dart +++ b/apps/rider_new/lib/features/profile/cubit/profile_cubit.dart @@ -1,13 +1,9 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/api/api_exception.dart'; - import '../../../core/api/api_failure.dart'; - import '../../../core/session/app_user.dart'; - import '../../../core/ui/view_status.dart'; - import '../data/profile_repository.dart'; class ProfileState { diff --git a/apps/rider_new/lib/features/profile/view/profile_edit_page.dart b/apps/rider_new/lib/features/profile/view/profile_edit_page.dart index 4fe1654..c14f0a4 100644 --- a/apps/rider_new/lib/features/profile/view/profile_edit_page.dart +++ b/apps/rider_new/lib/features/profile/view/profile_edit_page.dart @@ -1,23 +1,14 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/design/tokens.dart'; - import '../../../core/design/tripz_colors.dart'; - import '../../../core/di.dart'; - import '../../../core/l10n/l10n.dart'; - import '../../../core/session/session_cubit.dart'; - import '../../../core/ui/tripz_button.dart'; - import '../../../core/ui/tripz_scaffold.dart'; - import '../../../core/ui/tripz_text_field.dart'; - import '../cubit/profile_cubit.dart'; /// تعديل الاسم. **الرقم لا يُعدَّل**: هو هوية الحساب على الخادم، وتغييره diff --git a/apps/rider_new/lib/features/profile/view/profile_setup_page.dart b/apps/rider_new/lib/features/profile/view/profile_setup_page.dart index bcb34ab..fb381b6 100644 --- a/apps/rider_new/lib/features/profile/view/profile_setup_page.dart +++ b/apps/rider_new/lib/features/profile/view/profile_setup_page.dart @@ -1,25 +1,15 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/design/tokens.dart'; - import '../../../core/di.dart'; - import '../../../core/l10n/l10n.dart'; - import '../../../core/session/session_cubit.dart'; - import '../../../core/ui/failure_text.dart'; - -import '../../../core/ui/tripz_button.dart'; - -import '../../../core/ui/tripz_scaffold.dart'; - -import '../../../core/ui/tripz_text_field.dart'; - import '../../../core/ui/form_header.dart'; - +import '../../../core/ui/tripz_button.dart'; +import '../../../core/ui/tripz_scaffold.dart'; +import '../../../core/ui/tripz_text_field.dart'; import '../cubit/profile_cubit.dart'; class ProfileSetupPage extends StatefulWidget { diff --git a/apps/rider_new/lib/features/rating/cubit/rating_cubit.dart b/apps/rider_new/lib/features/rating/cubit/rating_cubit.dart index d0d7364..05c47e0 100644 --- a/apps/rider_new/lib/features/rating/cubit/rating_cubit.dart +++ b/apps/rider_new/lib/features/rating/cubit/rating_cubit.dart @@ -1,9 +1,7 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/api/api_exception.dart'; - import '../data/rating_repository.dart'; - import 'rating_state.dart'; class RatingCubit extends Cubit { diff --git a/apps/rider_new/lib/features/rating/view/rating_page.dart b/apps/rider_new/lib/features/rating/view/rating_page.dart index 9b11882..24074fc 100644 --- a/apps/rider_new/lib/features/rating/view/rating_page.dart +++ b/apps/rider_new/lib/features/rating/view/rating_page.dart @@ -1,27 +1,16 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/design/tokens.dart'; - import '../../../core/design/tripz_colors.dart'; - import '../../../core/di.dart'; - import '../../../core/l10n/l10n.dart'; - import '../../../core/ui/tripz_button.dart'; - import '../../../core/ui/tripz_scaffold.dart'; - import '../../../core/ui/tripz_text_field.dart'; - import '../cubit/rating_cubit.dart'; - import '../cubit/rating_state.dart'; - import '../data/rating_repository.dart'; - import 'star_row.dart'; /// شاشة التقييم — **تُفرض عند فتح التطبيق** إن كانت هناك رحلة معلّقة diff --git a/apps/rider_new/lib/features/rating/view/star_row.dart b/apps/rider_new/lib/features/rating/view/star_row.dart index f118b82..bd8f664 100644 --- a/apps/rider_new/lib/features/rating/view/star_row.dart +++ b/apps/rider_new/lib/features/rating/view/star_row.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import '../../../core/design/tokens.dart'; - import '../../../core/design/tripz_colors.dart'; /// خمس نجوم — أهداف لمس ≥ 48 لأن الشاشة تُستعمل عادةً والمستخدم مستعجل. diff --git a/apps/rider_new/lib/features/settings/cubit/settings_cubit.dart b/apps/rider_new/lib/features/settings/cubit/settings_cubit.dart index b020ca3..4875dff 100644 --- a/apps/rider_new/lib/features/settings/cubit/settings_cubit.dart +++ b/apps/rider_new/lib/features/settings/cubit/settings_cubit.dart @@ -1,7 +1,5 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; - import 'package:shared_preferences/shared_preferences.dart'; import 'settings_state.dart'; diff --git a/apps/rider_new/lib/features/settings/cubit/settings_state.dart b/apps/rider_new/lib/features/settings/cubit/settings_state.dart index a0b4049..f841895 100644 --- a/apps/rider_new/lib/features/settings/cubit/settings_state.dart +++ b/apps/rider_new/lib/features/settings/cubit/settings_state.dart @@ -1,5 +1,4 @@ import 'package:equatable/equatable.dart'; - import 'package:flutter/material.dart'; /// المظهر واللغة (docs/26 §4) — محفوظان محلياً، وليسا سرّاً فيذهبان إلى diff --git a/apps/rider_new/lib/features/settings/view/settings_page.dart b/apps/rider_new/lib/features/settings/view/settings_page.dart index ce7d4c5..8c313d8 100644 --- a/apps/rider_new/lib/features/settings/view/settings_page.dart +++ b/apps/rider_new/lib/features/settings/view/settings_page.dart @@ -1,17 +1,11 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/design/tokens.dart'; - import '../../../core/design/tripz_colors.dart'; - import '../../../core/l10n/l10n.dart'; - import '../../../core/ui/tripz_scaffold.dart'; - import '../cubit/settings_cubit.dart'; - import '../cubit/settings_state.dart'; /// المظهر واللغة. تبديل اللغة **يقلب الاتجاه كاملاً** بلا إعادة تشغيل — diff --git a/apps/rider_new/lib/features/splash/view/splash_page.dart b/apps/rider_new/lib/features/splash/view/splash_page.dart index 0a0f48b..c043869 100644 --- a/apps/rider_new/lib/features/splash/view/splash_page.dart +++ b/apps/rider_new/lib/features/splash/view/splash_page.dart @@ -1,19 +1,12 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/build_config.dart'; - import '../../../core/config.dart'; - import '../../../core/design/tokens.dart'; - import '../../../core/design/tripz_colors.dart'; - import '../../../core/l10n/l10n.dart'; - import '../../../core/session/session_cubit.dart'; - import '../../../core/tenant/tenant_cubit.dart'; /// شاشة الإقلاع: تستعيد الجلسة ثم يتولّى الراوتر التوجيه. لا تنقّل من هنا — diff --git a/apps/rider_new/lib/features/support/view/support_page.dart b/apps/rider_new/lib/features/support/view/support_page.dart index 55bc86f..e9bcf0a 100644 --- a/apps/rider_new/lib/features/support/view/support_page.dart +++ b/apps/rider_new/lib/features/support/view/support_page.dart @@ -1,15 +1,10 @@ import 'package:flutter/material.dart'; import '../../../core/build_config.dart'; - import '../../../core/design/tokens.dart'; - import '../../../core/design/tripz_colors.dart'; - import '../../../core/l10n/l10n.dart'; - import '../../../core/ui/tripz_card.dart'; - import '../../../core/ui/tripz_scaffold.dart'; /// المساعدة و«عن التطبيق». محتوى محلّي عمداً: الخادم لا يوفّر نقطة أسئلة diff --git a/apps/rider_new/lib/features/trip/cubit/history_cubit.dart b/apps/rider_new/lib/features/trip/cubit/history_cubit.dart index 1f7b35e..3901e20 100644 --- a/apps/rider_new/lib/features/trip/cubit/history_cubit.dart +++ b/apps/rider_new/lib/features/trip/cubit/history_cubit.dart @@ -1,11 +1,8 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/api/api_exception.dart'; - import '../../../core/ui/view_status.dart'; - import '../data/history_repository.dart'; - import '../data/models/trip.dart'; class HistoryState { 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 0de84f8..c6a4181 100644 --- a/apps/rider_new/lib/features/trip/cubit/ride_cubit.dart +++ b/apps/rider_new/lib/features/trip/cubit/ride_cubit.dart @@ -3,26 +3,16 @@ import 'dart:async'; import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/api/api_exception.dart'; - import '../../../core/config.dart'; - import '../../../core/location/location_service.dart'; - import '../../../core/realtime/realtime_service.dart'; - import '../data/maps_repository.dart'; - -import '../data/models/geo_point.dart'; - import '../data/models/fare_quote.dart'; +import '../data/models/geo_point.dart'; import '../data/models/place.dart'; - import '../data/models/ride_type.dart'; - import '../data/models/trip.dart'; - import '../data/trip_repository.dart'; - import 'ride_state.dart'; /// شاشة الراكب كاملة: التخطيط ← التسعيرة ← الطلب ← البحث ← الرحلة الجارية. diff --git a/apps/rider_new/lib/features/trip/cubit/ride_state.dart b/apps/rider_new/lib/features/trip/cubit/ride_state.dart index 9077b81..d121263 100644 --- a/apps/rider_new/lib/features/trip/cubit/ride_state.dart +++ b/apps/rider_new/lib/features/trip/cubit/ride_state.dart @@ -1,15 +1,10 @@ import 'package:equatable/equatable.dart'; import '../data/models/fare_quote.dart'; - import '../data/models/geo_point.dart'; - import '../data/models/place.dart'; - import '../data/models/ride_type.dart'; - import '../data/models/route_info.dart'; - import '../data/models/trip.dart'; /// مراحل شاشة الراكب. المرحلة تحدّد ما يُعرض على الخريطة وما في الورقة diff --git a/apps/rider_new/lib/features/trip/data/history_repository.dart b/apps/rider_new/lib/features/trip/data/history_repository.dart index b57ccb6..7667b08 100644 --- a/apps/rider_new/lib/features/trip/data/history_repository.dart +++ b/apps/rider_new/lib/features/trip/data/history_repository.dart @@ -1,5 +1,4 @@ import '../../../core/api/api_client.dart'; - import 'models/trip.dart'; /// سجلّ الرحلات. لا يستورد ميزة الرحلة كمنطق — يعيد استعمال نموذجها فقط، diff --git a/apps/rider_new/lib/features/trip/data/maps_repository.dart b/apps/rider_new/lib/features/trip/data/maps_repository.dart index 292cc26..f3dff78 100644 --- a/apps/rider_new/lib/features/trip/data/maps_repository.dart +++ b/apps/rider_new/lib/features/trip/data/maps_repository.dart @@ -1,9 +1,6 @@ import '../../../core/api/antlaq_api.dart'; - import 'models/geo_point.dart'; - import 'models/place.dart'; - import 'models/route_info.dart'; /// كل الخرائط من map-saas مباشرة (قرار 2026-07-20). نقاط `/maps/*` في باك إند diff --git a/apps/rider_new/lib/features/trip/data/trip_repository.dart b/apps/rider_new/lib/features/trip/data/trip_repository.dart index f82d925..9b1afe9 100644 --- a/apps/rider_new/lib/features/trip/data/trip_repository.dart +++ b/apps/rider_new/lib/features/trip/data/trip_repository.dart @@ -1,11 +1,7 @@ import '../../../core/api/api_client.dart'; - import 'models/fare_quote.dart'; - import 'models/geo_point.dart'; - import 'models/ride_type.dart'; - import 'models/trip.dart'; class TripRepository { diff --git a/apps/rider_new/lib/features/trip/view/history_page.dart b/apps/rider_new/lib/features/trip/view/history_page.dart index a62070b..856e7d3 100644 --- a/apps/rider_new/lib/features/trip/view/history_page.dart +++ b/apps/rider_new/lib/features/trip/view/history_page.dart @@ -1,27 +1,16 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/design/tokens.dart'; - import '../../../core/design/tripz_colors.dart'; - import '../../../core/design/typography.dart'; - import '../../../core/di.dart'; - import '../../../core/format/money.dart'; - import '../../../core/l10n/l10n.dart'; - import '../../../core/ui/empty_view.dart'; - import '../../../core/ui/tripz_card.dart'; - import '../../../core/ui/tripz_scaffold.dart'; - import '../cubit/history_cubit.dart'; - import '../data/models/trip.dart'; class HistoryPage extends StatefulWidget { diff --git a/apps/rider_new/lib/features/trip/view/ride_page.dart b/apps/rider_new/lib/features/trip/view/ride_page.dart index 5d6cba1..3d642df 100644 --- a/apps/rider_new/lib/features/trip/view/ride_page.dart +++ b/apps/rider_new/lib/features/trip/view/ride_page.dart @@ -1,36 +1,21 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/design/tokens.dart'; - import '../../../core/design/tripz_colors.dart'; - import '../../../core/di.dart'; - import '../../../core/l10n/l10n.dart'; - import '../../../core/ui/app_drawer.dart'; - import '../../../core/ui/status_banner.dart'; import '../../../core/ui/tripz_button.dart'; - import '../cubit/ride_cubit.dart'; - import '../cubit/ride_state.dart'; - import '../data/models/geo_point.dart'; - import 'widgets/active_trip_sheet.dart'; - import 'widgets/center_pin.dart'; - import 'widgets/confirm_sheet.dart'; - import 'widgets/planner_sheet.dart'; - import 'widgets/ride_map.dart'; - import 'widgets/searching_sheet.dart'; /// شاشة الراكب: خريطة ثابتة + ورقة سفلية تتبدّل بالمرحلة (docs/39 §3). diff --git a/apps/rider_new/lib/features/trip/view/widgets/active_trip_sheet.dart b/apps/rider_new/lib/features/trip/view/widgets/active_trip_sheet.dart index 662d96c..9c0ecae 100644 --- a/apps/rider_new/lib/features/trip/view/widgets/active_trip_sheet.dart +++ b/apps/rider_new/lib/features/trip/view/widgets/active_trip_sheet.dart @@ -1,27 +1,18 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:go_router/go_router.dart'; -import '../../../../core/design/tokens.dart'; - -import '../../../../core/design/tripz_colors.dart'; - import '../../../../core/build_config.dart'; +import '../../../../core/design/tokens.dart'; +import '../../../../core/design/tripz_colors.dart'; import '../../../../core/l10n/l10n.dart'; import '../../../../core/router.dart'; import '../../../../core/tenant/feature_gate.dart'; - import '../../../../core/ui/tripz_button.dart'; - import '../../cubit/ride_cubit.dart'; - import '../../data/models/trip.dart'; - import 'cancel_sheet.dart'; - import 'driver_badge.dart'; - import 'fare_summary.dart'; /// بطاقة الرحلة الجارية: حالة واضحة ثم بيانات السائق ثم فعل واحد. diff --git a/apps/rider_new/lib/features/trip/view/widgets/cancel_sheet.dart b/apps/rider_new/lib/features/trip/view/widgets/cancel_sheet.dart index 184b6b2..2b70745 100644 --- a/apps/rider_new/lib/features/trip/view/widgets/cancel_sheet.dart +++ b/apps/rider_new/lib/features/trip/view/widgets/cancel_sheet.dart @@ -1,11 +1,8 @@ import 'package:flutter/material.dart'; import '../../../../core/design/tokens.dart'; - import '../../../../core/l10n/l10n.dart'; - import '../../../../core/ui/tripz_button.dart'; - import '../../../../core/ui/tripz_sheet.dart'; /// الإلغاء لا يقع بضغطة واحدة: تأكيدٌ صريح مع سبب. diff --git a/apps/rider_new/lib/features/trip/view/widgets/center_pin.dart b/apps/rider_new/lib/features/trip/view/widgets/center_pin.dart index 065871c..a047591 100644 --- a/apps/rider_new/lib/features/trip/view/widgets/center_pin.dart +++ b/apps/rider_new/lib/features/trip/view/widgets/center_pin.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import '../../../../core/design/tokens.dart'; - import '../../../../core/design/tripz_colors.dart'; /// دبّوس ثابت في منتصف الشاشة أثناء الاختيار من الخريطة. diff --git a/apps/rider_new/lib/features/trip/view/widgets/confirm_sheet.dart b/apps/rider_new/lib/features/trip/view/widgets/confirm_sheet.dart index cabfd55..0e0b2e1 100644 --- a/apps/rider_new/lib/features/trip/view/widgets/confirm_sheet.dart +++ b/apps/rider_new/lib/features/trip/view/widgets/confirm_sheet.dart @@ -1,28 +1,18 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; -import '../../../../core/design/tokens.dart'; - -import '../../../../core/design/tripz_colors.dart'; - -import '../../../../core/design/typography.dart'; - -import '../../../../core/l10n/l10n.dart'; - -import '../../../../core/ui/status_banner.dart'; - import '../../../../core/build_config.dart'; +import '../../../../core/design/tokens.dart'; +import '../../../../core/design/tripz_colors.dart'; +import '../../../../core/design/typography.dart'; +import '../../../../core/l10n/l10n.dart'; import '../../../../core/tenant/feature_gate.dart'; +import '../../../../core/ui/status_banner.dart'; import '../../../../core/ui/tripz_button.dart'; import '../../../../core/ui/tripz_text_field.dart'; - import '../../cubit/ride_cubit.dart'; - import '../../cubit/ride_state.dart'; - import 'ride_type_picker.dart'; - import 'route_timeline.dart'; /// التأكيد: نوع الرحلة والأجرة والمسافة والمدة — ثم زر واحد. diff --git a/apps/rider_new/lib/features/trip/view/widgets/driver_badge.dart b/apps/rider_new/lib/features/trip/view/widgets/driver_badge.dart index d4493cd..cabb7ac 100644 --- a/apps/rider_new/lib/features/trip/view/widgets/driver_badge.dart +++ b/apps/rider_new/lib/features/trip/view/widgets/driver_badge.dart @@ -1,11 +1,8 @@ import 'package:flutter/material.dart'; import '../../../../core/design/tokens.dart'; - import '../../../../core/design/tripz_colors.dart'; - import '../../../../core/design/typography.dart'; - import '../../data/models/trip.dart'; /// بطاقة السائق ولوحة المركبة — أهم ما يبحث عنه الراكب في الشارع، فيأخذ diff --git a/apps/rider_new/lib/features/trip/view/widgets/fare_summary.dart b/apps/rider_new/lib/features/trip/view/widgets/fare_summary.dart index 70110ba..baf4f83 100644 --- a/apps/rider_new/lib/features/trip/view/widgets/fare_summary.dart +++ b/apps/rider_new/lib/features/trip/view/widgets/fare_summary.dart @@ -1,15 +1,10 @@ import 'package:flutter/material.dart'; 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/trip.dart'; /// ملخّص الأجرة عند الإنهاء. diff --git a/apps/rider_new/lib/features/trip/view/widgets/planner_sheet.dart b/apps/rider_new/lib/features/trip/view/widgets/planner_sheet.dart index 4a9335a..a72fe32 100644 --- a/apps/rider_new/lib/features/trip/view/widgets/planner_sheet.dart +++ b/apps/rider_new/lib/features/trip/view/widgets/planner_sheet.dart @@ -1,19 +1,12 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../../core/design/tokens.dart'; - import '../../../../core/l10n/l10n.dart'; - import '../../../../core/ui/tripz_button.dart'; - import '../../../../core/ui/tripz_text_field.dart'; - import '../../cubit/ride_cubit.dart'; - import '../../cubit/ride_state.dart'; - import 'route_timeline.dart'; /// المخطّط الموسّع بترتيبه المثبت ميدانياً (docs/39 §3): diff --git a/apps/rider_new/lib/features/trip/view/widgets/route_timeline.dart b/apps/rider_new/lib/features/trip/view/widgets/route_timeline.dart index 07e87fd..e2c0894 100644 --- a/apps/rider_new/lib/features/trip/view/widgets/route_timeline.dart +++ b/apps/rider_new/lib/features/trip/view/widgets/route_timeline.dart @@ -1,9 +1,7 @@ import 'package:flutter/material.dart'; import '../../../../core/design/tokens.dart'; - import '../../../../core/design/tripz_colors.dart'; - import '../../../../core/l10n/l10n.dart'; /// الخط الزمني للمسار: المصدر ← المحطات ← الوجهة، بنقاط ملوّنة تطابق diff --git a/apps/rider_new/lib/features/trip/view/widgets/searching_sheet.dart b/apps/rider_new/lib/features/trip/view/widgets/searching_sheet.dart index 44a32bb..d8e2830 100644 --- a/apps/rider_new/lib/features/trip/view/widgets/searching_sheet.dart +++ b/apps/rider_new/lib/features/trip/view/widgets/searching_sheet.dart @@ -1,15 +1,10 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../../core/design/tokens.dart'; - import '../../../../core/design/tripz_colors.dart'; - import '../../../../core/l10n/l10n.dart'; - import '../../../../core/ui/tripz_button.dart'; - import '../../cubit/ride_cubit.dart'; /// نافذة البحث عن سائق. diff --git a/apps/rider_new/lib/features/wallet/cubit/wallet_cubit.dart b/apps/rider_new/lib/features/wallet/cubit/wallet_cubit.dart index 2e23664..b8f743a 100644 --- a/apps/rider_new/lib/features/wallet/cubit/wallet_cubit.dart +++ b/apps/rider_new/lib/features/wallet/cubit/wallet_cubit.dart @@ -1,11 +1,8 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/api/api_exception.dart'; - import '../../../core/ui/view_status.dart'; - import '../data/wallet_repository.dart'; - import 'wallet_state.dart'; class WalletCubit extends Cubit { diff --git a/apps/rider_new/lib/features/wallet/cubit/wallet_state.dart b/apps/rider_new/lib/features/wallet/cubit/wallet_state.dart index 36f6287..5ddedc6 100644 --- a/apps/rider_new/lib/features/wallet/cubit/wallet_state.dart +++ b/apps/rider_new/lib/features/wallet/cubit/wallet_state.dart @@ -1,9 +1,7 @@ import 'package:equatable/equatable.dart'; import '../../../core/ui/view_status.dart'; - import '../data/models/wallet.dart'; - import '../data/models/wallet_txn.dart'; enum WalletAction { none, toppingUp, topupFailed, topupDone } diff --git a/apps/rider_new/lib/features/wallet/data/wallet_repository.dart b/apps/rider_new/lib/features/wallet/data/wallet_repository.dart index 31972d0..2f7a121 100644 --- a/apps/rider_new/lib/features/wallet/data/wallet_repository.dart +++ b/apps/rider_new/lib/features/wallet/data/wallet_repository.dart @@ -1,7 +1,5 @@ import '../../../core/api/api_client.dart'; - import 'models/wallet.dart'; - import 'models/wallet_txn.dart'; class WalletRepository { diff --git a/apps/rider_new/lib/features/wallet/view/wallet_page.dart b/apps/rider_new/lib/features/wallet/view/wallet_page.dart index 4202bfa..9e30801 100644 --- a/apps/rider_new/lib/features/wallet/view/wallet_page.dart +++ b/apps/rider_new/lib/features/wallet/view/wallet_page.dart @@ -1,35 +1,22 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import '../../../core/design/tokens.dart'; - import '../../../core/design/tripz_colors.dart'; +import '../../../core/di.dart'; +import '../../../core/l10n/l10n.dart'; import '../../../core/payments/payment_channels.dart'; import '../../../core/tenant/tenant_cubit.dart'; import '../../../core/ui/channel_picker.dart'; -import '../../../core/ui/status_banner.dart'; - -import '../../../core/di.dart'; - -import '../../../core/l10n/l10n.dart'; - import '../../../core/ui/empty_view.dart'; - +import '../../../core/ui/status_banner.dart'; import '../../../core/ui/tripz_button.dart'; - import '../../../core/ui/tripz_scaffold.dart'; - import '../../../core/ui/tripz_sheet.dart'; - import '../../../core/ui/tripz_text_field.dart'; - import '../cubit/wallet_cubit.dart'; - import '../cubit/wallet_state.dart'; - import 'widgets/balance_card.dart'; - import 'widgets/txn_tile.dart'; /// محفظة الراكب — **رصيدٌ دفعه ولم يستهلكه**، أي التزامٌ على المنصّة. diff --git a/apps/rider_new/lib/features/wallet/view/widgets/balance_card.dart b/apps/rider_new/lib/features/wallet/view/widgets/balance_card.dart index 06ce746..469815a 100644 --- a/apps/rider_new/lib/features/wallet/view/widgets/balance_card.dart +++ b/apps/rider_new/lib/features/wallet/view/widgets/balance_card.dart @@ -1,11 +1,8 @@ import 'package:flutter/material.dart'; import '../../../../core/design/tokens.dart'; - import '../../../../core/design/tripz_colors.dart'; - import '../../../../core/design/typography.dart'; - import '../../../../core/format/money.dart'; /// بطاقة الرصيد — الرقم هو بطل الشاشة، فيأخذ أكبر حجم وأعلى تباين. diff --git a/apps/rider_new/lib/features/wallet/view/widgets/txn_tile.dart b/apps/rider_new/lib/features/wallet/view/widgets/txn_tile.dart index 3e06a1e..dacd33f 100644 --- a/apps/rider_new/lib/features/wallet/view/widgets/txn_tile.dart +++ b/apps/rider_new/lib/features/wallet/view/widgets/txn_tile.dart @@ -1,13 +1,9 @@ import 'package:flutter/material.dart'; import '../../../../core/design/tokens.dart'; - import '../../../../core/design/tripz_colors.dart'; - import '../../../../core/design/typography.dart'; - import '../../../../core/format/money.dart'; - import '../../data/models/wallet_txn.dart'; class TxnTile extends StatelessWidget { diff --git a/apps/rider_new/lib/main.dart b/apps/rider_new/lib/main.dart index 042ab85..955cded 100644 --- a/apps/rider_new/lib/main.dart +++ b/apps/rider_new/lib/main.dart @@ -1,11 +1,8 @@ import 'package:flutter/material.dart'; - import 'package:flutter_bloc/flutter_bloc.dart'; import 'app.dart'; - import 'core/di.dart'; - import 'core/session/session_cubit.dart'; Future main() async {