Files
tripz-llc/apps/driver/lib/controller/transit/transit_driver_controller.dart
T
Hamza-AyedandClaude Opus 5 4d8414c96b feat: استيراد كود سيرو إلى تريبز (سيرو @ecfe7568) — بلا تعديل
قرار المالك 2026-07-27: باك إند سيرو PHP هو المعتمد، وتطبيقاته المجرّبة
ميدانياً تحل محل إعادة البناء المؤرشفة. سيرو نفسه لم يُمسّ.

الخريطة:
  backend · payment_server · loction_server · ride_server ·
  passenger_server · docker · dashboard · stress_test  → الجذر
  siro_rider  → apps/rider          siro_driver  → apps/driver
  siro_admin  → dashboards/admin    siro_service → dashboards/service
  android_bot → apps/android_bot    socialBot    → apps/socialBot

نُسخ المتعقَّب في git سيرو فقط عبر `git archive` (3,198 ملفاً / ~169 م.ب)
لا `cp -r` — فاستُثنيت مخلفات البناء تلقائياً. بلا أي تعديل محتوى عمداً:
كل ما يلي يصير فرقاً مقروءاً مقابل المصدر.

لم يُستورد وسببه: siromove.com (الموقع التسويقي يبقى marketing/ في تريبز،
سيرو فيه 8 ملفات) · docs و planning (تريبز له docs/ الخاص) · deploy.sh
(ليس نشراً على سيرفر بل `git add . && git push origin --all` — فخّ في
مستودع آخر) · transit_dashboard (بانتظار قرار مصير backend-transit و
dashboards/transit-web).

⚠️ لا يبني بعد — ثلاثة نواقص متوقعة ومقصودة:
1. `.env` و `lib/env/env.g.dart` غير متعقَّبين في سيرو (أسرار لكل مستأجر):
   كل تطبيق فلاتر يحتاج .env خاصاً ثم توليد env.g.dart بـ build_runner.
2. إعدادات Firebase (9 ملفات google-services.json و GoogleService-Info.plist)
   يستبعدها .gitignore تريبز — ولكل مستأجر مشروع Firebase خاص أصلاً.
3. apps/driver في سيرو يشير إلى `../../Intaleq/packages/get` خارج المستودع →
   يجب ضمّ الحزم داخله أسوة بـ apps/rider.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-27 05:14:13 +03:00

221 lines
6.5 KiB
Dart

// transit_driver_controller.dart — تحكم وضع الباص (جهة السائق)
import 'package:geolocator/geolocator.dart';
import 'package:get/get.dart';
import 'package:intaleq_maps/intaleq_maps.dart' show LatLng;
import '../../views/widgets/error_snakbar.dart';
import '../functions/location_controller.dart';
import 'transit_driver_models.dart';
import 'transit_driver_service.dart';
class TransitDriverController extends GetxController {
bool isCheckingBusDriver = true;
bool isBusDriver = false;
int? driverTransitId;
String orgName = '';
bool isLoadingTrips = false;
List<TransitDriverTrip> todayTrips = [];
TransitDriverTrip? activeTrip;
bool isActionInProgress = false;
// ── حالة التفعيل ──────────────────────────────────────────────
bool isActivating = false;
String activationError = '';
// ── جيوفينس المحطات — آخر محطة تم الإعلان عنها ──────────────
int _lastAnnouncedStopSeq = 0;
@override
void onInit() {
super.onInit();
checkBusDriverStatus();
}
Future<void> checkBusDriverStatus() async {
isCheckingBusDriver = true;
update();
final res = await TransitDriverService.checkIsBusDriver();
if (res.success && res.data != null) {
isBusDriver = res.data!['is_bus_driver'] == true;
if (isBusDriver) {
driverTransitId =
int.tryParse(res.data!['driver_transit_id'].toString());
orgName = res.data!['org_name']?.toString() ?? '';
await fetchTodayTrips();
}
}
isCheckingBusDriver = false;
update();
}
/// تفعيل الحساب بتوكن الدعوة
Future<void> activateWithToken(String token) async {
if (token.trim().isEmpty) return;
isActivating = true;
activationError = '';
update();
final res = await TransitDriverService.activateByInviteToken(token.trim());
if (res.success) {
// أعد فحص الحالة بعد التفعيل
await checkBusDriverStatus();
} else {
activationError = res.message;
}
isActivating = false;
update();
}
Future<void> fetchTodayTrips() async {
isLoadingTrips = true;
update();
final res = await TransitDriverService.getTodayTrips();
if (res.success) {
todayTrips = res.data ?? [];
final started = todayTrips.where((t) => t.status == 'started');
activeTrip = started.isNotEmpty ? started.first : null;
// إن كانت هناك رحلة قيد التشغيل بالفعل (إعادة فتح التطبيق)
if (activeTrip != null && Get.isRegistered<LocationController>()) {
Get.find<LocationController>().setBusMode(
enabled: true,
tripId: activeTrip!.id,
routeId: activeTrip!.routeId,
);
}
}
isLoadingTrips = false;
update();
}
Future<bool> startTrip(TransitDriverTrip trip) async {
if (isActionInProgress) return false;
isActionInProgress = true;
update();
LatLng pos = const LatLng(0, 0);
if (Get.isRegistered<LocationController>()) {
pos = Get.find<LocationController>().myLocation;
}
final res = await TransitDriverService.startTrip(
tripId: trip.id,
lat: pos.latitude,
lng: pos.longitude,
);
if (res.success) {
_lastAnnouncedStopSeq = 0;
if (Get.isRegistered<LocationController>()) {
Get.find<LocationController>().setBusMode(
enabled: true,
tripId: trip.id,
routeId: trip.routeId,
);
}
await fetchTodayTrips();
} else {
mySnackbarError(res.message);
}
isActionInProgress = false;
update();
return res.success;
}
Future<bool> endTrip(TransitDriverTrip trip) async {
if (isActionInProgress) return false;
isActionInProgress = true;
update();
final res = await TransitDriverService.endTrip(tripId: trip.id);
if (res.success) {
_lastAnnouncedStopSeq = 0;
if (Get.isRegistered<LocationController>()) {
Get.find<LocationController>().setBusMode(enabled: false);
}
await fetchTodayTrips();
} else {
mySnackbarError(res.message);
}
isActionInProgress = false;
update();
return res.success;
}
Future<bool> reportDelay(TransitDriverTrip trip, int minutes,
{String? reason}) async {
final res = await TransitDriverService.reportDelay(
tripId: trip.id,
delayMinutes: minutes,
reason: reason,
);
if (!res.success) mySnackbarError(res.message);
return res.success;
}
Future<bool> boardPassenger(int stopId, String passengerId) async {
final res = await TransitDriverService.boardPassenger(stopId, passengerId);
return res.success;
}
Future<List<Map<String, dynamic>>> getStopPassengers(int stopId) async {
final res = await TransitDriverService.getStopPassengers(stopId);
if (res.success && res.data != null) {
return res.data!;
}
return [];
}
// ── كشف الوصول للمحطات (جيوفينس) ────────────────────────────
// تُستدعى من LocationController عند كل تحديث للموقع أثناء وضع الباص
void onLocationUpdate(LatLng pos) {
if (activeTrip == null || activeTrip!.stops.isEmpty) return;
final stops = activeTrip!.stops;
int? nextSeq;
// الاتجاه للمحطة التالية بعد آخر معلنة
for (final stop in stops) {
if (stop.sequence <= _lastAnnouncedStopSeq) continue;
final dist = Geolocator.distanceBetween(
pos.latitude,
pos.longitude,
stop.latitude,
stop.longitude,
);
if (dist <= stop.geofenceRadius) {
nextSeq = stop.sequence;
break;
}
}
if (nextSeq != null && nextSeq > _lastAnnouncedStopSeq) {
_lastAnnouncedStopSeq = nextSeq;
// أرسل التسلسل الحالي عبر السوكيت
if (Get.isRegistered<LocationController>()) {
final lc = Get.find<LocationController>();
lc.emitBusLocationToSocket(pos, 0, 0, currentStopSeq: nextSeq);
}
update();
}
}
// عودة بالمحطة الحالية للعرض
TransitStop? get currentStop {
if (activeTrip == null || _lastAnnouncedStopSeq == 0) return null;
try {
return activeTrip!.stops
.firstWhere((s) => s.sequence == _lastAnnouncedStopSeq);
} catch (_) {
return null;
}
}
}