المجلدات وأسماء حزم dart واستيراداتها: siro_rider → intaleq_rider siro_driver → intaleq_driver siro_admin → intaleq_admin siro_service → intaleq_service شمل ذلك `name:` في كل pubspec وكل `package:siro_*` في الاستيرادات (115 ملفاً في rider وحده)، وإشارات أسماء المجلدات في docs/ وتعليق في backend/Admin/notifications/broadcast.php. لم تبقَ إشارة واحدة (تحقّقت). + ضُمّت حزم get و get_storage داخل intaleq_driver/packages/ وحُوّلت مساراتها الثلاثة من `../../Intaleq/packages/*` إلى `./packages/*`. كانت تُحل عرضاً إلى مجلد App/Intaleq القديم المجاور — تبعية خارج المستودع تنكسر بأول نقل. لم يبقَ في أي تطبيق تبعية مسار خارج الشجرة. ⚠️ لم تُمسّ بعد: هويات المتاجر (applicationId · PRODUCT_BUNDLE_IDENTIFIER · shorebird app_id) ولا الألوان ولا النصوص المرئية ولا النطاقات — كل منها كوميت منفصل، والهويات تحتاج قرار المالك. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
221 lines
6.5 KiB
Dart
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;
|
|
}
|
|
}
|
|
}
|