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,201 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:siro_rider/constant/box_name.dart';
|
||||
import 'package:siro_rider/constant/colors.dart';
|
||||
import 'package:siro_rider/constant/links.dart';
|
||||
import 'package:siro_rider/controller/functions/crud.dart';
|
||||
import 'package:siro_rider/main.dart';
|
||||
import 'package:siro_rider/views/widgets/error_snakbar.dart';
|
||||
import 'package:http_parser/http_parser.dart';
|
||||
import 'package:mime/mime.dart';
|
||||
|
||||
import '../../../env/env.dart';
|
||||
import '../../../print.dart';
|
||||
import '../../../views/widgets/mydialoug.dart';
|
||||
import '../../functions/encrypt_decrypt.dart';
|
||||
|
||||
class ComplaintController extends GetxController {
|
||||
bool isLoading = false;
|
||||
final formKey = GlobalKey<FormState>();
|
||||
final complaintController = TextEditingController();
|
||||
|
||||
List feedBack = [];
|
||||
Map<String, dynamic>? passengerReport;
|
||||
Map<String, dynamic>? driverReport;
|
||||
|
||||
var isUploading = false.obs;
|
||||
var uploadSuccess = false.obs;
|
||||
String audioLink = ''; // سيتم تخزين رابط الصوت هنا بعد الرفع
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
getLatestRidesForPassengers();
|
||||
}
|
||||
|
||||
// --- دالة مخصصة لعرض إشعارات Snackbar بشكل جميل ---
|
||||
void _showCustomSnackbar(String title, String message,
|
||||
{bool isError = false}) {
|
||||
if (isError) {
|
||||
mySnackbarError(message.tr);
|
||||
} else {
|
||||
mySnackbarSuccess(message.tr);
|
||||
}
|
||||
}
|
||||
|
||||
// --- هذه الدالة تبقى كما هي لجلب بيانات الرحلة ---
|
||||
getLatestRidesForPassengers() async {
|
||||
isLoading = true;
|
||||
update();
|
||||
var res = await CRUD().get(link: AppLink.getFeedBack, payload: {
|
||||
'passengerId': box.read(BoxName.passengerID).toString(),
|
||||
});
|
||||
if (res != 'failure') {
|
||||
var d = res['message'];
|
||||
feedBack = d;
|
||||
}
|
||||
isLoading = false;
|
||||
update();
|
||||
}
|
||||
|
||||
// --- تم تحديث الهيدر في هذه الدالة ---
|
||||
Future<void> uploadAudioFile(File audioFile) async {
|
||||
try {
|
||||
isUploading.value = true;
|
||||
update();
|
||||
|
||||
var uri = Uri.parse('${AppLink.server}/upload_audio.php');
|
||||
var request = http.MultipartRequest('POST', uri);
|
||||
String token = r(box.read(BoxName.jwt)).toString().split(Env.addd)[0];
|
||||
final String fingerPrint =
|
||||
box.read(BoxName.deviceFpEncrypted)?.toString() ?? '';
|
||||
|
||||
var mimeType = lookupMimeType(audioFile.path);
|
||||
// ** التعديل: تم استخدام نفس هيدر التوثيق **
|
||||
request.headers.addAll({
|
||||
'Authorization': 'Bearer $token',
|
||||
'X-Device-FP': fingerPrint,
|
||||
});
|
||||
request.files.add(
|
||||
await http.MultipartFile.fromPath(
|
||||
'audio',
|
||||
audioFile.path,
|
||||
contentType: mimeType != null ? MediaType.parse(mimeType) : null,
|
||||
),
|
||||
);
|
||||
|
||||
var response = await request.send();
|
||||
var responseBody = await http.Response.fromStream(response);
|
||||
|
||||
if (response.statusCode == 200) {
|
||||
var jsonResponse = jsonDecode(responseBody.body);
|
||||
if (jsonResponse['status'] == 'Audio file uploaded successfully.') {
|
||||
uploadSuccess.value = true;
|
||||
audioLink = jsonResponse['link']; // تخزين الرابط في المتغير
|
||||
Get.back();
|
||||
// استخدام الـ Snackbar الجديد
|
||||
_showCustomSnackbar('Success', 'Audio uploaded successfully.');
|
||||
} else {
|
||||
uploadSuccess.value = false;
|
||||
_showCustomSnackbar('Error', 'Failed to upload audio file.',
|
||||
isError: true);
|
||||
}
|
||||
} else {
|
||||
uploadSuccess.value = false;
|
||||
_showCustomSnackbar('Error', 'Server error: ${response.statusCode}',
|
||||
isError: true);
|
||||
}
|
||||
} catch (e) {
|
||||
uploadSuccess.value = false;
|
||||
_showCustomSnackbar(
|
||||
'Error', 'An application error occurred during upload.',
|
||||
isError: true);
|
||||
} finally {
|
||||
isUploading.value = false;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
// --- الدالة الجديدة التي تتصل بالسكريبت الجديد ---
|
||||
Future<void> submitComplaintToServer() async {
|
||||
// 1. التحقق من صحة الفورم
|
||||
if (!formKey.currentState!.validate() || complaintController.text.isEmpty) {
|
||||
// استخدام الـ Snackbar الجديد
|
||||
_showCustomSnackbar(
|
||||
'Error', 'Please describe your issue before submitting.',
|
||||
isError: true);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. التحقق من وجود بيانات الرحلة
|
||||
if (feedBack.isEmpty) {
|
||||
// استخدام الـ Snackbar الجديد
|
||||
_showCustomSnackbar(
|
||||
'Error', 'Ride information not found. Please refresh the page.',
|
||||
isError: true);
|
||||
return;
|
||||
}
|
||||
|
||||
isLoading = true;
|
||||
update();
|
||||
|
||||
try {
|
||||
// 3. استخراج البيانات المطلوبة
|
||||
final rideId = feedBack[0]['id'].toString(); // ! تأكد أن اسم حقل ID صحيح
|
||||
final complaint = complaintController.text;
|
||||
String token = r(box.read(BoxName.jwt)).toString().split(Env.addd)[0];
|
||||
|
||||
// 4. استدعاء سكربت PHP الجديد باستخدام http.post
|
||||
final response = await http.post(
|
||||
Uri.parse(AppLink.add_solve_all),
|
||||
headers: {'Authorization': 'Bearer $token'},
|
||||
body: {
|
||||
'ride_id': rideId,
|
||||
'complaint_text': complaint,
|
||||
'audio_link': audioLink,
|
||||
},
|
||||
);
|
||||
|
||||
Log.print('Server Response: ${response.body}');
|
||||
|
||||
if (response.statusCode != 200) {
|
||||
_showCustomSnackbar(
|
||||
'Error', 'Failed to connect to the server. Please try again.',
|
||||
isError: true);
|
||||
return;
|
||||
}
|
||||
|
||||
// 5. التعامل مع محتوى الرد من الخادم
|
||||
final responseData = jsonDecode(response.body);
|
||||
|
||||
if (responseData['status'] == 'success') {
|
||||
passengerReport = responseData['data']['passenger_response'];
|
||||
driverReport = responseData['data']['driver_response'];
|
||||
update();
|
||||
|
||||
MyDialogContent().getDialog(
|
||||
'Success'.tr, Text('Your complaint has been submitted.'.tr), () {
|
||||
Get.back();
|
||||
complaintController.clear();
|
||||
audioLink = '';
|
||||
formKey.currentState?.reset();
|
||||
});
|
||||
} else {
|
||||
String errorMessage =
|
||||
responseData['message'] ?? 'An unknown server error occurred'.tr;
|
||||
_showCustomSnackbar('Submission Failed', errorMessage, isError: true);
|
||||
}
|
||||
} catch (e) {
|
||||
Log.print("Submit Complaint Error: $e");
|
||||
_showCustomSnackbar('Error', 'An application error occurred.'.tr,
|
||||
isError: true);
|
||||
} finally {
|
||||
isLoading = false;
|
||||
update();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,330 @@
|
||||
import 'package:siro_rider/constant/box_name.dart';
|
||||
import 'package:siro_rider/constant/colors.dart';
|
||||
import 'package:siro_rider/constant/links.dart';
|
||||
import 'package:siro_rider/controller/functions/country_logic.dart';
|
||||
import 'package:siro_rider/controller/functions/crud.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_contacts/flutter_contacts.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:permission_handler/permission_handler.dart';
|
||||
import 'package:share_plus/share_plus.dart';
|
||||
|
||||
import '../../../main.dart';
|
||||
import '../../../print.dart';
|
||||
import '../../../views/widgets/error_snakbar.dart';
|
||||
import '../../../views/widgets/mydialoug.dart';
|
||||
import '../../functions/launch.dart';
|
||||
import '../../notification/notification_captain_controller.dart';
|
||||
import '../../payment/payment_controller.dart';
|
||||
|
||||
class InviteController extends GetxController {
|
||||
final TextEditingController invitePhoneController = TextEditingController();
|
||||
List driverInvitationData = [];
|
||||
List driverInvitationDataToPassengers = [];
|
||||
String? couponCode;
|
||||
String? driverCouponCode;
|
||||
|
||||
int selectedTab = 0;
|
||||
PassengerStats passengerStats = PassengerStats();
|
||||
|
||||
List<Contact> contacts = <Contact>[];
|
||||
RxList<Map<String, dynamic>> contactMaps = <Map<String, dynamic>>[].obs;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
// It's good practice to fetch initial data in onInit or onReady
|
||||
// fetchDriverStats();
|
||||
// fetchDriverStatsPassengers();
|
||||
}
|
||||
|
||||
void updateSelectedTab(int index) {
|
||||
selectedTab = index;
|
||||
update();
|
||||
}
|
||||
|
||||
// --- Sharing Methods ---
|
||||
|
||||
Future<void> shareDriverCode() async {
|
||||
if (driverCouponCode != null) {
|
||||
final String shareText = '''
|
||||
${'Join Siro as a driver using my referral code!'.tr}
|
||||
${'Use code:'.tr} $driverCouponCode
|
||||
${'Download the Siro Driver app now and earn rewards:'.tr}
|
||||
${AppLink.inviteRedirectUrl}?code=$driverCouponCode&app=driver
|
||||
''';
|
||||
await Share.share(shareText);
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> sharePassengerCode() async {
|
||||
if (couponCode != null) {
|
||||
final String shareText = '''
|
||||
${'Get a discount on your first Siro ride!'.tr}
|
||||
${'Use my referral code:'.tr} $couponCode
|
||||
${'Download the Siro app now and enjoy your ride:'.tr}
|
||||
${AppLink.inviteRedirectUrl}?code=$couponCode&app=rider
|
||||
''';
|
||||
await Share.share(shareText);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Data Fetching ---
|
||||
|
||||
void fetchDriverStats() async {
|
||||
try {
|
||||
var response = await CRUD().get(link: AppLink.getInviteDriver, payload: {
|
||||
"driverId": box.read(BoxName.driverID),
|
||||
});
|
||||
if (response != 'failure' && response is Map) {
|
||||
driverInvitationData = response['message'];
|
||||
update();
|
||||
}
|
||||
} catch (e) {
|
||||
Log.print("Error fetching driver stats: $e");
|
||||
}
|
||||
}
|
||||
|
||||
void fetchDriverStatsPassengers() async {
|
||||
try {
|
||||
var response = await CRUD()
|
||||
.get(link: AppLink.getDriverInvitationToPassengers, payload: {
|
||||
"driverId": box.read(BoxName.passengerID),
|
||||
});
|
||||
if (response != 'failure' && response is Map) {
|
||||
driverInvitationDataToPassengers = response['message'];
|
||||
update();
|
||||
}
|
||||
} catch (e) {
|
||||
Log.print("Error fetching passenger stats: $e");
|
||||
}
|
||||
}
|
||||
|
||||
// --- Contact Handling ---
|
||||
|
||||
/// **IMPROVEMENT**: This function now filters out contacts without any phone numbers.
|
||||
/// This is the fix for the `RangeError` you were seeing, which happened when the UI
|
||||
/// tried to access the first phone number of a contact that had none.
|
||||
Future<void> pickContacts() async {
|
||||
try {
|
||||
// 1. Check current permission status using permission_handler for better control
|
||||
PermissionStatus status = await Permission.contacts.status;
|
||||
|
||||
// 2. If status is permanently denied, direct user to settings
|
||||
if (status.isPermanentlyDenied) {
|
||||
Get.defaultDialog(
|
||||
title: 'Permission Required'.tr,
|
||||
middleText:
|
||||
'Contact permission is permanently denied. Please enable it in settings to continue.'
|
||||
.tr,
|
||||
textConfirm: 'Settings'.tr,
|
||||
textCancel: 'Cancel'.tr,
|
||||
confirmTextColor: Colors.white,
|
||||
onConfirm: () {
|
||||
openAppSettings();
|
||||
Get.back();
|
||||
},
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
// 3. Request permission if not already granted
|
||||
if (!status.isGranted) {
|
||||
status = await Permission.contacts.request();
|
||||
}
|
||||
|
||||
// 4. Proceed if granted
|
||||
if (status.isGranted) {
|
||||
// Also call flutter_contacts requestPermission to ensure it's synced (internal state)
|
||||
await FlutterContacts.requestPermission(readonly: true);
|
||||
|
||||
final List<Contact> allContacts =
|
||||
await FlutterContacts.getContacts(withProperties: true);
|
||||
final int totalContactsOnDevice = allContacts.length;
|
||||
|
||||
// **FIX**: Filter contacts to only include those with at least one phone number.
|
||||
contacts = allContacts.where((c) => c.phones.isNotEmpty).toList();
|
||||
final int contactsWithPhones = contacts.length;
|
||||
|
||||
if (contactsWithPhones > 0) {
|
||||
Log.print('Found $contactsWithPhones contacts with phone numbers.');
|
||||
contactMaps.value = contacts.map((contact) {
|
||||
return {
|
||||
'name': contact.displayName,
|
||||
'phones': contact.phones.map((p) => p.number).toList(),
|
||||
'emails': contact.emails.map((e) => e.address).toList(),
|
||||
};
|
||||
}).toList();
|
||||
update();
|
||||
|
||||
// **IMPROVEMENT**: Provide feedback if some contacts were filtered out.
|
||||
if (contactsWithPhones < totalContactsOnDevice) {
|
||||
// Get.snackbar('Contacts Loaded'.tr,
|
||||
// '${'Showing'.tr} $contactsWithPhones ${'of'.tr} $totalContactsOnDevice ${'contacts. Others were hidden because they don\'t have a phone number.'.tr}',
|
||||
// snackPosition: SnackPosition.BOTTOM);
|
||||
}
|
||||
} else {
|
||||
mySnackbarWarning('No contacts with phone numbers were found on your device.'.tr);
|
||||
}
|
||||
} else {
|
||||
mySnackbarWarning('Contact permission is required to pick contacts'.tr);
|
||||
}
|
||||
} catch (e) {
|
||||
Log.print('Error picking contacts: $e');
|
||||
mySnackbarError('An error occurred while picking contacts: $e'.tr);
|
||||
}
|
||||
}
|
||||
|
||||
void selectPhone(String phone) {
|
||||
invitePhoneController.text = phone;
|
||||
update();
|
||||
Get.back();
|
||||
}
|
||||
|
||||
/// Formats phone number based on the current country (Syria=963, Jordan=962, Egypt=20).
|
||||
String _formatPhoneByCountry(String phone) {
|
||||
return CountryLogic.formatCurrentCountryPhone(phone);
|
||||
}
|
||||
|
||||
void sendInviteToPassenger() async {
|
||||
if (invitePhoneController.text.isEmpty ||
|
||||
invitePhoneController.text.length < 9) {
|
||||
mySnackbarError('Please enter a correct phone'.tr);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
String formattedPhoneNumber =
|
||||
_formatPhoneByCountry(invitePhoneController.text);
|
||||
|
||||
var response =
|
||||
await CRUD().post(link: AppLink.addInvitationPassenger, payload: {
|
||||
"driverId": box.read(BoxName.passengerID),
|
||||
"inviterPassengerPhone": formattedPhoneNumber,
|
||||
});
|
||||
|
||||
if (response != 'failure' && response is Map) {
|
||||
var d = response;
|
||||
mySnackbarSuccess('Invite sent successfully'.tr);
|
||||
|
||||
var payload = d['data'] ?? d['message'];
|
||||
if (payload is String) {
|
||||
payload = d;
|
||||
}
|
||||
|
||||
String expirationTime = (payload['expirationTime'] ?? '').toString();
|
||||
String inviteCode = (payload['inviteCode'] ?? '').toString();
|
||||
|
||||
String message =
|
||||
"👋 ${'Hello! I\'m inviting you to try Siro.'.tr}\n\n"
|
||||
"🎁 ${'Use my invitation code to get a special gift on your first ride!'.tr}\n\n"
|
||||
"${'Your personal invitation code is:'.tr}\n"
|
||||
"*$inviteCode*\n\n"
|
||||
"⏳ ${'Be sure to use it quickly! This code expires at'.tr} *$expirationTime*.\n\n"
|
||||
"🔗 ${'Quick Invite Link:'.tr}\n"
|
||||
"${AppLink.inviteRedirectUrl}?code=$inviteCode&app=rider\n\n"
|
||||
"💡 ${'Note: If the link above is not clickable, please save this contact or reply to this message to activate all links.'.tr}\n\n"
|
||||
"${'See you on the road!'.tr} 🚗";
|
||||
|
||||
launchCommunication('whatsapp', formattedPhoneNumber, message);
|
||||
invitePhoneController.clear();
|
||||
update();
|
||||
} else {
|
||||
mySnackbarWarning("This phone number has already been invited.".tr);
|
||||
}
|
||||
} catch (e) {
|
||||
Log.print("Error sending invite: $e");
|
||||
mySnackbarError('An unexpected error occurred. Please try again.'.tr);
|
||||
}
|
||||
}
|
||||
|
||||
// This function is dependent on the `pickContacts` method filtering out contacts without phones.
|
||||
savePhoneToServer() async {
|
||||
for (var contactMap in contactMaps) {
|
||||
// The `pickContacts` function ensures the 'phones' list is not empty here.
|
||||
var phones = contactMap['phones'] as List<String>;
|
||||
var res = await CRUD().post(link: AppLink.savePhones, payload: {
|
||||
"name": contactMap['name'] ?? 'No Name',
|
||||
"phones": phones.first, // Safely access the first phone number
|
||||
"phones2": phones.join(', '),
|
||||
});
|
||||
if (res == 'failure') {
|
||||
Log.print('Failed to save contact: ${contactMap['name']}');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void onSelectPassengerInvitation(int index) async {
|
||||
try {
|
||||
final invitation = driverInvitationDataToPassengers[index];
|
||||
final tripCount =
|
||||
int.tryParse(invitation['countOfInvitDriver'].toString()) ?? 0;
|
||||
final passengerName = invitation['passengerName'].toString();
|
||||
final isGiftTaken = invitation['isGiftToken'].toString() == '1';
|
||||
|
||||
if (tripCount >= 2) {
|
||||
// Gift can be claimed
|
||||
if (!isGiftTaken) {
|
||||
MyDialog().getDialog(
|
||||
'You deserve the gift'.tr,
|
||||
'${'Claim your 20 LE gift for inviting'.tr} $passengerName!',
|
||||
() async {
|
||||
Get.back(); // Close dialog first
|
||||
|
||||
var response = await CRUD().post(
|
||||
link: AppLink.claimInviteReward,
|
||||
payload: {
|
||||
'invite_id': invitation['id'].toString(),
|
||||
'passenger_id': box.read(BoxName.passengerID).toString(),
|
||||
'country_code': box.read(BoxName.countryCode).toString(),
|
||||
},
|
||||
);
|
||||
|
||||
if (response != 'failure' && response is Map) {
|
||||
if (response['status'] == 'success') {
|
||||
NotificationCaptainController().addNotificationCaptain(
|
||||
invitation['passengerInviterId'].toString(),
|
||||
"You have got a gift for invitation".tr,
|
||||
'${"You have earned 20".tr} ${'LE'}',
|
||||
false,
|
||||
);
|
||||
fetchDriverStatsPassengers(); // Refresh list
|
||||
} else {
|
||||
mySnackbarError(response['message'] ?? 'Claim failed'.tr);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
} else {
|
||||
MyDialog().getDialog(
|
||||
"Gift Already Claimed".tr,
|
||||
"You have already received your gift for inviting $passengerName."
|
||||
.tr,
|
||||
() => Get.back(),
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Gift not yet earned
|
||||
MyDialog().getDialog(
|
||||
'${'Keep it up!'.tr}',
|
||||
'$passengerName ${'has completed'.tr} $tripCount / 2 ${'trips'.tr}. ${"You can claim your gift once they complete 2 trips.".tr}',
|
||||
() => Get.back(),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
Log.print("Error in onSelectPassengerInvitation: $e");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PassengerStats {
|
||||
final int totalInvites;
|
||||
final int activeUsers;
|
||||
final double totalEarnings;
|
||||
|
||||
PassengerStats({
|
||||
this.totalInvites = 0,
|
||||
this.activeUsers = 0,
|
||||
this.totalEarnings = 0.0,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:siro_rider/constant/box_name.dart';
|
||||
import 'package:siro_rider/constant/links.dart';
|
||||
import 'package:siro_rider/controller/functions/crud.dart';
|
||||
import 'package:siro_rider/main.dart';
|
||||
import 'package:siro_rider/views/widgets/error_snakbar.dart';
|
||||
|
||||
class InvitesRewardsController extends GetxController {
|
||||
bool isLoading = false;
|
||||
String? referralCode;
|
||||
int totalInvitedDrivers = 0;
|
||||
int totalInvitedPassengers = 0;
|
||||
List<dynamic> referrals = [];
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
super.onInit();
|
||||
fetchPassengerReferrals();
|
||||
}
|
||||
|
||||
Future<void> fetchPassengerReferrals() async {
|
||||
isLoading = true;
|
||||
update();
|
||||
|
||||
try {
|
||||
// Ensure a referral code exists first (creates one if not)
|
||||
var codeResponse = await CRUD().post(
|
||||
link: AppLink.getUnifiedCode,
|
||||
payload: {},
|
||||
);
|
||||
|
||||
if (codeResponse != 'failure' && codeResponse['status'] == 'success') {
|
||||
referralCode = codeResponse['message']['referral_code'];
|
||||
}
|
||||
|
||||
var response = await CRUD().post(
|
||||
link: AppLink.getPassengerReferrals,
|
||||
payload: {
|
||||
'country_code': box.read(BoxName.countryCode) ?? 'Jordan',
|
||||
},
|
||||
);
|
||||
|
||||
if (response != 'failure') {
|
||||
if (response['status'] == 'success') {
|
||||
referralCode ??= response['message']['referral_code'];
|
||||
totalInvitedDrivers = response['message']['total_invited_drivers'] ?? 0;
|
||||
totalInvitedPassengers = response['message']['total_invited_passengers'] ?? 0;
|
||||
referrals = response['message']['referrals'] ?? [];
|
||||
} else {
|
||||
referrals = [];
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
print("Error fetching passenger referrals: $e");
|
||||
}
|
||||
|
||||
isLoading = false;
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
Future<void> linkInviteCode(String inviteCode) async {
|
||||
Get.dialog(const Center(child: CircularProgressIndicator()), barrierDismissible: false);
|
||||
|
||||
try {
|
||||
var response = await CRUD().post(
|
||||
link: AppLink.addUnifiedInvite,
|
||||
payload: {
|
||||
"inviter_code": inviteCode,
|
||||
}
|
||||
);
|
||||
|
||||
Navigator.maybeOf(Get.context!)?.pop(); // close loading dialog only
|
||||
|
||||
if (response != 'failure') {
|
||||
if (response['status'] == 'success') {
|
||||
mySnackbarSuccess("You have been successfully referred!".tr);
|
||||
} else {
|
||||
mySnackbarWarning(response['message'] ?? "Could not add invite".tr);
|
||||
}
|
||||
} else {
|
||||
mySnackbarError("Network error occurred".tr);
|
||||
}
|
||||
} catch (e) {
|
||||
Navigator.maybeOf(Get.context!)?.pop(); // close loading dialog only
|
||||
mySnackbarError("Network error occurred".tr);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:get/get.dart';
|
||||
import 'package:siro_rider/constant/box_name.dart';
|
||||
import 'package:siro_rider/constant/links.dart';
|
||||
import 'package:siro_rider/controller/functions/crud.dart';
|
||||
import 'package:siro_rider/main.dart';
|
||||
|
||||
class OrderHistoryController extends GetxController {
|
||||
List<dynamic> orderHistoryListPassenger = [];
|
||||
bool isloading = true;
|
||||
|
||||
@override
|
||||
void onInit() {
|
||||
getOrderHistoryByPassenger();
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
Future getOrderHistoryByPassenger() async {
|
||||
var res = await CRUD().get(link: AppLink.getRides, payload: {
|
||||
'passenger_id': box.read(BoxName.passengerID).toString(),
|
||||
});
|
||||
if (res.toString() == 'failure') {
|
||||
// Get.snackbar('failure', 'message');
|
||||
isloading = false;
|
||||
update();
|
||||
} else {
|
||||
var jsonDecoded = res is String ? jsonDecode(res) : res;
|
||||
var rawData = jsonDecoded['data'] ?? jsonDecoded['message'];
|
||||
orderHistoryListPassenger = rawData is List ? rawData : [];
|
||||
isloading = false;
|
||||
update();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import 'package:siro_rider/constant/box_name.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:siro_rider/constant/links.dart';
|
||||
import 'package:siro_rider/controller/functions/crud.dart';
|
||||
|
||||
import '../../../main.dart';
|
||||
|
||||
class PromosController extends GetxController {
|
||||
List<dynamic> promoList = [];
|
||||
bool isLoading = true;
|
||||
late String promos;
|
||||
@override
|
||||
void onInit() {
|
||||
getPromoByToday();
|
||||
super.onInit();
|
||||
}
|
||||
|
||||
Future getPromoByToday() async {
|
||||
var res = await CRUD().get(link: AppLink.getPromoBytody, payload: {
|
||||
'passengerID': box.read(BoxName.passengerID).toString(),
|
||||
});
|
||||
if (res.toString() == 'failure') {
|
||||
// Get.defaultDialog(
|
||||
// title: 'No Promo for today .'.tr,
|
||||
// middleText: '',
|
||||
// titleStyle: AppStyle.title,
|
||||
// confirm: MyElevatedButton(
|
||||
// title: 'Back'.tr,
|
||||
// onPressed: () {
|
||||
// Get.back();
|
||||
// Get.back();
|
||||
// }));
|
||||
isLoading = false;
|
||||
update();
|
||||
} else {
|
||||
promoList = res['message'];
|
||||
isLoading = false;
|
||||
update();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user