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>
This commit is contained in:
co-authored by
Claude Opus 5
parent
9909d9b4c1
commit
4d8414c96b
@@ -0,0 +1,271 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intaleq_maps/intaleq_maps.dart';
|
||||
|
||||
import '../../../constant/colors.dart';
|
||||
import '../../../constant/table_names.dart';
|
||||
import '../../../controller/functions/toast.dart';
|
||||
import '../../../controller/home/map/location_search_controller.dart';
|
||||
import '../../../controller/home/map/map_engine_controller.dart';
|
||||
import '../../../controller/home/map/ride_lifecycle_controller.dart';
|
||||
import '../../../main.dart';
|
||||
import '../../../print.dart';
|
||||
import '../../widgets/mydialoug.dart';
|
||||
import 'rp_theme.dart';
|
||||
|
||||
/// All controller side-effects for the route planner live here, so the
|
||||
/// widgets stay declarative. This is a faithful port of the behavior that
|
||||
/// previously lived inside `form_search_*.dart` and `main_bottom_Menu_map`.
|
||||
///
|
||||
/// No controller is modified — this only *calls* their existing API.
|
||||
class RpActions {
|
||||
RpActions._();
|
||||
|
||||
static LocationSearchController get _s =>
|
||||
Get.find<LocationSearchController>();
|
||||
static MapEngineController get _map => Get.find<MapEngineController>();
|
||||
static RideLifecycleController get _ride =>
|
||||
Get.find<RideLifecycleController>();
|
||||
|
||||
// ── Destination ─────────────────────────────────────────────────────────
|
||||
static void pickDestinationOnMap() {
|
||||
_map.changeMainBottomMenuMap();
|
||||
_map.changePickerShown();
|
||||
}
|
||||
|
||||
static Future<void> selectDestination(int index, dynamic res) async {
|
||||
final lat = res['latitude'];
|
||||
final lng = res['longitude'];
|
||||
if (lat == null || lng == null) {
|
||||
Toast.show(Get.context!, 'Invalid location data', AppColor.redColor);
|
||||
return;
|
||||
}
|
||||
final String title =
|
||||
(res['name_ar'] ?? res['name'] ?? 'Unknown Place').toString();
|
||||
|
||||
await sql.insertMapLocation({
|
||||
'latitude': lat,
|
||||
'longitude': lng,
|
||||
'name': title,
|
||||
'rate': 'N/A',
|
||||
'createdAt': DateTime.now().toIso8601String(),
|
||||
}, TableName.recentLocations);
|
||||
|
||||
final dest =
|
||||
LatLng(double.parse(lat.toString()), double.parse(lng.toString()));
|
||||
|
||||
if (_ride.isAnotherOreder) {
|
||||
_s.myDestination = dest;
|
||||
_s.clearPlacesDestination();
|
||||
await _ride.getDirectionMap(
|
||||
'${_s.passengerLocation.latitude},${_s.passengerLocation.longitude}',
|
||||
'${_s.myDestination.latitude},${_s.myDestination.longitude}');
|
||||
_map.isPickerShown = false;
|
||||
_s.passengerStartLocationFromMap = false;
|
||||
_map.changeMainBottomMenuMap();
|
||||
_ride.showBottomSheet1();
|
||||
} else {
|
||||
_s.passengerLocation = _s.newMyLocation;
|
||||
_s.myDestination = dest;
|
||||
_s.convertHintTextDestinationNewPlaces(index);
|
||||
_s.clearPlacesDestination();
|
||||
_map.changeMainBottomMenuMap();
|
||||
_s.passengerStartLocationFromMap = true;
|
||||
_map.isPickerShown = true;
|
||||
_map.mapController?.animateCamera(
|
||||
CameraUpdate.newLatLngZoom(_s.passengerLocation, 16),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Origin / pickup ───────────────────────────────────────────────────────
|
||||
/// Map-pick for the *other person's* pickup (order-for-someone-else mode).
|
||||
static void pickOtherPickupOnMap() {
|
||||
_s.passengerStartLocationFromMap = true;
|
||||
_map.changeMainBottomMenuMap();
|
||||
_map.changePickerShown();
|
||||
}
|
||||
|
||||
/// Map-pick for my own start point (updates the pickup without re-routing).
|
||||
static void pickMyStartOnMap() {
|
||||
_s.startLocationFromMap = true;
|
||||
_map.changeMainBottomMenuMap();
|
||||
_map.changePickerShown();
|
||||
}
|
||||
|
||||
/// Selecting a searched start point (other person's pickup).
|
||||
static void selectStart(dynamic res) {
|
||||
final lat = res['latitude'];
|
||||
final lng = res['longitude'];
|
||||
if (lat == null || lng == null) return;
|
||||
final String title =
|
||||
(res['name_ar'] ?? res['name'] ?? 'Unknown Place').toString();
|
||||
_s.passengerLocation =
|
||||
LatLng(double.parse(lat.toString()), double.parse(lng.toString()));
|
||||
_s.placeStartController.text = title;
|
||||
_s.clearPlacesStart();
|
||||
_s.update();
|
||||
}
|
||||
|
||||
// ── Gate Selection (Bottom Sheet) ──────────────────────────────────────────
|
||||
static void handleLocationSelection(
|
||||
BuildContext context, int index, dynamic place, bool isOrigin) {
|
||||
final hasGates = place['gates'] != null &&
|
||||
place['gates'] is List &&
|
||||
(place['gates'] as List).isNotEmpty;
|
||||
|
||||
if (!hasGates) {
|
||||
if (isOrigin) {
|
||||
selectStart(place);
|
||||
} else {
|
||||
selectDestination(index, place);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
final List gates = place['gates'];
|
||||
final bool isArabic = Get.locale?.languageCode == 'ar';
|
||||
|
||||
showModalBottomSheet(
|
||||
context: context,
|
||||
backgroundColor: Colors.transparent,
|
||||
isScrollControlled: true,
|
||||
builder: (context) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.only(top: 12, left: 16, right: 16, bottom: 24),
|
||||
decoration: BoxDecoration(
|
||||
color: RP.sheetElevated,
|
||||
borderRadius: const BorderRadius.vertical(top: Radius.circular(RP.rSheet)),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
RP.grabber(),
|
||||
const SizedBox(height: 16),
|
||||
Text(
|
||||
'Select Gate'.tr,
|
||||
style: RP.title,
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
...gates.map((gate) {
|
||||
final String gateName = isArabic
|
||||
? (gate['name_ar'] ?? gate['name_en'] ?? 'Gate'.tr)
|
||||
: (gate['name_en'] ?? gate['name_ar'] ?? 'Gate'.tr);
|
||||
|
||||
return ListTile(
|
||||
contentPadding: EdgeInsets.zero,
|
||||
leading: Container(
|
||||
padding: const EdgeInsets.all(8),
|
||||
decoration: BoxDecoration(
|
||||
color: AppColor.primaryColor.withOpacity(0.1),
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Icon(Icons.door_sliding_outlined,
|
||||
color: AppColor.primaryColor, size: 20),
|
||||
),
|
||||
title: Text(gateName, style: RP.fieldValue),
|
||||
subtitle: gate['is_main_gate'] == true
|
||||
? Text('Main Gate'.tr, style: RP.body)
|
||||
: null,
|
||||
onTap: () {
|
||||
Navigator.pop(context);
|
||||
final modifiedPlace = Map<String, dynamic>.from(place);
|
||||
modifiedPlace['latitude'] = gate['latitude'];
|
||||
modifiedPlace['longitude'] = gate['longitude'];
|
||||
modifiedPlace['name_ar'] = '${place['name_ar'] ?? place['name'] ?? ''} - $gateName';
|
||||
modifiedPlace['name'] = '${place['name'] ?? place['name_ar'] ?? ''} - $gateName';
|
||||
|
||||
if (isOrigin) {
|
||||
selectStart(modifiedPlace);
|
||||
} else {
|
||||
selectDestination(index, modifiedPlace);
|
||||
}
|
||||
},
|
||||
);
|
||||
}),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
// ── Stops (menu waypoints) ────────────────────────────────────────────────
|
||||
static void addStop() => _s.addMenuWaypoint();
|
||||
static void removeStop(int index) => _s.removeMenuWaypoint(index);
|
||||
static void pickStopOnMap(int index) => _s.startPickingWaypointOnMap(index);
|
||||
|
||||
// ── Home / Work quick actions ─────────────────────────────────────────────
|
||||
static void pickHomeOrWork({required bool isWork}) {
|
||||
if (isWork) {
|
||||
_s.workLocationFromMap = true;
|
||||
} else {
|
||||
_s.homeLocationFromMap = true;
|
||||
}
|
||||
_map.changeMainBottomMenuMap();
|
||||
_map.changePickerShown();
|
||||
}
|
||||
|
||||
static void changeHomeOrWork({required bool isWork}) {
|
||||
MyDialog().getDialog(
|
||||
isWork ? 'Change Work location ?'.tr : 'Change Home location ?'.tr,
|
||||
'',
|
||||
() => pickHomeOrWork(isWork: isWork),
|
||||
);
|
||||
}
|
||||
|
||||
/// Navigate to a saved Home/Work location and open the ride sheet.
|
||||
static Future<void> goToSaved(String boxName, String hint) async {
|
||||
try {
|
||||
final locationString = box.read(boxName).toString();
|
||||
final parts = locationString.split(',');
|
||||
final latLng =
|
||||
LatLng(double.parse(parts[0].trim()), double.parse(parts[1].trim()));
|
||||
|
||||
_s.hintTextDestinationPoint = hint.tr;
|
||||
_map.changeMainBottomMenuMap();
|
||||
|
||||
await _ride.getDirectionMap(
|
||||
'${_s.passengerLocation.latitude},${_s.passengerLocation.longitude}',
|
||||
'${latLng.latitude},${latLng.longitude}',
|
||||
);
|
||||
|
||||
_s.currentLocationToFormPlaces = false;
|
||||
_s.clearPlacesDestination();
|
||||
_s.passengerStartLocationFromMap = false;
|
||||
_map.isPickerShown = false;
|
||||
_ride.showBottomSheet1();
|
||||
} catch (e) {
|
||||
Log.print('RpActions.goToSaved error: $e');
|
||||
Toast.show(Get.context!, 'Failed to get location'.tr, AppColor.redColor);
|
||||
}
|
||||
}
|
||||
|
||||
// ── Favorites / recents ───────────────────────────────────────────────────
|
||||
static Future<void> addFavorite(
|
||||
BuildContext context, dynamic lat, dynamic lng, String title) async {
|
||||
if (lat == null || lng == null) {
|
||||
Toast.show(context, 'Invalid location data', AppColor.redColor);
|
||||
return;
|
||||
}
|
||||
await sql.insertMapLocation({
|
||||
'latitude': lat,
|
||||
'longitude': lng,
|
||||
'name': title,
|
||||
'rate': 'N/A',
|
||||
}, TableName.placesFavorite);
|
||||
if (!context.mounted) return;
|
||||
Toast.show(
|
||||
context, '$title ${'Saved Successfully'.tr}', AppColor.primaryColor);
|
||||
}
|
||||
|
||||
/// Route to a stored place (recent or favorite) after confirmation.
|
||||
static Future<void> goToStoredPlace(dynamic place) async {
|
||||
await _s.getLocation();
|
||||
await _ride.getDirectionMap(
|
||||
'${_s.passengerLocation.latitude},${_s.passengerLocation.longitude}',
|
||||
'${place['latitude']},${place['longitude']}',
|
||||
);
|
||||
_ride.showBottomSheet1();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user