refactor: disable Mawasalati feature, wrap controller initialization in post-frame callbacks, and add documentation plan
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
# خطة مواصلاتي (Siro Transit) - ما تم إنجازه وما لم يتم
|
||||
|
||||
تُوثق هذه الوثيقة حالة ميزة "مواصلاتي" (النقل المؤسسي للجامعات والفنادق) في منصة سيرو، وتفصل ما تم بناءه حتى الآن وما يتبقى إكماله قبل الإطلاق النهائي.
|
||||
|
||||
## ✅ أولاً: ما تم إنجازه
|
||||
|
||||
### 1. البنية التحتية وقواعد البيانات (Backend)
|
||||
- تصميم قواعد البيانات وإضافة الجداول اللازمة (مؤسسات، خطوط، محطات، جداول زمنية، رحلات، واشتراكات الركاب).
|
||||
- بناء مسارات الـ API بلغة PHP لخدمة الراكب والسائق (جلب المؤسسات، المسارات، المحطات، الرحلات اليومية).
|
||||
|
||||
### 2. تطبيق الراكب (Rider App)
|
||||
- شاشة **تصفح المؤسسات** (`TransitOrgBrowsePage`) لعرض الجامعات والفنادق المتاحة.
|
||||
- شاشة **مسارات المؤسسة** (`TransitRoutesPage`) لعرض الخطوط والمحطات على الخريطة.
|
||||
- شاشة **الصفحة الرئيسية للمواصلاتي** (`TransitHomePage`) لعرض اشتراكات الراكب الحالية.
|
||||
- شاشة **التتبع الحي** (`TransitLiveMapPage`) المهيأة مبدئياً لتتبع الباص.
|
||||
- إصلاح مشاكل الـ UI (ParentDataWidget و initState).
|
||||
|
||||
### 3. تطبيق السائق (Driver App)
|
||||
- شاشة **الرحلات المجدولة** (`TransitDriverHomePage`) لعرض الرحلات المطلوبة من السائق اليوم، مع أزرار "بدء الرحلة" و"إنهاء الرحلة".
|
||||
- تحسين خريطة السائق الأساسية (`home_captin.dart`) وتفعيل ماركر السيارة الصحيح (`car_icon`) ليكون جاهزاً لبث الموقع الفعلي أثناء الرحلة، وإصلاح مشاكل الـ UI (Overflow).
|
||||
|
||||
---
|
||||
|
||||
## ⏳ ثانياً: ما لم يتم إنجازه (الخطوات القادمة والأولويات)
|
||||
|
||||
> [!IMPORTANT]
|
||||
> تم تعليق (إخفاء) أزرار الوصول لخدمة "مواصلاتي" مؤقتاً من تطبيقي الراكب والسائق حتى يتم الانتهاء من هذه الخطوات واختبارها فعلياً على سيرفرات الإنتاج.
|
||||
|
||||
### أولوية 1: الاختبار الميداني الحقيقي
|
||||
- **بث واستقبال الموقع الحي (WebSockets):** تشغيل واختبار `driver_socket.php` و `passenger_socket.php` على سيرفر الإنتاج الفعلي.
|
||||
- التأكد من إعدادات الـ Redis (`REDIS_LOCATION_HOST`) في الـ `.env`.
|
||||
- التأكد من أن "ماركر" الباص يتحرك بسلاسة على خريطة الراكب عندما يبدأ السائق الرحلة.
|
||||
|
||||
### أولوية 2: دورة حياة الرحلة للسائق والتذاكر
|
||||
- **وضع التوجيه للسائق (Active Trip Navigation):** شاشة تظهر للسائق عند الضغط على "بدء الرحلة" لترشده للمحطات بالترتيب وتتبع تقدمه.
|
||||
- **الاشتراكات والتذاكر (Ticketing):** شراء الراكب للاشتراك وخصم الرصيد من المحفظة.
|
||||
- **مسح كود الصعود (QR Boarding):** آلية لكي يثبت الراكب صعوده للباص (إما السائق يمسح كود الراكب، أو يعلّم عليه في قائمة الركاب).
|
||||
|
||||
### أولوية 3: لوحة تحكم المؤسسات والتنبيهات
|
||||
- **واجهة الويب للمؤسسات (B2B Admin Dashboard):** واجهة ويب تتيح لمدير الحركة في الجامعة إضافة المسارات، الباصات، وتتبع الأسطول.
|
||||
- **استيراد كشف الركاب:** واجهة لرفع ملفات CSV (`import_roster.php`) لتعريف طلاب الجامعة المسموح لهم بالركوب.
|
||||
- **التنبيهات الجغرافية (Geofence Push Notifications):** تفعيل إشعارات FCM ("الباص على بُعد 5 دقائق من محطتك").
|
||||
- **إدارة العقود:** زر في تطبيق `siro_admin` لتفعيل/تعليق عقود المؤسسات (`contract_status`).
|
||||
|
||||
---
|
||||
سيتم الرجوع لهذه الخطة واستئناف العمل عليها في المرحلة القادمة بعد الانتهاء من الأولويات الحالية للنظام.
|
||||
@@ -28,7 +28,9 @@ class _TransitOrgAdminsPageState extends State<TransitOrgAdminsPage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
Get.find<TransitAdminController>().fetchOrgAdmins(widget.orgId);
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
Get.find<TransitAdminController>().fetchOrgAdmins(widget.orgId);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -19,7 +19,9 @@ class _TransitOrgDetailsPageState extends State<TransitOrgDetailsPage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
Get.find<TransitAdminController>().loadOrgDetails(widget.orgId);
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
Get.find<TransitAdminController>().loadOrgDetails(widget.orgId);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
|
||||
@@ -56,11 +56,11 @@ class AppDrawer extends StatelessWidget {
|
||||
|
||||
// 2. تعريف بيانات القائمة بشكل مركزي ومنظم
|
||||
final List<DrawerItem> drawerItems = [
|
||||
DrawerItem(
|
||||
title: 'Mawasalati'.tr,
|
||||
icon: Icons.directions_bus_filled_rounded,
|
||||
color: Colors.teal,
|
||||
onTap: () => Get.to(() => const TransitDriverHomePage())),
|
||||
// DrawerItem(
|
||||
// title: 'Mawasalati'.tr,
|
||||
// icon: Icons.directions_bus_filled_rounded,
|
||||
// color: Colors.teal,
|
||||
// onTap: () => Get.to(() => const TransitDriverHomePage())),
|
||||
DrawerItem(
|
||||
title: 'Balance'.tr,
|
||||
icon: Icons.account_balance_wallet,
|
||||
|
||||
@@ -149,11 +149,11 @@ class MapMenuWidget extends StatelessWidget {
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 12, vertical: 4),
|
||||
children: [
|
||||
MenuListItem(
|
||||
title: 'Mawasalati'.tr,
|
||||
icon: Icons.directions_bus_filled_rounded,
|
||||
onTap: () => Get.to(() => const TransitHomePage()),
|
||||
),
|
||||
// MenuListItem(
|
||||
// title: 'Mawasalati'.tr,
|
||||
// icon: Icons.directions_bus_filled_rounded,
|
||||
// onTap: () => Get.to(() => const TransitHomePage()),
|
||||
// ),
|
||||
MenuListItem(
|
||||
title: 'My Balance'.tr,
|
||||
icon: Icons.account_balance_wallet_outlined,
|
||||
|
||||
@@ -24,7 +24,9 @@ class _TransitLiveMapPageState extends State<TransitLiveMapPage> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
Get.find<TransitController>().openLiveRoute(widget.routeId);
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
Get.find<TransitController>().openLiveRoute(widget.routeId);
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
@@ -78,52 +80,56 @@ class _TransitLiveMapPageState extends State<TransitLiveMapPage> {
|
||||
title: widget.routeName,
|
||||
isleading: true,
|
||||
body: [
|
||||
Expanded(
|
||||
child: Stack(
|
||||
children: [
|
||||
IntaleqMap(
|
||||
apiKey: Env.mapSaasKey,
|
||||
initialCameraPosition: CameraPosition(target: initialTarget, zoom: 14),
|
||||
markers: markers,
|
||||
onMapCreated: (ctrl) => _mapController = ctrl,
|
||||
Column(
|
||||
children: [
|
||||
Expanded(
|
||||
child: Stack(
|
||||
children: [
|
||||
IntaleqMap(
|
||||
apiKey: Env.mapSaasKey,
|
||||
initialCameraPosition: CameraPosition(target: initialTarget, zoom: 14),
|
||||
markers: markers,
|
||||
onMapCreated: (ctrl) => _mapController = ctrl,
|
||||
),
|
||||
if (c.isLoadingLiveTrip)
|
||||
const Positioned.fill(
|
||||
child: ColoredBox(
|
||||
color: Colors.black12,
|
||||
child: Center(child: CircularProgressIndicator()),
|
||||
),
|
||||
),
|
||||
if (!c.isLoadingLiveTrip && c.liveError.isNotEmpty)
|
||||
Positioned(
|
||||
top: 16,
|
||||
left: 16,
|
||||
right: 16,
|
||||
child: _statusBanner(c.liveError, AppColor.redColor),
|
||||
),
|
||||
if (!c.isLoadingLiveTrip &&
|
||||
c.liveError.isEmpty &&
|
||||
c.liveBusPosition == null)
|
||||
Positioned(
|
||||
top: 16,
|
||||
left: 16,
|
||||
right: 16,
|
||||
child: _statusBanner(
|
||||
'No bus running on this route right now'.tr, AppColor.yellowColor),
|
||||
),
|
||||
if (c.liveTripData?['trip']?['delay_minutes'] != null &&
|
||||
(int.tryParse(c.liveTripData!['trip']['delay_minutes'].toString()) ?? 0) > 0)
|
||||
Positioned(
|
||||
bottom: 24,
|
||||
left: 16,
|
||||
right: 16,
|
||||
child: _statusBanner(
|
||||
'${'Bus is delayed'.tr} ${c.liveTripData!['trip']['delay_minutes']} ${'min'.tr}',
|
||||
AppColor.yellowColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (c.isLoadingLiveTrip)
|
||||
const Positioned.fill(
|
||||
child: ColoredBox(
|
||||
color: Colors.black12,
|
||||
child: Center(child: CircularProgressIndicator()),
|
||||
),
|
||||
),
|
||||
if (!c.isLoadingLiveTrip && c.liveError.isNotEmpty)
|
||||
Positioned(
|
||||
top: 16,
|
||||
left: 16,
|
||||
right: 16,
|
||||
child: _statusBanner(c.liveError, AppColor.redColor),
|
||||
),
|
||||
if (!c.isLoadingLiveTrip &&
|
||||
c.liveError.isEmpty &&
|
||||
c.liveBusPosition == null)
|
||||
Positioned(
|
||||
top: 16,
|
||||
left: 16,
|
||||
right: 16,
|
||||
child: _statusBanner(
|
||||
'No bus running on this route right now'.tr, AppColor.yellowColor),
|
||||
),
|
||||
if (c.liveTripData?['trip']?['delay_minutes'] != null &&
|
||||
(int.tryParse(c.liveTripData!['trip']['delay_minutes'].toString()) ?? 0) > 0)
|
||||
Positioned(
|
||||
bottom: 24,
|
||||
left: 16,
|
||||
right: 16,
|
||||
child: _statusBanner(
|
||||
'${'Bus is delayed'.tr} ${c.liveTripData!['trip']['delay_minutes']} ${'min'.tr}',
|
||||
AppColor.yellowColor,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user